线程中可以创建进程吗_Linux 进程线程是如何创建的?
上文講了《Linux進(jìn)程在內(nèi)核眼中是什么樣子的?》,可以理解內(nèi)核關(guān)于進(jìn)程線程的所有管理就通過(guò)一個(gè)結(jié)構(gòu)體 —— task_struct。知道了內(nèi)核眼中進(jìn)程的描述,本文通過(guò)三個(gè)例子站在用戶態(tài)看下進(jìn)程線程是如何創(chuàng)建的,不同的創(chuàng)建方式又有哪些優(yōu)劣?
fork例子
先看一個(gè)例子:
#include#include#includeint main() { pid_t pid; int cnt = 0; pid = fork(); if(pid<0) printf("error in fork!\n"); else if(pid == 0) { cnt++; printf("cnt=%d\n",cnt); printf("I am the child process,ID is %d\n",getpid()); } else { cnt++; printf("cnt=%d\n",cnt); printf("I am the parent process,ID is %d\n",getpid()); } return 0; }運(yùn)行結(jié)果為:cnt=1I am the parent process,ID is 15247cnt=1I am the child process,ID is 15248注意,第二個(gè)cnt并不是2,為什么會(huì)這個(gè)結(jié)果呢?因?yàn)樽舆M(jìn)程是父進(jìn)程的副本,它將獲得父進(jìn)程數(shù)據(jù)空間、堆、棧等資源的副本。這意味著父子進(jìn)程間不共享這些存儲(chǔ)空間。內(nèi)核將復(fù)制父進(jìn)程的地址空間內(nèi)容給子進(jìn)程,因此,子進(jìn)程有了獨(dú)立的地址空間。由于在復(fù)制時(shí)復(fù)制了父進(jìn)程的堆棧段,所以兩個(gè)進(jìn)程都停留在fork函數(shù)中,等待返回。因此fork函數(shù)會(huì)返回兩次,一次是在父進(jìn)程中返回,另一次是在子進(jìn)程中返回,這兩次的返回值是不一樣的。調(diào)用fork之后,數(shù)據(jù)、堆棧有兩份,但是代碼段仍然為一份,這個(gè)代碼段是兩個(gè)進(jìn)程的共享代碼段,都從fork函數(shù)中返回。當(dāng)父子進(jìn)程有一個(gè)想要修改數(shù)據(jù)或者堆棧時(shí),兩個(gè)進(jìn)程真正分裂。
fork有兩個(gè)特點(diǎn):- “調(diào)用一次,返回兩次”,在父進(jìn)程中調(diào)用一次,在父進(jìn)程和子進(jìn)程中各返回一次。
- 所有由父進(jìn)程打開(kāi)的描述符都被復(fù)制到子進(jìn)程中。父、子進(jìn)程中相同編號(hào)的文件描述符在內(nèi)核中指向同一個(gè)file結(jié)構(gòu)體,也就是說(shuō),file結(jié)構(gòu)體的引用計(jì)數(shù)要增加。
vfork例子
把上面程序中的fork改成vfork,運(yùn)行結(jié)果是什么樣子的呢?
cnt=1I am the child process,ID is 15385cnt=-486109114I am the parent process,ID is 15384a.out: cxa_atexit.c:100: __new_exitfn: Assertion `l != NULL' failed.Aborted?(core?dumped)咦?為什么會(huì)有段錯(cuò)誤?這是因?yàn)闆](méi)有調(diào)用exec函數(shù),vfork()保證子進(jìn)程先運(yùn)行,在它調(diào)用exec或exit之后父進(jìn)程才可能被調(diào)度運(yùn)行。我們把上面的程序修改如下:#include#include#includeint main() { pid_t pid; int cnt = 0; pid = vfork(); if(pid<0) printf("error in fork!\n"); else if(pid == 0) { cnt++; printf("cnt=%d\n",cnt); printf("I am the child process,ID is %d\n",getpid()); _exit(0); } else { cnt++; printf("cnt=%d\n",cnt); printf("I am the parent process,ID is %d\n",getpid()); } return 0; }運(yùn)行結(jié)果如下:cnt=1I am the child process,ID is 15524cnt=2I am the parent process,ID is 15523可見(jiàn)成功執(zhí)行了,并且cnt是2。因?yàn)檎{(diào)用了exec,使得子進(jìn)程退出,父進(jìn)程執(zhí)行,這樣else 后的語(yǔ)句就會(huì)被父進(jìn)程執(zhí)行,又因在子進(jìn)程調(diào)用exec或exit之前與父進(jìn)程數(shù)據(jù)是共享的, 所以子進(jìn)程退出后把父進(jìn)程的數(shù)據(jù)段count改成1 了,子進(jìn)程退出后,父進(jìn)程又執(zhí)行,最終就將cnt變成了2。fork 和?vfork的一些思考
根據(jù)上面的例子我們知道 fork 和 vfork 各有優(yōu)劣,可以用下圖大概描述。
圖片來(lái)自網(wǎng)絡(luò)fork 要多拷貝一次內(nèi)存,vfork 用起來(lái)又麻煩而且有風(fēng)險(xiǎn),講真,并不鼓勵(lì)用 vfork。那么有沒(méi)有辦法對(duì) fork 做個(gè)優(yōu)化,答案是肯定的。目前內(nèi)核對(duì) fork 做了寫時(shí)拷貝(COW)的優(yōu)化。也就是說(shuō),對(duì)于fork后并不是立馬拷貝內(nèi)存,而是只有你在需要改變的時(shí)候,才會(huì)從父進(jìn)程中拷貝到子進(jìn)程中,這樣fork 后立馬執(zhí)行 exec 的成本就非常小了。clone?創(chuàng)建線程
現(xiàn)在我們知道了創(chuàng)建進(jìn)程有兩種方式:fork,vfork。那么創(chuàng)建線程呢?
首先得知道什么是進(jìn)程,什么是線程。有句名言 “進(jìn)程是資源管理的最小單位,線程是程序執(zhí)行的最小單位。”?在操作系統(tǒng)設(shè)計(jì)上,從進(jìn)程演化出線程,最主要的目的就是減小多進(jìn)程上下文切換開(kāi)銷。
因此進(jìn)程之間共享代碼段,文件描述符,信號(hào)處理,全局變量等的話就稱為線程,如果不共享,就是我們所說(shuō)的進(jìn)程。
線程的創(chuàng)建接口是用 clone,或者經(jīng)常用的 pthread_create。進(jìn)程線程創(chuàng)建總圖
我們先站在上帝視角以一張圖來(lái)看下進(jìn)程線程創(chuàng)建的大體框架,具體的實(shí)現(xiàn)下文見(jiàn)。
添加極客助手微信,加入技術(shù)交流群
長(zhǎng)按,掃碼,關(guān)注公眾號(hào)
總結(jié)
以上是生活随笔為你收集整理的线程中可以创建进程吗_Linux 进程线程是如何创建的?的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 机房维护 网拷_机房维护一二三
- 下一篇: 查看网口命令_20个常用Linux命令