CreateProcess
fork() exit() wait() execev() 簡介
- fork()
- parent process經由呼叫fork()建立一個 child process
- child獲得parent的stack segment、data segment、heap segment、text segment
- 可說是把parent process一分為二
- exit()
- terminate a process
- 將佔用的所有資源歸還給kernel
- parent 可以利用 wait()來取得結束的狀態(status)
- wait()
- 若child process還未呼叫exit(),那wait()會suspend parent process,直到有任一child process terminated
- 可以取得status
- execve()
- Load a new program到目前process的記憶體
- 丟去現存的text segment
- 重新建立 stack segment、data segment、heap segment
大致流程圖

fork()
1
2
3
|
#include <unistd.h>
pid_t fork(void);
In parent: returns process ID of child on success, or –1 on error; in successfully created child: always returns 0
|
- 呼叫後將存在兩個process,且每個process都會從fork()的返回處繼續執行
- 兩個process擁有不同的stack segment、data segment、heap segment、text segment副本(child可以再修改,且不影響parent)
- 返回pid == 0是child process
- 返回pid != 0是parent process
- 無法確定誰先執行
Parent and child 檔案共用
執行fork()後,child會有parent的file description 副本,意味著雙方對應的描述符均指向相同的開啟檔案描述符
- 開啟描述符包含
- 目前的offset
- file status flag
一個處於開啟狀態下的檔案,會在父子process中共用檔案狀態屬性
用途
- 假設父子同時寫入同一個檔案,共用檔案offset會確保兩者不會覆蓋彼此的輸出內容
- 若不需要共用這類的檔案屬性,在fork()後應:
- 使父子使用不同的file description
- 各自關閉不再使用的description
vfork()
1
2
3
|
#include <unistd.h>
pid_t vfork(void);
In parent: returns process ID of child on success, or –1 on error; in successfully created child: always returns 0
|
源由與目前的fork()
在早期BSD,fork()會對文字、stack、heap等區段進行完整的複製,但經常有馬上就使用exec()的狀況,導致前面的複製動作很多餘,因此引用了vfork(),使用在fork()後立刻使用exec()的時候,增加了效率
但目前UNIX採用copy-on-write實作fork(),因此大幅消除了vfork()的需求
同步訊號以避免Race condition
呼叫fork()後,若A-process 需要等 B-process 完成,那B-process可以在動作完成之後向A-process發送訊號,A-process等待
範例
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
58
59
|
#include <signal.h>
#include "curr_time.h"
#include "tlpi_hdr.h"
#define SYNC_SIG SIGUSR1 /* Synchronization signal */
static void
handler(int sig) /* Signal handler - does nothing but return */
{
}
int
main(int argc, char *argv[])
{
pid_t childPid;
sigset_t blockMask, origMask, emptyMask;
struct sigaction sa;
setbuf(stdout, NULL); /* Disable buffering of stdout */
sigemptyset(&blockMask);
sigaddset(&blockMask, SYNC_SIG); /* Block signal */
if(sigprocmask(SIG_BLOCK, &blockMask, &origMask) == -1)
errExit("sigprocmask");
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
sa.sa_handler = handler;
if (sigaction(SYNC_SIG, &sa, NULL) == -1)
errExit("sigaction");
switch (childPid = fork()) {
case -1:
errExit("fork");
case 0: /* Child */
/* Child does some required action here... */
printf("[%s %ld] Child started - doing some work\n", currTime("%T"), (long) getpid());
sleep(2); /* Simulate time spent doing some work */
/* And then signals parent that it's done */
printf("[%s %ld] Child about to signal parent\n", currTime("%T"), (long) getpid());
if (kill(getppid(), SYNC_SIG) == -1)
errExit("kill");
/* Now child can do other things... */
_exit(EXIT_SUCCESS);
default: /* Parent */
/* Parent may do some work here, and then complete the required action */
printf("[%s %ld] Parent about to wait for signal\n", currTime("%T"), (long) getpid());
sigemptyset(&emptyMask);
if (sigsuspend(&emptyMask) == -1 && errno != EINTR)
errExit("sigsuspend");
printf("[%s %ld] Parent got signal\n", currTime("%T"), (long) getpid());
/* If required, return signal mask to its original state */
if (sigprocmask(SIG_SETMASK, &origMask, NULL) == -1)
errExit("sigprocmask");
/* Parent carries on to do other things... */
exit(EXIT_SUCCESS);
}
}
|
習題
- 下列程式執行後會出現幾個新行程?
1
2
3
|
fork();
fork();
fork();
|
Ans:
本身 -> p1, p2, p3
p1 -> p11, p12
p2 -> p21
p3
p11 -> p111
共:p1 + p2 + p3 + p11 + p12 + p21 + p111 = 7(個)