一、認識進程
進程(PCB)=內核數據結構(task_struct)+程序的代碼和數據
每一個進程都有其獨立的task_struct,OS對眾多的task_struct進行管理,如何管理?先描述再組織,所有運?在系統?的進程都以task_struct鏈表的形式存在內核?,而且是雙向鏈表
我們也可以通過ps指令來顯示當前終端下由當前用戶啟動的進程信息
二、創建進程
????????系統調用fork()可以創建進程,有兩個返回值,如果返回值等于0,那么為子進程,如果返回值大于0,那么就是父進程,如果返回值小于0 ,那么創建進程失敗。所創建的進程是當前進程的子進程,??進程代碼共享,數據各?開辟空間,私有?份(采?寫時拷?),兩者數據互不干涉
代碼示例:
#include<iostream>
#include<unistd.h>
#include<sys/types.h>
using namespace std;
int main()
{int ret=fork();if(ret<0){exit(1);}//返回值等于0,是子進程if(ret==0){pid_t pid=getpid();pid_t ppid= getppid();cout<<"我是子進程,我的pid是:"<<" "<<pid<<"我的父進程是:"<<ppid<<endl;}//返回值大于0,是父進程else{pid_t pid=getpid();cout<<"我是父進程,我的pid是:"<<pid<<endl;}return 0;
}
三、驗證父子進程的獨立性(寫實拷貝)
????????進程間有獨立性,哪怕是父子進程也不例外,子進程的資源從父進程獲得,但是獲得后子進程的數據通過寫實拷貝擁有了獨立性。
代碼驗證:
#include<iostream>
#include<unistd.h>
#include<sys/types.h>
using namespace std;
int main()
{int ret=fork();int count=100;if(ret<0){exit(1);}//返回值等于0,是子進程if(ret==0){pid_t pid=getpid();pid_t ppid= getppid();while(1){cout<<"我是子進程,我的pid是:"<<" "<<pid<<"我的父進程是:"<<ppid<<"count的值為:"<<count<<endl;count++;sleep(4);}}//返回值大于0,是父進程else{pid_t pid=getpid();while(1){cout<<"我是父進程,我的pid是:"<<pid<<"count的值為:"<<count<<endl;count--;sleep(2);}}return 0;
}
可以觀察到,相同的變量count,在子進程里面是遞增的,在父進程里面是遞減的。?
四、進程的常見狀態
?
?
監測S狀態
再次運行上方創建子進程的代碼,通過命令監測可以看到,?父進程和子進程都是S狀態,而不是R狀態,因為有了IO,IO執行的時間太快了,剩下的時間都是在等待狀態S,所以我們就無法監測到R狀態,想要監測到R狀態,只要把IO設備取消就好,如沒有輸入輸出函數的死循環
?
監測R狀態?
?
?僵尸進程
????????所有的進程都是某個進程的子進程,所創建的子進程都是拿來執行某個任務的,任務完成的怎么樣,完成的相關信息父進程是需要知道的。
? ? ? ? 一個子進程在死亡到被抬走之間的時間,子進程的狀態就是僵尸狀態Z,目的就是為例讓父進程獲取子進程的退出信息。如果子進程退出,父進程不回收,不獲取子進程的退出信息,那么子進程的task_struck會一直存在,就類似與C語言中的結構體,一直占用空間,那么就會造成內存泄露。
?孤兒進程
????????如果父進程退出了,子進程沒退出,子進程還在運行,那么子進程就是個孤兒進程。子進程被OS領養,也就是被進程1領養。父進程庫隨意退,因為父進程的父進程是bash
五、進程終止
進程的正常終止有三種:main返回,exit,_exit。
我們可以通過echo $?查看退出碼,以獲得最后?次執?的命令的狀態。
exit與_exit
兩者都用于終止進程,并設置退出碼,但是exit終止進程前會對?I/O 緩沖區被刷新,并且會執行注冊的終止處理函數,保證程序的資源得到正確釋放和清理。而_exit不會調用任何注冊的終止處理函數,它會直接終止進程,繞過這些清理操作。
使用exit():
#include<iostream>
using namespace std;int main()
{cout<<"helloworld";exit(1);return 0;
}
使用_exit()
#include<iostream>
#include<unistd.h>
using namespace std;int main()
{cout<<"helloworld";_exit(0);return 0;
}
wait與waitpid
????????如果子進程退出,父進程沒有回收,那么就會進入僵尸,那么kill -9 也沒辦法,所以父進程等待子進程是有必要的
??wait
?函數會讓調用它的進程阻塞,直至其任意一個子進程終止。之后,它會獲取子進程的終止狀態,并將其存儲于?status
?所指向的內存位置。如果不在意子進程的終止信息,那么可以設置status為null。
#include<iostream>
#include <sys/types.h>
#include <sys/wait.h>
#include<unistd.h>
using namespace std;int main()
{pid_t id=fork();if(id==0){sleep(10);cout<<"我是子進程"<<endl;}else{wait(nullptr);cout<<"我是父進程,已經回收子進程完畢"<<endl;}return 0;
}
上面的代碼,子進程完成cout后被父進程回收,才執行父進程的cout?
waitpid
?函數比?wait
?函數更靈活,它能讓你指定要等待的子進程。
#include<iostream>
#include <sys/types.h>
#include <sys/wait.h>
#include<unistd.h>
using namespace std;int main()
{pid_t id=fork();if(id==0){cout<<"我是子進程"<<endl;sleep(10);}else{// wait(nullptr);waitpid(id,nullptr,0);//0表示阻塞等待cout<<"我是父進程,已經回收子進程完畢"<<endl;}return 0;
}