一、認識進程 --- PCB
寫一個死循環程序執行起來,觀察進程
ps ajx? ? ? ? 顯示所有進程
用分號可以在命令行的一行中執行多條指令,也可以用 && :
ps ajx | head -1 && ps ajx | grep proc
終止掉進程后再查看:
所以 ./proc 就是死循環程序運行的進程
grep --color=auto proc 之所以一直都在,是因為命令行指令執行時也是一個進程
ps ajx | grep proc 執行時,grep 指令也變成了一個進程,查詢結果自然就會包含在內
可以用 grep 的 -v 選項反向搜索來不顯示這個進程:
進程可以分為兩種:一是執行完就退出的,例如指令,二是用戶不關就不退的,被稱為常駐進程,例如殺毒軟件。
二、進程屬性 --- task_struct 內容分類
1. PID
PID 是進程的標示符,用于唯一標識一個進程。
可以用 getpid()?來獲取進程的 PID:(需要 sys/types.h 頭文件)
2.kill
kill 是一條進程相關的指令,有許多選項:
通過 -9 或 -SIGKILL 可以關閉一個進程:
kill -9 <PID>
3. ?/proc
Linux 中一切皆文件,所以進程的屬性也以文件的形式可供用戶查看
這里以數字命名的每個目錄都代表著一個進程,里面的文件都是這個進程相關的屬性
其中:
(1)exe
exe 是一個鏈接文件,標識了這個進程來源于哪個可執行程序
如果我們刪掉 proc 文件,就會變成:
注意此時進程之所以還在進行,還有這個進程的目錄,是因為我們的刪除操作刪除掉的是磁盤中的 proc 文件,而進程是內存級的,只要不停止運行就會一直在。
(2)cwd
current work directory “當前工作目錄”的縮寫
進程剛剛創建時,會用自己的 cwd 屬性記錄下程序所在的目錄作為默認的“當前路徑”
如果我們在程序中用 fopen 打開一個新 log.txt 文件,運行后就會在當前路徑下創建出一個 log.txt 文件。這個“當前路徑”,就來自于進程的 cwd 屬性。
而且這個文件被新建時,創建的路徑使用的是絕對路徑,是用進程的 cwd 和文件名拼接成的:/home/mmr/linux-c/par02/log.txt
想要改變進程的 cwd,可以用 chdir()
就可以把進程的 cwd 更改為根目錄,log.txt 也就可以新建在根目錄下了(注意普通用戶沒有根目錄的寫權限,所以普通用戶會創建失敗)
(3)/proc是內存級文件
/proc 并不存儲在硬盤當中,關機時整個文件被釋放掉,不會存儲。
4.PPID
在 Linux 系統中,系統啟動之后,新創建的任何進程,都是由自己的父進程創建的。
PPID 就是父進程的 PID 。
可以用 getppid() 來獲取進程的 ppid
可以看到 ppid 一直是相同的,我們查看一下:
可以看到這個進程是 bash,叫做命令行解釋器,是 Linux 系統的shell?外殼
命令行中,執行指令、執行程序,本質都是 bash 的進程,創建的子進程,由子進程來執行代碼
用戶每一次登錄,系統都會為用戶創建一個 bash 進程,這里 bash 前有一個 "-",代表當前這個用戶是使用命令行終端進行登錄的。
三、使用系統調用,創建進程 ---?fork
我們運行一下:
可以看到 child proc 打印了兩遍。
這是因為 fork 創建出子進程后,原先的進程和子進程都要運行。原先的進程的父進程是 bash,子進程的父進程是原先的進程。
這兩個進程先后連續創建的,所以 pid 也是連續的。
由 fork 的返回值可以知道,如果子進程創建成功,那么在原先的進程中,獲得的返回值是子進程的 pid,而子進程中獲得的 fork 返回值是 0
fork 創建的子進程,與其父進程共享同一份代碼,但是數據是各自私有一份的,互不干擾。
父進程的代碼,是由硬盤加載進內存后運行的,而子進程的代碼,是直接共享自父進程的,而非從硬盤中加載得來。
所以說,進程具有很強的獨立性。
多個進程之間,運行時,是互不影響的。
四、創建多個進程
1. C++
Linux 中 C++ 可以使用 .cpp .cc .cxx 作為后綴
使用 g++ -o <程序名> <源文件名> 生成可執行程序
如果使用了C++11中的語法:
2.創建多個進程
為什么這里子進程的 PPID 是 1 ?
因為我截這個圖時父進程已經執行完畢掛掉了,此時子進程的 PPID 就不會是原先的父進程,會被 init 進程,托管給 1 號進程。
五、再理解創建子進程
1. fork 函數為什么有兩個返回值
在 fork 函數體內部,先是父進程在走,在走的時候,子進程被創建出來,因為父子進程是共享同一份代碼的,所以這時候就是父子進程一起在走 fork 函數。
當 fork 函數運行到最后的 return 語句時,父子進程各自執行一次 return,父進程返回父進程的值,子進程返回子進程的值。又因為父子進程的數據是各自私有一份的,所以即使返回值不同也互不干預,不會有任何后果。
因此雖然一個函數有兩個返回值,但其實并不沖突。
2. fork之后,父子誰先運行
fork之后,父子進程誰先運行是不確定的,是由OS的調度器自主決定的。