作業系統 multiprocess & multithread 作業

contents

  1. 1. A 班程式要求
  2. 2. 注意事項
  3. 3. 開始動手
    1. 3.1. multiprocess
    2. 3.2. multithread
    3. 3.3. 後記
  4. 4. 執行
    1. 4.1. 編譯
    2. 4.2. 運行
  5. 5. Mac 運行問題
  6. 6. 其他作業

A 班程式要求

請使用所熟悉的程式開發環境設計單一行程產生子行程 ( Child Process ) 與 多執行緒 ( Multi-Thread) 的程式,意即完成下列傳統 for loop 之列印工作,將其改寫成多行程與多執行緒,由不同的行程與執行緒列印不同 i 值。

1
2
for (int i = 0; i < 10; i++)
printf("output i = %d\n", i);

注意事項

  • 嚴禁從網路抄襲作業 。每份程式作業將被程式比對軟體檢查,一旦發現抄襲,該份作業以零分計,並且這學期作業系統作業成績以零分計。
  • 後略

這種作業不抄來抄去也難。但是作業格式不清楚,所以也有不同理解方式就是了。

開始動手

對於將 for 迴圈改寫,我們必須認知到幾項

  • 是否要按照順序輸出?
  • 平台 M$, Linux, or Max OS?

此篇假設目標是一模一樣的輸出

multiprocess

child process 屬於 multiprocess 中的一種方式。為什麼這麼說呢?multiprocess 只是要求一個 CPU 中有多個工作同時在進行,藉由排程器去進行工作分配。

先不管這些,要實作 child process 看起來只有走向 fork() 一途。而之前 inker 大神則是走非 child process 使用類似 ./nachos -e ../test/test1 -e ../test/test1 的方式去運行。

為了實作按照順序輸出,我們需要 process 共同擁有的 shared memory,這個 shared memory 將要紀錄原本 for 裡面的 i 值和一個 sem_t,來保證 process 和 process 之間不會同時搶先動作,sem_t 也許是不需要的,這裡留給實驗者去測試。

為了使輸出一致,在 fork() 完之後,使用 wait() 來等待所有子程序結束。這樣會造成當 for loop 需要跑 n 次,就會總共 n 個 process,也就是一整條鏈下去。

  • 思考 wait()sem_t 都不是這麼必要?// 這個要看寫法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <semaphore.h>
#ifndef MAP_ANONYMOUS
#ifdef MAP_ANON
#define MAP_ANONYMOUS MAP_ANON
#endif
#endif
static int *glob_var;
static int n = 32;
int main(void)
{
glob_var = (int *)mmap(NULL, sizeof *glob_var, PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_ANONYMOUS, -1, 0);
sem_t *sema = (sem_t *)mmap(NULL, sizeof(sema),
PROT_READ |PROT_WRITE,MAP_SHARED|MAP_ANONYMOUS,
-1, 0);
sem_init(sema, 1, 0);
*glob_var = 0;
while (*glob_var < n) {
sem_wait(sema);
int pid = fork();
int output = (*glob_var)++;
if (output < n)
printf("%d, pid = %d\n", output, pid);
sem_post(sema);
int wpid, status = 0;
while ((wpid = wait(&status)) > 0)
{
//printf("Exit status of %d was %d (%s)\n", (int)wpid, status, (status > 0) ? "accept" : "reject");
}
}
munmap(glob_var, sizeof *glob_var);
return 0;
}

multithread

Thread 就相當簡單,因為它們是共用同一個記憶體區段,因此沒有 share memory 的問題。
雖然使用 sem_wait(&sem)sem_post(&sem) 來保證區域內的代碼不會同時有多個 thread 在運行,但仍遇到 i = 0 同時在多個 Thread 噴出的情況,這一點百思不得其解,為了解決這一切問題,使用課本提到的 Producer–consumer problem 寫法。

希望能從代碼中看懂。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <semaphore.h>
#include <pthread.h>
int i = 0, n = 16;
int count = 0;
sem_t sem;
const int MAX_THREAD = 4;
void *Producer(void *arg) {
for(int i = 0; i < n;) {
sem_wait(&sem);
if(count == 1) {
sem_post(&sem);
continue;
}
count++;
// printf("\nProducer is :%d\n", count);
sem_post(&sem);
i++;
}
pthread_exit(NULL);
}
void *runPrint(void *arg) {
int id = *((int *) arg);
for (; i < n ;) {
sem_wait(&sem);
if (count == 0) {
sem_post(&sem);
continue;
}
count--;
i++;
printf("loop i = %d, thread id %d\n", i, id);
sem_post(&sem);
}
pthread_exit(NULL);
}
int main() {
pthread_t *threads;
sem_init(&sem, 0, 0);
threads = (pthread_t *) malloc(MAX_THREAD * sizeof(pthread_t));
for (int i = 0; i < MAX_THREAD; i++) {
int *p = (int *) malloc(sizeof(int));
*p = i;
pthread_create(&threads[i], NULL, runPrint, (void *)(p));
}
pthread_t producer;
int *p = (int *) malloc(sizeof(int));
*p = -1;
pthread_create(&producer, NULL, Producer, (void *)(p));
for (i = 0; i < MAX_THREAD; i++) {
pthread_join(threads[i], NULL);
}
sem_destroy(&sem);
return 0;
}

後記

代碼沒有寫得很完整,也就是能達到輸出相同且用符合需求,這一點是有很多漏洞的,前人也是鑽漏洞交作業的。相較之下,我們 B 班的作業略顯兇殘,但是網路上資料還真是少呢,雖然還是找地方抄和理解。

測試的時候,為了保證輸出是穩定的,記得得多測試幾次,並且把數字範圍作變調,當然能不能讓一個 create thread 跑完整個迴圈呢?畢竟加上 main thread 就算是 multithread?haha

執行

編譯

  • Mac OSX

    $ clang -pthread x.cpp -o x
    
  • Linux

    $ gcc -lpthread x.cpp -o x
    

    運行

  • Linux & Max OSX

    $ ./x
    

Mac 運行問題

1
2
3
4
5
#ifndef MAP_ANONYMOUS
#ifdef MAP_ANON
#define MAP_ANONYMOUS MAP_ANON
#endif
#endif

不知道為什麼 clang 中沒有定義 MAP_ANONYMOUS,增加上述代碼後正確運行。

其他作業

B 班程式作業 1
B 班程式作業 2