Linux-C-day1-進程基礎

進程概念

進程是程序在計算機上執行一次的過程,也就是一個執行中的程序,進程是一個獨立的邏輯控制流,獨占處理器,相當于工人和機器,進程具有私有的地址空間,獨占存儲器系統,相當于工廠;進程的本質是程序在地址空間中按照代碼邏輯控制流執行的過程,同時進程是資源分配的最小單位;

進程和程序的區別

進程是動態的,并且具有生命周期,并且一個進程只能對應于一個程序;相反,程序是動態的指令集合,一個程序可以對應于多個進程;

進程的狀態

就緒: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;
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • Linux 進程管理與程序開發 進程是Linux事務管理的基本單元,所有的進程均擁有自己獨立的處理環境和系統資源,...
    JamesPeng閱讀 2,512評論 1 14
  • 又來到了一個老生常談的問題,應用層軟件開發的程序員要不要了解和深入學習操作系統呢? 今天就這個問題開始,來談談操...
    tangsl閱讀 4,173評論 0 23
  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,993評論 19 139
  • 我一直覺得自己是個內向而敏感的人。 被別人傷了,想回擊,可每次,反復思慮好幾遍,最后還是放棄。人善被人欺,我總是這...
    唐菖蒲小朋友閱讀 867評論 0 0
  • 如果依照蘇格拉底和孔子思維來說 智者最好的行為就是(述而不作) 只是——面對這個世界混亂的世界 總得有人發一下聲才...
    南岳衡山隱士閱讀 558評論 0 0