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) { } } 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++; 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 運行問題
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