Linux下進程的控制

linux下進程的控制

進程的創建

  1. 首先,我們知道進程的創建需要調用fork()函數。
  2. fork()一次調用兩次返回,子進程返回0,父進程返回子進程的pid。
  3. 同樣也可以調用vfork來創建子進程,但此時父子進程共享地址空間,因此我們一般不建議使用。
#include<stdio.h>
#include<unistd.h>
int main()
{

        pid_t pid;
        pid=fork();
        if(pid<0)
        {
                perror("fork");
                return -1;
        }
        if(pid==0)
        {
                //chilid
                while(1)
                {
                        printf("I am Child\n");
                        sleep(1);
                }

        }
        while(1)
        {
                printf("I am Father\n");
                sleep(1);
        }
        return 0;
}

這樣就完成了進程的創建。

進程的等待

  1. 我們知道,一個進程的退出,它的父進程必須知道它的退出信息,因此會保持殭屍狀態,而如果殭屍進程沒有人去回收的話,就會造成內存的泄露。

  2. 父進程需要通過等待的方式,來獲取子進程退出的信息,並且回收掉子進程的資源。

  3. 函數1:pid_t wait(int*status),成功返回等待進程id,失敗返回-1,並且帶出等待進程的退出信息。不關心退出信息的話就可以設置成NULL;

  4. 函數2:pid_t waitpid(pid_t pid,int *status,int options);正常返回收集到的子進程的ID,如何設置了WNOHANG,調用發現已經沒有已退出的子進程時候,返回0,pid=-1時,表示回收任一個子進程,pid>0表示等待其進程的ID和pid相等的子進程。


  1 #include<stdio.h>
  2 #include<unistd.h>                                                                                                                                                                                           
  3 int main()
  4 {
  5     int status=0;
  6     int count=5;
  7     pid_t pid=fork();
  8     if(pid<0)
  9     {
 10         perror("fork");
 11             return -1;
 12     }
 13     else if(pid==0)
 14     {
 15         //child
 16         while(count--)
 17         {
 18             printf("Child process running\n");
 19  
 20         }
 21         exit(1);
 22  
 23  
 24     }
 25     else
 26     {
 27         //father
 28         wait(&status);
 29         printf("status is %d\n",status);
 30         printf("Father process running\n");
 31     }
 32  
 33     return 0;
 34 }

其中不管是wait還是waitpid,都有一個status標誌位標誌着子進程的退出消息,因此很有必要研究一下里面到底存放的是什麼。
這裏寫圖是三數片描是述

  • 首先,這個status不能簡單的當,作一個整形來看待,更應該把它比喻成一個位圖。
  • 而我們只需要研究它的低16位就足夠了
  • 其中,當一個進程代碼完成,正常結束掉的時候,它所告訴父進程的退出狀態存放在高8位。第八位全0
  • 而它如果是由信號殺死的話,那麼它的低7位就存放的是被那個終止信號幹掉的,第8爲是core dump 標誌位,表示它是否生成了core dump文件。我們稱之爲核心轉儲

進程終止

  1. 一個進程的結束有三種情況:1.代碼運行完,結果正確,2.代碼運行完,結果不正確,3.代碼
    運行異常終止
  2. 而正常退出又分爲man函數return返回和調用exit或者_exit來。
  3. 異常終止比如接收到ctrl+c 這樣的信號終止。
    調用exit
  1 #include<stdio.h>
  2 #include<stdlib.h>                                                                                                                                                                                           
  3 #include<unistd.h>
  4 int main()
  5 {
  6  
  7     printf("hello world\n");
  8     exit(1);
  9 }

這裏寫圖片描述
調用_exit

  1 #include<stdio.h>
  2 #include<stdlib.h>
  3 #include<unistd.h>
  4 int main()
  5 {     
  6       
  7     printf("hello world");
  8       _exit(1);
  9 }  

這裏寫圖片描述

  • 爲什麼同樣是正常退出,但是結果卻不一樣呢,這裏我們要講一下exit和_exit的區別。
  • 首先,exit是c庫函數,而_exit是系統調用函數,exit封裝了_exit。
  • 當我們調用exit 的時候,首先執行用戶自定義的清理函數,再刷新緩衝區,關閉流等操作,最後纔回去調用_exit。
  • 其中,還有一種退出方式就是return ,其實return還是將後面的參數傳遞給了exit。

進程替換

  • 我們創建子進程的最主要原因,就是想讓子進程來代替父進程做一部分工作。而fork出來的子進程和父進程擁有相同的代碼和數據,因此就需要對子進程的代碼和數據進行替換,從而完成別的任務,這就是進程替換的基本思想。
  • 下面我們來看看6個進程替換的函數:
  • 這裏寫圖片描述

如何更好記憶

  1. 首先,execve纔是真正的系統調用接口,這些函數都是在它的基礎上進行了封裝.
  2. l表示參數使用列表,即可變參數參數列表注意要以NULL結尾,而v表示參數採用數組,需要用戶自己定義。
  3. 帶p表示自動搜索當前環境變量PATH,不帶P就需要手動的來添加路徑。
  4. 帶e表示需要自己維護環境變量。
    自己實現一個shall
    綜合了之前所學的知識,我們大概能明白shall是如何運行命令的,當我們用戶輸入一條命令時,shall獲取這條命令,並且對它進行解析,創建一個子進程,然後用當前命令替換子進程,父進程等待子進程的退出。
  1 #include<stdio.h>
  2 #include<sys/wait.h>
  3 #include<stdlib.h>
  4 #include<string.h>
  5 #include<unistd.h>
  6 char *argv[8];  
  7 int argc=0;     
  8 void do_pause(char*buf)
  9 {              
 10     int i;  
 11     int status=0;
 12     for(argc=i=0;buf[i];i++)
 13     {    
 14         if(!isspace(buf[i])&&status==0)
 15         {   
 16             argv[argc++]=buf+i;
 17             status=1;
 18              
 19         }    
 20         else if(isspace(buf[i]))
 21         {   
 22             status=0;
 23             buf[i]=0;
 24         }   
 25     }       
 26     argv[argc]=NULL;
 27 }   
 28 void do_execute(void)
 29 {   
 30     pid_t pid=fork();
 31     switch(pid)
 32     {       
 33         case -1:
 34             perror("fork");
 35                 exit(EXIT_FAILURE);
 36             break;
 37         case 0:
 38             execvp(argv[0],argv);
 39             perror("execvp");
 40             exit(EXIT_FAILURE);
 41         default:
 42             {  
 43                 int st;
 44                 while(wait(&st)!=pid)
 45                     ;
 46             }
 47     }       
 48 }   
 49 int main()  
 50 {   
 51     char buf[1024]={};
 52     while(1)
 53     {       
 54         printf("myshell>>");
 55         scanf("%[^\n]%*c",buf);
 56         do_pause(buf);
 57         do_execute();
 58     
 59     }
 60 }

這裏寫圖片描述

popen/system

  • FILE *popen(const char *command, const char *type);
  • 這個函數創建一個管道,然後通過fork或者inovke一個子進程,然後在子進程裏面執行command的命令,然後把結果返回到標準I/O當中,由於管道只能單向同通信的原因,所以command只能從管道中讀取r或者把結果w到管道里面。在調用完成後我們要調用fclose來回收創建的子進程,不然就可能產生殭屍進程。
    int system(const char *command);
  • 這個函數創建一個進程,然後進程替換去執行我們指定的command命令。然後shell阻塞直到該命令執行完成,再返回到調用函數,
  • 函數 process_create(pid_t* pid, void* func, void* arg), func回調函數就是子進程執行的入口函數, arg是傳遞給func回調函數的參數
    int process_create(int(*func)(),const char* file,char* argv[])
    {
    int ret = 0;
    pid_t pid = fork();
    if(pid == 0)
    {
    //child
    ret = func(file,argv);
    if(ret == -1)
    {
    printf("調用execvp失敗\n");
    perror("func");
    _exit(-1);
    }
    }
    else
    {
    int st;
    pid_t ret = wait(&st);
    if(ret == -1)
    {
    perror("wait");
    exit(-1);
    }
    }
    return 0;
    }
    int main()
    {
    char* argv1[] = {"ls"};
    char* argv2[] = {"ls","-al","/etc/passwd",0};
    process_create(execvp,*argv1,argv2);
    return 0;
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章