進(jìn)程概念
進(jìn)程是程序在計(jì)算機(jī)上執(zhí)行一次的過程,也就是一個(gè)執(zhí)行中的程序,進(jìn)程是一個(gè)獨(dú)立的邏輯控制流,獨(dú)占處理器,相當(dāng)于工人和機(jī)器,進(jìn)程具有私有的地址空間,獨(dú)占存儲器系統(tǒng),相當(dāng)于工廠;進(jìn)程的本質(zhì)是程序在地址空間中按照代碼邏輯控制流執(zhí)行的過程,同時(shí)進(jìn)程是資源分配的最小單位;
進(jìn)程和程序的區(qū)別
進(jìn)程是動(dòng)態(tài)的,并且具有生命周期,并且一個(gè)進(jìn)程只能對應(yīng)于一個(gè)程序;相反,程序是動(dòng)態(tài)的指令集合,一個(gè)程序可以對應(yīng)于多個(gè)進(jìn)程;
進(jìn)程的狀態(tài)
就緒:Ready,當(dāng)進(jìn)程獲得出CPU以外的所有的必要的資源時(shí),進(jìn)程進(jìn)入就緒狀態(tài),也就是說當(dāng)進(jìn)程得到CPU就可以立即運(yùn)行;
?運(yùn)行:Runing,進(jìn)程正在占據(jù)CPU資源;
?阻塞(Blocked):進(jìn)程猶豫等待某個(gè)事件發(fā)生而無法執(zhí)行時(shí),進(jìn)程被迫放棄CPU執(zhí)行資源;
進(jìn)程的創(chuàng)建
進(jìn)程的查看
Windows可以使用tasklist 例如:tasklist / F1 "PID eq 進(jìn)程PID";
在Linux底下可以使用ps命令進(jìn)行查看,ps命令的相關(guān)選項(xiàng):
?user:表示進(jìn)程所屬的用戶;
?PID:表示進(jìn)程ID;
?ppid:表示父進(jìn)程ID;
?%CPU:表示CPU占有率;
?%mem:表示內(nèi)存占有率;
?VSZ:表示進(jìn)程虛擬大??;
?RSS:表示常駐內(nèi)存,也就是內(nèi)存中頁面的數(shù)量;
?tty:表示進(jìn)程使用的終端;
?stat:表示進(jìn)程的狀態(tài);
?start:表示啟動(dòng)進(jìn)程的時(shí)間;
?TIME :表示進(jìn)程消耗CPU的時(shí)間;
?command:表示打開進(jìn)程的命令和參數(shù);
?進(jìn)程狀態(tài)標(biāo)識符:
??D:表示不可中斷進(jìn)程,Uninterruptible;
??R:表示正在運(yùn)行,或者在隊(duì)列中的進(jìn)程;
??S:處于休眠狀態(tài);
??T:進(jìn)程處于停止或者被追蹤狀態(tài);
??Z:僵尸進(jìn)程;
??W:表示進(jìn)入內(nèi)存交換,從內(nèi)核2.6以后開始失效;
??X:表示死掉的進(jìn)程;
??<:表示高優(yōu)先級;
??n:表示低優(yōu)先級;
??s:表示包含子進(jìn)程;
??+:表示位于后臺的進(jìn)程組;
?查看某個(gè)具體的進(jìn)程:ps -p pid;ps -C在命令行進(jìn)行查看;同樣的還可以使用pstree進(jìn)行查看,但是可能需要安裝yum install psmisc -y;同樣的的還可以使用top命令動(dòng)態(tài)的查看系統(tǒng)進(jìn)程;
進(jìn)程的殺死
windows:task kill /F /PID 或者task kill /F /IM 程序名
?linux:kill 進(jìn)程標(biāo)識PID
?與進(jìn)程相關(guān)的文件一般在/proc/底下,通常是進(jìn)程id作為目錄名的文件;
進(jìn)程的操作
進(jìn)程的標(biāo)識通常使用pid來完成,獲取當(dāng)前進(jìn)程的id使用函數(shù)pid_t getpid();獲取當(dāng)前進(jìn)程父進(jìn)程的ID使用函數(shù)pid_t getppid();
#include <stdio.h>
#include <unistd.h>
int main(){
printf("PID:%dPPID:%d\n",getpid(),getppid());//分別用于獲得當(dāng)前進(jìn)程ID和父進(jìn)程ID;
for(;;);
}
關(guān)于進(jìn)程的創(chuàng)建
fork()函數(shù)
函數(shù)失敗時(shí)返回值是-1,返回值為0表示子進(jìn)程邏輯控制流,如果返回值為其他,表示父進(jìn)程控制流;
?函數(shù)的特點(diǎn):
??1、調(diào)用一次,返回兩次;
??2、相同但是具有獨(dú)立的地址空間;
??3、可以并發(fā)執(zhí)行;
??4、這個(gè)函數(shù)的本質(zhì)是函數(shù)堆棧數(shù)據(jù),text文本的復(fù)制;
函數(shù)的本質(zhì)是父進(jìn)程函數(shù)相關(guān)數(shù)據(jù)的復(fù)制,然后分叉出另一個(gè)函數(shù);
#include <stdio.h>
#include <unistd.h>
int main(){
printf("PID:%d,PPID:%d\n",getpid(),getppid());
pid_t pid = fork();
fork();
if(pid == 0){// child
printf("this is child\n");
printf("res:%d,PID:%d,PPID:%d\n",pid,getpid(),getppid());
}else{
printf("this is father\n");
printf("res:%d,PID:%d,PPID:%d\n",pid,getpid(),getppid());
}
for(;;);
}
子進(jìn)程是父進(jìn)程的副本,它將獲得父進(jìn)程數(shù)據(jù)空間、堆、棧等資源的副本。注意,子進(jìn)程持有的是上述存儲空間的“副本”,這意味著父子進(jìn)程間不共享這些存儲空間。
fork02.c:這個(gè)函數(shù)是用來構(gòu)建子進(jìn)程,用來輸出一些值;
#include <stdio.h>
#include <unistd.h>
int i = 100;
int main(){
int j=100;
pid_t pid = fork();
if(pid == 0){// child
int k;
for(k=0;k<10000;k++)
printf("this is childi%d\t j%d\n",++i,++j);
}else{
int k;
for(k=0;k<10000;k++)
printf("this is fatheri%d\t j%d\n",--i,--j);
}
}
fork03.c:這個(gè)函數(shù)父進(jìn)程在增加變量的值,子進(jìn)程在減小函數(shù)的值,用來模擬兩個(gè)進(jìn)程的運(yùn)行情況;
#include <stdio.h>
#include <unistd.h>
int i = 100;
int main(){
int j=100;
FILE* fd = fopen("./test","w+");
pid_t pid = fork();
if(pid == 0){// child
int k;
for(k=0;k<10000;k++)
fprintf(fd,"this is childi%d\t j%d\n",++i,++j);
}else{
int k;
for(k=0;k<10000;k++)
fprintf(fd,"this is fatheri%d\t j%d\n",--i,--j);
}
}
exec函數(shù)簇
char **environ;
int execl (const char *path, const char *arg0, ..., (char*)0);
int execlp(const char *file, const char *arg0, ..., (char*)0);
int execle(const char *path, const char *arg0, ..., (char*)0, char *const envp[]);
int execv (const char *path, char *const argv[]);
int execvp(cosnt char *file, char *const argv[]);
int execve(const char *path, char *const argv[], char *const envp[]);
execl.c
#include<stdio.h>
#include <unistd.h>
extern char ** environ;
int main(int argc,char **argv){
execl("/bin/ps","ps","-a","-o","pid,ppid,cmd,stat",0);
return 0;
}
execle.c
#include<stdio.h>
#include<unistd.h>
extern char **environ;
int main(int argc,char **argv){
execle("/bin/ps","ps","-a","-o","pid,ppid,cmd,stat",0,environ);
return 0;
}
execlp.c
#include <stdio.h>
#include <unistd.h>
int main(int argc,char **argv){
execlp("ps","ps","-a","-o","pid,ppid,cmd,stat",0);
return 0;
}
execv.c
#include <stdio.h>
#include <unistd.h>
int main(){
char *args[]={"ps","-a","-o","pid,ppid,cmd,stat",0};
execv("/bin/ps",args);
return 0;
}
execve.c
#include <stdio.h>
#include <unistd.h>
extern char **environ;
int main(){
char *args[]={"ps","-a","-o","pid,ppid,cmd,stat",0};
execve("/bin/ps",args,environ);
return 0;
}
execvp.c
#include<stdio.h>
#include<unistd.h>
int main(){
char *argv[] = {"ps","-a","-o","pid,ppid,cmd,stat",0};
execvp("ps",argv);
return 0;
}
函數(shù)命名的規(guī)律組總結(jié)
v:表示第二個(gè)參數(shù)是數(shù)組;
l:第二個(gè)參數(shù)之后是變參;
p:第一個(gè)參數(shù)是文件名;
e:最后一個(gè)參數(shù)是環(huán)境變量;
常見的字符串?dāng)?shù)組函數(shù)
execv(),execvp(),execve()
可變參數(shù)函數(shù)
execle(),execlp(),execl();
exec.c函數(shù):
?exec函數(shù)簇函數(shù)的返回值,-1表示失敗,執(zhí)行成功時(shí),是不返回任何值的,這些函數(shù)具有以下特點(diǎn):一次調(diào)用,失敗返回;改朝換代,取而代之;但是函數(shù)的PID是不會(huì)變化的,變化的是程序地址空間的內(nèi)容,exec函數(shù)簇函數(shù)的本質(zhì)是覆蓋原有函數(shù),執(zhí)行新的函數(shù);
system(shell字符串)
這個(gè)函數(shù)是系統(tǒng)函數(shù)int system(shell 字符串或者命令);函數(shù)的返回值為-1時(shí),表示失??;函數(shù)的返回值是127時(shí),表示無法啟動(dòng)shell;其它返回值用于表示函數(shù)的退出碼;系統(tǒng)調(diào)用函數(shù)一次調(diào)用,一次返回;這個(gè)函數(shù)本質(zhì)上是在執(zhí)行shell命令;
system.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc,char** argv){
printf("PID:%d\n",getpid());
system("sleep 3&");
printf("PID:%d\n",getpid());
}
進(jìn)程的結(jié)束
對于return和exit來說,函數(shù)的執(zhí)行屬于壽終正寢,并且自己能夠了結(jié)后事。_exit()函數(shù)屬于自殺的情況,但是沒有料理后事。abort()函數(shù)屬于意外的死亡,也沒有料理后事;信號終止屬于他殺;料理后事表示的意思是程序結(jié)束以后,相關(guān)堆棧資源的回收工作;
?main()函數(shù)return 0 是語言級別的退出,使用這種情況的退出,函數(shù)仍然需要調(diào)用exit(0)函數(shù),exit(0)是函數(shù)級別的退出;
?exit(0),表示釋放所有的靜態(tài)的全局對象,緩存,關(guān)掉所有的IO通道,然后終止程序;abort()會(huì)直接終止程序,但是不會(huì)釋放任何資源;
?exit(0)函數(shù)在調(diào)用_exit()系統(tǒng)調(diào)用之前要檢查文件的打開情況,吧文件緩沖區(qū)中的內(nèi)容寫回文件,清理IO緩沖,exit(0)是標(biāo)準(zhǔn)的C函數(shù);
?_exit()不做清理IO緩沖函數(shù),_exit()是Linux系統(tǒng)調(diào)用;
?main函數(shù)退出:
main.c
#include <stdio.h>
#include <unistd.h>
int main(){
printf("PID:%d,PPID:%d\n",getpid(),getppid());
return 100;
}
exit函數(shù)退出
exit函數(shù)一般用在main函數(shù)以外的函數(shù)退出
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(){
printf("PID:%d,PPID:%d\n",getpid(),getppid());
exit(EXIT_FAILURE);
abort();
}
_exit函數(shù)一般用來結(jié)束子進(jìn)程
這個(gè)函數(shù)暫時(shí)沒有說明
abort()函數(shù)一般用來異常退出
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(){
printf("PID:%d,PPID:%d\n",getpid(),getppid());
abort();
}
還有一種方式--信號終止
pause.c
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
void test(int sig){
printf("revc a signal%d",sig);
}
int main(){
signal(SIGINT,test);
printf("before pause\n");
pause();
printf("after pause\n");
}
進(jìn)程的停止
int sleep(unsigned int secs)函數(shù) ,sece用于表示休眠的秒數(shù),如果指定為-1表示永久休眠,函數(shù)的返回值是未休眠的秒數(shù);如果沒有信號中斷,休眠指定的秒數(shù)返回0,否則馬上返回未休眠的秒數(shù);
#include <stdio.h>
#include <unistd.h>
#include <time.h>
#include <strings.h>
int main(){
int i = 0;
for(;;){
time_t t;
time(&t);
struct tm* ptm = gmtime(&t);
char buf[BUFSIZ];
bzero(buf,BUFSIZ);
strftime(buf,BUFSIZ,"%P %T",ptm);
printf("\r%s",buf);
fflush(stdout);
sleep(1);
}
}
int pause()函數(shù)返回值一定是-1,如果函數(shù)沒有處理信號,直接中斷,執(zhí)行默認(rèn)的信號處理,程序后續(xù)代碼不再執(zhí)行。如果程序存在信號處理,執(zhí)行處理信號后,執(zhí)行后續(xù)代碼。
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
void test(int sig){
printf("revc a signal%d",sig);
}
int main(){
signal(SIGINT,test);
printf("before pause\n");
pause();
printf("after pause\n");
}
pid_t wait(int * status);函數(shù)等價(jià)于pid_t waitpid(-1,status,0);這個(gè)函數(shù)的參數(shù)包括:pid:小于-1時(shí),表示的是等待進(jìn)程組為pid的所有進(jìn)程,-1:表示等待任何子進(jìn)程;0:表示等待同組的進(jìn)程;大于0:表示等待進(jìn)程為pid的子進(jìn)程;
?status:參數(shù)的含義:
??正常結(jié)束,WIFEXITED(status),參數(shù)為0時(shí),表示的是正常結(jié)束任何子進(jìn)程,0表示的是非正常結(jié)束子進(jìn)程;WEXITSTATUS()表示取得子進(jìn)程exit()返回的結(jié)束代碼,一般會(huì)先用WIFEXITED來判斷是否正常結(jié)束才是用這個(gè)宏定義;
??異常結(jié)束:WIFSIGNALED(ststus)非0值表示異常結(jié)束子進(jìn)程,0表示非異常結(jié)束子進(jìn)程;WTERMSIG(status),取得子進(jìn)程因信號而終止的信號代碼,一般先會(huì)用WIFSIGNALED來判斷后才能使用宏;
??暫停:WIFSTOPPED(status),非0值表示暫停結(jié)束子進(jìn)程,0表示暫停結(jié)束子進(jìn)程;WSTOPSIG(status),取得引發(fā)子進(jìn)程暫停的信號代碼,一般先用
WIFSTOPPED來判斷后才能使用此宏;
?option,選項(xiàng)WNOHANG,若子進(jìn)程沒有結(jié)束,返回0,不予等待。若子進(jìn)程結(jié)束,返回該子進(jìn)程的ID;WUNTRACED,若子進(jìn)程進(jìn)入暫停狀態(tài),則馬上返回,但子進(jìn)程的結(jié)束狀態(tài)不予理會(huì)。
?返回值,-1表示失?。黄渌当硎镜却腜ID;
wait.c:
#include <stdio.h>
#include <unistd.h>
#include <wait.h>
void handler(int sig){
pid_t cpid = wait(NULL);
printf("child %d exit",cpid);
}
int main(){
signal(SIGCHLD,handler);
printf("PID:%d,PPID:%d\n",getpid(),getppid());
pid_t pid = fork();
if(pid == 0){// child
sleep(2);
printf("this is child\n");
printf("res:%d,PID:%d,PPID:%d\n",pid,getpid(),getppid());
}else{
printf("this is father\n");
printf("res:%d,PID:%d,PPID:%d\n",pid,getpid(),getppid());
}
}
wait02.c:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <wait.h>
void handler(int sig){
int status;
pid_t cpid = wait(&status);
if(WIFEXITED(status)){
printf("child exit by %d\n",WEXITSTATUS(status));
}
if(WIFSIGNALED(status)){
printf("child exit by signal %d\n",WTERMSIG(status));
}
printf("child %d exit\n",cpid);
}
int main(){
signal(SIGCHLD,handler);
printf("PID:%d,PPID:%d\n",getpid(),getppid());
pid_t pid = fork();
if(pid == 0){// child
sleep(2);
printf("this is child\n");
printf("res:%d,PID:%d,PPID:%d\n",pid,getpid(),getppid());
//exit(0);
}else{
printf("this is father\n");
printf("res:%d,PID:%d,PPID:%d\n",pid,getpid(),getppid());
printf("leave:%d\n",sleep(5));
//exit(200);
}
for(;;);
}
孤兒進(jìn)程與僵尸進(jìn)程
孤兒進(jìn)程:父進(jìn)程先于子進(jìn)程退出,init進(jìn)程作為新的父進(jìn)程,但是對于系統(tǒng)無害,init進(jìn)程會(huì)回收這些系統(tǒng)資源;
?僵尸進(jìn)程:子進(jìn)程退出,父進(jìn)程沒有獲得子進(jìn)程的狀態(tài)信息,可以通過調(diào)用wait(status)或者調(diào)用waitpid函數(shù)來實(shí)現(xiàn);
waitpid.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <wait.h>
int main(){
printf("PID:%d,PPID:%d\n",getpid(),getppid());
pid_t pid = fork();
if(pid == 0){// child
sleep(2);
printf("this is child\n");
printf("res:%d,PID:%d,PPID:%d\n",pid,getpid(),getppid());
//exit(0);
}else{
printf("this is father\n");
printf("res:%d,PID:%d,PPID:%d\n",pid,getpid(),getppid());
printf("pid:%d exit\n",waitpid(pid,NULL,0));
}
}
zomble.c:
#include <stdio.h>
#include <unistd.h>
#include <wait.h>
void handle(int sig){
//wait(NULL);
printf("this is child exit %d",sig);
}
int main(){
signal(SIGCHLD,handle);
printf("PID:%d,PPID:%d\n",getpid(),getppid());
pid_t pid = fork();
if(pid == 0){// child
printf("this is child\n");
printf("res:%d,PID:%d,PPID:%d\n",pid,getpid(),getppid());
}else{
printf("this is father\n");
printf("res:%d,PID:%d,PPID:%d\n",pid,getpid(),getppid());
for(;;);
}
}
一個(gè)模擬簡單shell的程序:
shell.c
//這個(gè)程序是一個(gè)簡單的shell程序,可以用于執(zhí)行shell命令中不帶有參數(shù)選項(xiàng)的命令;
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
void handle(int sig){
wait(NULL);
}
int main(){
printf("welcome to sim shell\n");
//
signal(SIGCHLD,handle);
for(;;){
printf(">");
fflush(stdout);
char buf[BUFSIZ];
bzero(buf,BUFSIZ);
fgets(buf,BUFSIZ,stdin);
size_t n = strlen(buf);
if(buf[n-1] == '\n'){
buf[n-1] = '\0';
}
if(strcmp(buf,"quit")==0)
break;
//system(buf);
char* argv[128];
bzero(argv,128);
char* p = buf;
int index = 0;
char* temp = NULL;
do{
temp =strtok(p," ");
p = NULL;
argv[index++] = temp;
}while(temp != NULL);
if(fork() == 0){
execvp(argv[0],argv);
}
//sleep(-1);
pause();
}
printf("sim shell exit\n");
return 0;
}