網(wǎng)站吸引流量的方法seo推廣崗位職責
文章目錄
- 一.進程的概念
- 進程控制塊(PCB)
- 二.進程查看
- 通過指令查看進程
- 通過proc目錄查看
- 進程的`cwd`和`exe`
- 獲取進程pid和ppid
- 通過fork()創(chuàng)建子進程
一.進程的概念
進程是一個運行起來的程序,而程序是存放在磁盤的,cpu要想執(zhí)行程序的指令,需要先將程序加載到內(nèi)存中。
課本概念:進程是被加載到內(nèi)存運行的程序。
內(nèi)核觀點:擔當分配系統(tǒng)資源(CPU時間,內(nèi)存)的實體。
操作系統(tǒng)中有著大量的進程,操作系統(tǒng)作為管理者,管理的其實是大量進程相關(guān)的數(shù)據(jù),那么如何管理這些數(shù)據(jù)呢?
當二進制代碼直接加載到內(nèi)存時,操作系統(tǒng)為了更好地管理加載的程序,創(chuàng)建了描述該進程的數(shù)據(jù)結(jié)構(gòu)。這樣,操作系統(tǒng)只用看這個數(shù)據(jù)結(jié)構(gòu),不用管各種復雜多樣的二進制代碼,并且將它們組織起來進行管理。
進程控制塊(PCB)
這個數(shù)據(jù)結(jié)構(gòu)叫PCB(process control block),進程信息被放在其中,可以理解為進程屬性的集合,在linux的PCB是task_struct
。
struct task_struct {volatile long state; /* -1 unrunnable, 0 runnable, >0 stopped */struct thread_info *thread_info;atomic_t usage;unsigned long flags; /* per process flags, defined below */unsigned long ptrace;int lock_depth; /* Lock depth */int prio, static_prio;struct list_head run_list;prio_array_t *array;//.....
}
當有一個程序被加載到內(nèi)存時,操作系統(tǒng)會為該進程在內(nèi)存中創(chuàng)建一個task_struct
類型的對象,并將該進程放入雙鏈表等其他結(jié)構(gòu)中。這樣,操作系統(tǒng)對進程的管理就變?yōu)?strong>操作系統(tǒng)對PCB的管理,再變?yōu)?strong>操作系統(tǒng)對雙鏈表等結(jié)構(gòu)的增刪查改等操作。
由此可以總結(jié):進程 = 內(nèi)核數(shù)據(jù)結(jié)構(gòu)(PCB等)+ 可執(zhí)行程序(代碼+數(shù)據(jù))
二.進程查看
通過指令查看進程
為了讓進程能夠一直運行方便觀察,寫一個死循環(huán)程序,讓其每隔1秒鐘打印一句話。
#include <stdio.h>
#include <unistd.h>int main()
{while(1){printf("It's a process.\n");sleep(1);}return 0;
}
隨后運行它,此時該程序變成了一個進程:
接著就可以用ps
指令查看進程信息,同時配合grep
進行抓取
ps ajx | grep myprocess
得到以下結(jié)果:
可以看到系統(tǒng)中關(guān)于myprocess
的進程一共有兩個,第一行是我們寫的運行的程序,第二行是grep
命令進行抓取的進程。展示了各種信息:PPID、PID、PGID等等,這些就是PCB的一部分。
注意:task_struct
是內(nèi)核數(shù)據(jù)結(jié)構(gòu),查看進程信息讀取該數(shù)據(jù),必須要通過系統(tǒng)調(diào)用。
通過proc目錄查看
proc
是一個目錄,里面存放當前系統(tǒng)實時的 進程信息。
ls /proc
這里的數(shù)字就是進程的PID
,由于此時已經(jīng)將myprocess
進程停止,此目錄并沒有找到名為167647
的目錄。
但是,仔細看,卻有165058
,這是剛才myprocess
的父進程ID即PPID
,通過指令可以知道,該進程其實就是bash
:
再次運行myprocess
,并且通過指令得到其PID
,進入該文件夾,可以發(fā)現(xiàn)進程的數(shù)據(jù)顯式存在文件中。
進程的cwd
和exe
查看該目錄詳細信息,有兩個文件很矚目
cwd
: Current Work Directory 指出該進程當前工作路徑
exe
: 指出該進程可執(zhí)行程序的磁盤文件
修改程序,添加一個fopen
函數(shù)
#include <stdio.h>
#include <unistd.h>int main()
{FILE* fp = fopen("1.txt", "w"); // 若不存在就創(chuàng)建while (1) {printf("It's a process.\n");sleep(1);}
}
這恰好就是cwd
鏈接的目錄,說明fopen
使用了查看cwd
的系統(tǒng)調(diào)用。
再看exe
,此時進程運行中,直接刪除其鏈接在磁盤中的文件,發(fā)現(xiàn)進程沒有終止,停止進程再運行顯然就會失敗了。
運行程序,本質(zhì)就是將其從磁盤拷貝至內(nèi)存中,進程與其磁盤上對應(yīng)程序沒有直接關(guān)系。
獲取進程pid和ppid
可以直接通過系統(tǒng)調(diào)用getpid()
和getppid()
得到當前進程的pid和ppid(父進程的pid),返回值為pid_t
類型,底層就是整數(shù)。
運行以下代碼
#include <stdio.h>
#include <unistd.h>int main()
{while (1){printf("It's a process.\t");printf("pid:%d, ppid:%d\n",getpid(), getppid());sleep(1);}return 0;
}
可以看到打印出當前進程的pid
和ppid
。
通過ps axj | head -1; ps axj | grep 184670
進行驗證,當前進程是./myprocess
且其父進程是bash
。
通過fork()創(chuàng)建子進程
通過man
指令查看fork()
函數(shù)細節(jié)
fork()
函數(shù)可以創(chuàng)建子進程,創(chuàng)建成功后父子進程代碼共享。
若成功創(chuàng)建,子進程的pid
返回給父進程,0返回給子進程;
若失敗,-1返回給父進程,沒有子進程。
代碼共享可以通過以下代碼得到驗證
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>int main()
{printf("before\n");fork();printf("Hello, pid:%d\n", getpid());
}
在fork()
之前的代碼只執(zhí)行了一次,之后的代碼執(zhí)行了兩次,這兩次分別是兩個進程執(zhí)行的。
創(chuàng)建父子進程是為了做不同的事情,一般是通過if/else
來進行分流達到的,這恰恰用到了fork()
有兩個返回值的特點,下面的代碼若是初見一定會迷惑。
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>int main()
{pid_t id = fork();// id: 0-子進程 >0-父進程if (id == 0){while(1){printf("child process, pid: %d, ppid: %d", getpid(), getppid());sleep(1);}}else{while(1){printf("father process, pid: %d, ppid: %d", getpid(), getppid());sleep(1);}}
}
利用父子進程fork()
返回值不同,達到兩個死循環(huán)都在不斷執(zhí)行的效果:
通過指令查看,確實兩個進程是父子進程關(guān)系:
下面來簡要分析上面的情況,具體細節(jié)會在之后進程地址空間部分詳談。
- 為什么兩個死循環(huán)會同時執(zhí)行?
上節(jié)講過,進程 = 內(nèi)核數(shù)據(jù)結(jié)構(gòu)(PCB等)+ 可執(zhí)行程序(代碼+數(shù)據(jù))。通過fork()
創(chuàng)建子進程,肯定也要給子進程創(chuàng)建一個獨立的task_struct
,而其代碼和數(shù)據(jù)指向了父進程接下來的代碼和數(shù)據(jù)。子進程的大部分屬性值也是由父進程拷貝而來,修改前地址不會改變。
在CPU角度,它不會管誰是父進程,誰是子進程,會在操作系統(tǒng)的管理下并發(fā)執(zhí)行。在我們的視角下,兩個死循環(huán)同時執(zhí)行了。
- 為什么
fork()
返回值如此設(shè)計?
父與子的關(guān)系是一對一或者一對多的。這樣的關(guān)系導致父找子并不容易,所以創(chuàng)建子進程成功后需要把子進程的pid
返回給父進程,方便父進程控制子進程。
而子找父是很容易的,通過系統(tǒng)調(diào)用getppid()
即可。
- 為什么
fork()
會返回兩次值?
fork()
之前只有父進程,即只有父進程才能調(diào)用fork()
。fork()
內(nèi)部在return
之前肯定已經(jīng)將子進程創(chuàng)建成功,又子進程和父進程在創(chuàng)建成功后代碼共享,那么子進程和父進程都會執(zhí)行return
這條語句,這也就是為什么fork()
會返回兩次值。
- 同一個變量
id
怎么會既大于0,又等于0?
進程之間具有獨立性,一個進程崩潰了,不會影響另一個進程。這里的id
是父子進程的共享數(shù)據(jù),若父子進程對共享數(shù)據(jù)有寫操作,這時操作系統(tǒng)會將該數(shù)據(jù)拷貝兩份,這就是寫時拷貝。那么此時,雖然這是同一個變量名,但實際上表示的是不同的值,那么id
出現(xiàn)兩種情況也就不足為奇了,實際在底層的空間根本就不是一個。