用戶態(tài)進(jìn)程/線程的創(chuàng)建 Fork/vfork/Pthread_Create
fork
fork 函數(shù)創(chuàng)建子進(jìn)程成功后,父進(jìn)程返回子進(jìn)程的 pid,子進(jìn)程返回0。具體描述如下:
- fork返回值為-1, 代表創(chuàng)建子進(jìn)程失敗
- fork返回值為0,代表子進(jìn)程創(chuàng)建成功,這個(gè)分支是子進(jìn)程的運(yùn)行邏輯
- fork返回值大于0,這個(gè)分支是父進(jìn)程的運(yùn)行邏輯,并且返回值等于子進(jìn)程的 pid
我們看下通過(guò) fork 系統(tǒng)調(diào)用來(lái)創(chuàng)建子進(jìn)程的例子:
- #include <stdio.h>
- #include <sys/types.h>
- #include <unistd.h>
- int main()
- {
- pid_t pid = fork();
- if(pid == -1){
- printf("create child process failed!\n");
- return -1;
- }else if(pid == 0){
- printf("This is child process!\n");
- }else{
- printf("This is parent process!\n");
- printf("parent process pid = %d\n",getpid());
- printf("child process pid = %d\n",pid);
- }
- getchar();
- return 0;
- }
運(yùn)行結(jié)果:
- $ ./a.out
- This is parent process!
- parent process pid = 25483
- child process pid = 25484
- This is child process!
從上面的運(yùn)行結(jié)果來(lái)看,子進(jìn)程的pid=25484, 父進(jìn)程的pid=25483。
在前面介紹內(nèi)存缺頁(yè)異常的時(shí)候,提到寫時(shí)復(fù)制 COW 是一種推遲或者避免復(fù)制數(shù)據(jù)的技術(shù),主要用在 fork 系統(tǒng)調(diào)用里,當(dāng)執(zhí)行 fork 創(chuàng)建新子進(jìn)程時(shí),內(nèi)核不需要復(fù)制父進(jìn)程的整個(gè)進(jìn)程地址空間給子進(jìn)程,而是讓父進(jìn)程和子進(jìn)程共享同一個(gè)副本,只有寫入時(shí),數(shù)據(jù)才會(huì)被復(fù)制。我們用一個(gè)簡(jiǎn)單里的例子描述下:
- #include <stdio.h>
- #include <sys/types.h>
- #include <unistd.h>
- int peter = 10;
- int main()
- {
- pid_t pid = fork();
- if(pid == -1){
- printf("create child process failed!\n");
- return -1;
- }else if(pid == 0){
- printf("This is child process, peter = %d!\n", peter);
- peter = 100;
- printf("After child process modify peter = %d\n", peter);
- }else{
- printf("This is parent process = %d!\n", peter);
- }
- getchar();
- return 0;
- }
執(zhí)行結(jié)果:
- $ ./a.out
- This is parent process = 10!
- This is child process, peter = 10!
- After child process modify peter = 100
從運(yùn)行結(jié)果可以看到,不論子進(jìn)程如何去修改 peter 的值,父進(jìn)程永遠(yuǎn)看到的是自己的那一份。
vfork
接下來(lái)看下使用 vfork 來(lái)創(chuàng)建子進(jìn)程:
- #include <stdlib.h>
- #include <stdio.h>
- #include <sys/types.h>
- #include <unistd.h>
- int peter = 10;
- int main()
- {
- pid_t pid = vfork();
- if(pid == -1){
- printf("create child process failed!\n");
- return -1;
- }else if(pid == 0){
- printf("This is child process, peter = %d!\n", peter);
- peter = 100;
- printf("After child process modify peter = %d\n", peter);
- exit(0);
- }else{
- printf("This is parent process = %d!\n", peter);
- }
- getchar();
- return 0;
- }
運(yùn)行結(jié)果:
- $ ./a.out
- This is child process, peter = 10!
- After child process modify peter = 100
- This is parent process = 100!
從運(yùn)行結(jié)果中可以看出,當(dāng)子進(jìn)程修改了 peter=100 之后,父進(jìn)程中打印 peter 的值也是100。
pthread_create
現(xiàn)在我們知道了創(chuàng)建進(jìn)程有兩種方式:fork,vfork。那么創(chuàng)建線程呢?
線程的創(chuàng)建接口是用 pthread_create:
- #include <pthread.h>
- #include <stdio.h>
- #include <sys/types.h>
- #include <unistd.h>
- #include <sys/syscall.h>
- int peter = 10;
- static pid_t gettid(void)
- {
- return syscall(SYS_gettid);
- }
- static void* thread_call(void* arg)
- {
- peter = 100;
- printf("create thread success!\n");
- printf("thread_call pid = %d, tid = %d, peter = %d\n", getpid(), gettid(), peter);
- return NULL;
- }
- int main()
- {
- int ret;
- pthread_t thread;
- ret = pthread_create(&thread, NULL, thread_call, NULL);
- if(ret == -1)
- printf("create thread faild!\n");
- ret = pthread_join(thread, NULL);
- if(ret == -1)
- printf("pthread join failed!\n");
- printf("process pid = %d, tid = %d, peter = %d\n", getpid(), gettid(), peter);
- return ret;
- }
運(yùn)行結(jié)果:
- $ ./a.out
- create thread success!
- thread_call pid = 9719, tid = 9720, peter = 100
- process pid = 9719, tid = 9719, peter = 100
從上面的結(jié)果可以看出:進(jìn)程和線程的 pid 都是相同的。當(dāng)線程修改了 peter = 100 之后,父進(jìn)程中打印 peter 的值也是100。
進(jìn)程線程創(chuàng)建總圖
上面介紹了用戶態(tài)創(chuàng)建進(jìn)程和線程的方式,以及各個(gè)方式的特點(diǎn)。關(guān)于其底層的實(shí)現(xiàn)本質(zhì),我們后面會(huì)詳細(xì)講解。這里先提供一下三者之間的關(guān)系,可見(jiàn)三者最終都會(huì)調(diào)用 do_fork 實(shí)現(xiàn)。
但是內(nèi)核態(tài)沒(méi)有進(jìn)程線程的概念,內(nèi)核中只認(rèn) task_struct 結(jié)構(gòu),只要是 task_struct 結(jié)構(gòu)就可以參與調(diào)度。關(guān)于內(nèi)核態(tài)的任務(wù)創(chuàng)建,我們下文見(jiàn)。