作業系統 — process management with code

Vincent Ko
13 min readMay 15, 2024

--

3.13

在這一題中,題目詢問我們透過fork()創建child process,並且請child process生成Fibonacci sequence,創建完成之後結束child process,將控制權還給Parent process.

由於我發現在公告中,第一題會使用./檔名 參數的方式執行,我認為這裡的參數是輸入的number,用來決定輸出的斐波拉契數列的長度。因此,我使用的是在int main中定義好 int argc, char *argv[]的形式,來讓此程式可以使用 ./檔名 參數的形式來運行。

第9到12行用來檢查輸入的參數是否符合 ./檔名 參數的形式,如果不符合的話我們就重新要求輸入者使用規定的方式來運行此程式。

第20到22行給予了必要的正數判斷,如果輸入的參數小於0,我們會提示此程式要求positive integer。如果輸入正確的話,就會請parent process透過fork()來創建child process,並開始同時運行。如果pid process = 0,代表正在運行的是child process,反之則為parents process。

parent process創建子process成功之後,就透過waitpid來等待子陣列完成他該做的事情,parent process才能夠繼續跑下去,顯示parents ends; 在child process中,我們進行正常的fibonacci sequence創建程序。值得注意的是,在這個程式中我定義輸入的數字代表輸出的fibonacci sequence的長度,因此如果輸入1,輸出是[0],輸入2,輸出是[0,1],以此類推。 由於我使用INT來儲存變數,因此實證發現最多只能容納輸入number=47以內的情況,在這裡我不另行處理,這不是大一程式設計了。

等到child process輸出完整串數列之後,存取權才會還給parents process,parents輸出”Parent ends後,結束程式。由於這個題目的parent 和child擁有各自互不連通的data,所以其實parent不會知道child存儲的內容,如果要解決此問題的話,必須用share-memory的形式(如3.17題)解決。

圖(一)為3.13程式輸出的結果。

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>

int main(int argc, char *argv[])
{
if (argc != 2) {
printf("Usage: %s <number>\n", argv[0]);
return 1;
}

int i, a = 0, b = 1;
int n = 1;
pid_t pid;

i = atoi(argv[1]);

if (i <= 0) {
printf("Please enter a positive integer!\n");
}
else {
pid = fork();
if (pid == 0) {
printf("Child is producing the Fibonacci Sequence...\n");
if (i == 1) {
printf("%d ", 0);
} else if (i == 2) {
printf("%d ", 0);
printf("%d ", 1);
} else {
printf("%d ", 0);
printf("%d ", 1);
i -= 2;
while (i > 0) {
n = a + b;
printf("%d ", n);
a = b;
b = n;
i--;
}
}
printf("\nChild ends\n");
} else {
printf("Parent is waiting for child to complete...\n");
waitpid(pid, NULL, 0);
printf("Parent ends\n");
}
}
return 0;
}

3.17

雖然這題的輸出看起來跟3.13一樣,但是實際上這題使用了shared-memory的方式來處理processes之間的溝通,讓parent跟child可以共享share_data的資料,其中share_data是在題目中定義好的struct,包含fib_seq裝有斐波拉契數列,以及sequence_size來表示此數列的長度,定義在第12到第15行。

接者我會省略掉此題目與3.13題相同的細節,例如17到22行定義輸入的形式等等,這題也是使用 ./檔名 參數 的形式來跑,輸入的數字代表輸出的長度(也就是sequence_size,最長是十,同時在第28到31行檢查輸入的數字是否在0到10之間。

32到50行的的程式碼是用來建立一個共享記憶體段的。首先,第35行使用shm_open函數創建一個名為”fibonacci”的共享記憶體段,並設定可讀可寫的權限。如果shm_open 函數返回-1,表示出現了錯誤,會印出錯誤信息並返回1。

接著第42行,使用ftruncate 函數來配置共享記憶體段的大小,這裡將大小設定為sizeof(share_data)。

最後,第45行使用mmap 函數將共享記憶體段映射到進程的地址空間中,並返回一個指向共享記憶體段的指標。如果mmap 函數返回MAP_FAILED,表示出現了錯誤,會印出錯誤信息並返回1。

這樣下來,此share memory就可以被parent跟child process來使用,就可以在第50行創立child process。如果pid=0,代表正在運行的是child process,我們可以將相對應的數列內容以及長度放到fib_seq以及sequence_size裡面,當程式結束後,我們透過munmap來detach這個share memory segment from my address space,以釋放資源。 第78行則可以看到我們使用waitpid等待child process跑完之後,再進行parent process負責的部分,也就是把data內的fib_seq數列一個個印到terminal上面。

最後89–90行用於分離和刪除先前建立的共享記憶體段。首先,使用munmap函數將共享記憶體段從目前進程的地址空間中分離。接著,使用shm_unlink函數來刪除共享記憶體段的名稱。這會將共享記憶體段從文件系統中刪除。

以下是3.17的輸出結果,如圖(二):

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#define MAX_SEQ 10

typedef struct {
long fib_seq[MAX_SEQ];
int sequence_size;
} share_data;

int main(int argc, char *argv[])
{
if (argc != 2) {
printf("Usage: %s <number>\n", argv[0]);
return 1;
}
int i, a = 0, b = 1;
int n = 1;
pid_t pid;
int shm_fd;
i = atoi(argv[1]);
if (i < 0 || i > MAX_SEQ) {
printf("Please enter a non-negative integer less than or equal to %d\n", MAX_SEQ);
return 1;
}
else
{
// create the shared memory segment
shm_fd = shm_open("fibonacci", O_CREAT | O_RDWR, 0666);
if (shm_fd == -1) {
perror("shm_open");
return 1;
}

// configure the size of the shared memory segment
ftruncate(shm_fd, sizeof(share_data));

// map the shared memory segment to the address space of the process
share_data *data = mmap(0, sizeof(share_data), PROT_WRITE, MAP_SHARED, shm_fd, 0);
if (data == MAP_FAILED) {
perror("mmap");
return 1;
}
pid = fork();
if (pid == 0) {
data->sequence_size = i;
printf("Child is producing the Fibonacci Sequence...\n");
if (i == 1) {
data->fib_seq[0] = 0;
} else if (i == 2) {
data->fib_seq[0] = 0;
data->fib_seq[1] = 1;
} else {
int index = 1;
data->fib_seq[0] = 0;
data->fib_seq[1] = 1;
i -= 2;
while (i > 0 && index < MAX_SEQ) {
n = a + b;
index++;
data->fib_seq[index] = n;
a = b;
b = n;
i--;
}
}

// detach shared memory segment from your address space
// release the resources associated with it
munmap(data, sizeof(share_data));
printf("Child ends\n");
} else {
printf("Parent is waiting for child to complete...\n");
waitpid(pid, NULL, 0);
printf("Fibonacci Sequence: ");
for (int j = 0; j < data->sequence_size; j++) {
printf("%ld ", data->fib_seq[j]);
}
printf("\n");
// detach shared memory segment from your address space
munmap(data, sizeof(share_data));
// remove the shared memory segment
shm_unlink("fibonacci");
}
}
return 0;
}

3.20

透過pipe,我們可以來做到這題想要做的事情:將input.txt的資料被放在copy.txt裡面,用於從一個檔案讀取內容,然後將其寫入另一個檔案。我們會將inputs裡的資料寫進memory裡面,並且將這些資料放到copt.txt之中。 在第11行中,我們創建一個無名管道pipe(file),並將文件描述符存儲在file數組中,這樣下來,我們就可以將inputs跟outputs寫入pipe之中,以達成我們的目的。在第12到15行中,我們也檢查了輸入是否有specify input.txt跟輸出檔案(不一定叫做copy.txt)。

第16、17行的int source = open(argv[1], 0)代表打開第一個命令行參數指定的檔案,用於讀取內容。而int dest = open(argv[2], O_RDWR|O_CREAT|O_APPEND, 0666)代表打開第二個命令行參數指定的檔案,用於寫入內容。如果任一檔案打開失敗,則打印一條錯誤信息並退出程式。

如果能夠成功打開的話,就可以創建child process,也就是pid = fork();

在子進程中執行以下操作。操作逐行解釋如下:

1. close(file[1]);關閉管道的寫入端,因為子進程只需要從管道讀取。

2. while(read(file[0], memory, sizeof(memory)>0)){ … }從管道讀取數據,並將其寫入目標檔案,直到讀取結束。

3. close(file[0]);關閉管道的讀取端。

4. else:

5. close(file[0]);關閉管道的讀取端,因為父進程只需要向管道寫入。

6. while(read(source, memory, sizeof(memory)>0)){ … }從源檔案讀取數據,並將其寫入管道,直到讀取結束。

7. close(file[1]);關閉管道的寫入端,以通知子進程讀取完成。

8. wait(NULL);等待子進程結束。

我們透過這樣的定義,讓input的資料可以被讀取,並且被寫入到copy.txt之中,這樣的過程是被分開處理的,並且可以讓資源被妥善利用。以下是結果:

圖(三):檔案執行之前,只有input.txt,沒有output.txt。

圖(四):程式執行後,新增的檔案。

雖然terminal中沒有輸出任何的句子,但是可以看到我們成功創建了output.txt,並且擁有和Input.txt相同的內容。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/wait.h>
int main(int argc, char* argv[]){
pid_t pid;
char memory[100];
int file[2];
pipe(file);
if (argc != 3){
printf("please identify input file and output file");
exit(1);
}else{
int source = open(argv[1], 0);
int dest = open(argv[2], O_RDWR|O_CREAT|O_APPEND, 0666);
if (source == -1 || dest == -1){
printf("Files can't be open");
exit(1);
}
pid = fork();
if (pid == 0){
close(file[1]);
while(read(file[0], memory, sizeof(memory)>0)){
write(dest, memory, strlen(memory));
}
close(file[0]);
}else{
close(file[0]);
while(read(source, memory, sizeof(memory)>0)){
write(file[1], memory, strlen(memory));
}
close(file[1]);
wait(NULL);
}
}
}

--

--

Vincent Ko
Vincent Ko

Written by Vincent Ko

又名為黑翅鳶羽札,2024年即將邁向大四,正在國泰銀行資訊部門實習,可能會帶來第一手GenAI相關知識。LLM、人工智慧、資料分析與處理;財金、管理、財金數據分析。

No responses yet