進程概念
進程是程序在計算機上執行一次的過程,也就是一個執行中的程序,進程是一個獨立的邏輯控制流,獨占處理器,相當于工人和機器,進程具有私有的地址空間,獨占存儲器系統,相當于工廠;進程的本質是程序在地址空間中按照代碼邏輯控制流執行的過程,同時進程是資源分配的最小單位;
進程和程序的區別
進程是動態的,并且具有生命周期,并且一個進程只能對應于一個程序;相反,程序是動態的指令集合,一個程序可以對應于多個進程;
進程的狀態
就緒:Ready,當進程獲得出CPU以外的所有的必要的資源時,進程進入就緒狀態,也就是說當進程得到CPU就可以立即運行;
?運行:Runing,進程正在占據CPU資源;
?阻塞(Blocked):進程猶豫等待某個事件發生而無法執行時,進程被迫放棄CPU執行資源;
進程的創建
進程的查看
Windows可以使用tasklist 例如:tasklist / F1 "PID eq 進程PID";
在Linux底下可以使用ps命令進行查看,ps命令的相關選項:
?user:表示進程所屬的用戶;
?PID:表示進程ID;
?ppid:表示父進程ID;
?%CPU:表示CPU占有率;
?%mem:表示內存占有率;
?VSZ:表示進程虛擬大小;
?RSS:表示常駐內存,也就是內存中頁面的數量;
?tty:表示進程使用的終端;
?stat:表示進程的狀態;
?start:表示啟動進程的時間;
?TIME :表示進程消耗CPU的時間;
?command:表示打開進程的命令和參數;
?進程狀態標識符:
??D:表示不可中斷進程,Uninterruptible;
??R:表示正在運行,或者在隊列中的進程;
??S:處于休眠狀態;
??T:進程處于停止或者被追蹤狀態;
??Z:僵尸進程;
??W:表示進入內存交換,從內核2.6以后開始失效;
??X:表示死掉的進程;
??<:表示高優先級;
??n:表示低優先級;
??s:表示包含子進程;
??+:表示位于后臺的進程組;
?查看某個具體的進程:ps -p pid;ps -C在命令行進行查看;同樣的還可以使用pstree進行查看,但是可能需要安裝yum install psmisc -y;同樣的的還可以使用top命令動態的查看系統進程;
進程的殺死
windows:task kill /F /PID 或者task kill /F /IM 程序名
?linux:kill 進程標識PID
?與進程相關的文件一般在/proc/底下,通常是進程id作為目錄名的文件;
進程的操作
進程的標識通常使用pid來完成,獲取當前進程的id使用函數pid_t getpid();獲取當前進程父進程的ID使用函數pid_t getppid();
#include <stdio.h>
#include <unistd.h>
int main(){
printf("PID:%dPPID:%d\n",getpid(),getppid());//分別用于獲得當前進程ID和父進程ID;
for(;;);
}
關于進程的創建
fork()函數
函數失敗時返回值是-1,返回值為0表示子進程邏輯控制流,如果返回值為其他,表示父進程控制流;
?函數的特點:
??1、調用一次,返回兩次;
??2、相同但是具有獨立的地址空間;
??3、可以并發執行;
??4、這個函數的本質是函數堆棧數據,text文本的復制;
函數的本質是父進程函數相關數據的復制,然后分叉出另一個函數;
#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(;;);
}
子進程是父進程的副本,它將獲得父進程數據空間、堆、棧等資源的副本。注意,子進程持有的是上述存儲空間的“副本”,這意味著父子進程間不共享這些存儲空間。
fork02.c:這個函數是用來構建子進程,用來輸出一些值;
#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:這個函數父進程在增加變量的值,子進程在減小函數的值,用來模擬兩個進程的運行情況;
#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函數簇
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;
}
函數命名的規律組總結
v:表示第二個參數是數組;
l:第二個參數之后是變參;
p:第一個參數是文件名;
e:最后一個參數是環境變量;
常見的字符串數組函數
execv(),execvp(),execve()
可變參數函數
execle(),execlp(),execl();
exec.c函數:
?exec函數簇函數的返回值,-1表示失敗,執行成功時,是不返回任何值的,這些函數具有以下特點:一次調用,失敗返回;改朝換代,取而代之;但是函數的PID是不會變化的,變化的是程序地址空間的內容,exec函數簇函數的本質是覆蓋原有函數,執行新的函數;
system(shell字符串)
這個函數是系統函數int system(shell 字符串或者命令);函數的返回值為-1時,表示失敗;函數的返回值是127時,表示無法啟動shell;其它返回值用于表示函數的退出碼;系統調用函數一次調用,一次返回;這個函數本質上是在執行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());
}
進程的結束
對于return和exit來說,函數的執行屬于壽終正寢,并且自己能夠了結后事。_exit()函數屬于自殺的情況,但是沒有料理后事。abort()函數屬于意外的死亡,也沒有料理后事;信號終止屬于他殺;料理后事表示的意思是程序結束以后,相關堆棧資源的回收工作;
?main()函數return 0 是語言級別的退出,使用這種情況的退出,函數仍然需要調用exit(0)函數,exit(0)是函數級別的退出;
?exit(0),表示釋放所有的靜態的全局對象,緩存,關掉所有的IO通道,然后終止程序;abort()會直接終止程序,但是不會釋放任何資源;
?exit(0)函數在調用_exit()系統調用之前要檢查文件的打開情況,吧文件緩沖區中的內容寫回文件,清理IO緩沖,exit(0)是標準的C函數;
?_exit()不做清理IO緩沖函數,_exit()是Linux系統調用;
?main函數退出:
main.c
#include <stdio.h>
#include <unistd.h>
int main(){
printf("PID:%d,PPID:%d\n",getpid(),getppid());
return 100;
}
exit函數退出
exit函數一般用在main函數以外的函數退出
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(){
printf("PID:%d,PPID:%d\n",getpid(),getppid());
exit(EXIT_FAILURE);
abort();
}
_exit函數一般用來結束子進程
這個函數暫時沒有說明
abort()函數一般用來異常退出
#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");
}
進程的停止
int sleep(unsigned int secs)函數 ,sece用于表示休眠的秒數,如果指定為-1表示永久休眠,函數的返回值是未休眠的秒數;如果沒有信號中斷,休眠指定的秒數返回0,否則馬上返回未休眠的秒數;
#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()函數返回值一定是-1,如果函數沒有處理信號,直接中斷,執行默認的信號處理,程序后續代碼不再執行。如果程序存在信號處理,執行處理信號后,執行后續代碼。
#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);函數等價于pid_t waitpid(-1,status,0);這個函數的參數包括:pid:小于-1時,表示的是等待進程組為pid的所有進程,-1:表示等待任何子進程;0:表示等待同組的進程;大于0:表示等待進程為pid的子進程;
?status:參數的含義:
??正常結束,WIFEXITED(status),參數為0時,表示的是正常結束任何子進程,0表示的是非正常結束子進程;WEXITSTATUS()表示取得子進程exit()返回的結束代碼,一般會先用WIFEXITED來判斷是否正常結束才是用這個宏定義;
??異常結束:WIFSIGNALED(ststus)非0值表示異常結束子進程,0表示非異常結束子進程;WTERMSIG(status),取得子進程因信號而終止的信號代碼,一般先會用WIFSIGNALED來判斷后才能使用宏;
??暫停:WIFSTOPPED(status),非0值表示暫停結束子進程,0表示暫停結束子進程;WSTOPSIG(status),取得引發子進程暫停的信號代碼,一般先用
WIFSTOPPED來判斷后才能使用此宏;
?option,選項WNOHANG,若子進程沒有結束,返回0,不予等待。若子進程結束,返回該子進程的ID;WUNTRACED,若子進程進入暫停狀態,則馬上返回,但子進程的結束狀態不予理會。
?返回值,-1表示失敗;其他值表示等待的PID;
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(;;);
}
孤兒進程與僵尸進程
孤兒進程:父進程先于子進程退出,init進程作為新的父進程,但是對于系統無害,init進程會回收這些系統資源;
?僵尸進程:子進程退出,父進程沒有獲得子進程的狀態信息,可以通過調用wait(status)或者調用waitpid函數來實現;
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(;;);
}
}
一個模擬簡單shell的程序:
shell.c
//這個程序是一個簡單的shell程序,可以用于執行shell命令中不帶有參數選項的命令;
#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;
}