C語言並發程序設計


進程的概念

  程序:

    存放在磁盤上的指令和數據的有序集合(文件)

    靜態的

  進程:

    執行一個程序所分配的資源的總稱

    進程是程序的一次執行過程

    動態的,包括創建、調度、執行和消亡

進程包含的內容

  進程包含:正文段(代碼段)、用戶數據段、系統數據段

  程序包含:正文段(代碼段)、用戶數據段

  系統數據包含:進程控制塊、CPU寄存器值、堆棧

    進程控制塊(PCB)包含:

      進程標識PID

      進程用戶

      進程狀態、優先級

      文件描述符表

    CPU寄存器值:

      PC:program counter, 記錄着下一條執行指令的地址

    堆棧:所有的局部變量都是在棧中存在的

進程的類型

  交互進程:在shell下啟動。可以在前台運行,也可以在后台運行

  批處理進程:和在終端無關,被提交到一個作業隊列中以便順序執行

  守護進程:和終端無關,一直在后台運行 

進程的狀態

  運行態:進程正在運行,或者准備運行

  等待態:進行在等待一個事件的發生或某種系統資源,又分為可中斷和不可中斷

  停止態:進程被中止,收到信號后可繼續運行

  死亡態:已終止的進程,但pcb沒有沒有被釋放

查看進程信息

  ps:查看系統進程快照

    ps -ef : 查看系統中所有的進程信息

    ps aux: 比ps -ef 多一個當前的進程狀態信息,進程優先級

  top:查看進程動態信息,每個3秒刷新一次

  /proc:查看進程詳細信息,在proc目錄下以進程PID為名的目錄,其中status文件保存着進程的詳細信息,fd目錄保存這使用的文件

改變進程優先級

  nice: 按用戶指定的優先級運行進程,nice的區間[-20, 19],默認值為0,值越小優先級越高,普通用戶能夠設置的最小值為0。

    nice -n 2 ./a.out

  renice:改變正在運行進程的優先級,普通用戶只能增加這個值。

    renice -n 2 PID

前后台進程切換

  jobs:查看后台進程

    xdl@xdl-gj:~/C語言/thread$ ./a.out &

    [1] 22441            //1表示作業號
    xdl@xdl-gj:~/C語言/thread$ ./a.out &
    [2] 22442

    xdl@xdl-gj:~/C語言/thread$ jobs

    [1]- 運行中 ./a.out &
    [2]+ 運行中 ./a.out &

  bg:降掛起的進程在后台運行

  fg:把后台運行的進程放到前台運行

    xdl@xdl-gj:~/C語言/thread$ fg 1

    ./a.out

    ^Z      // ctrl +z 使進程掛起
    [1]+ 已停止 ./a.out
    xdl@xdl-gj:~/C語言/thread$ bg 1
    [1]+ ./a.out &

創建進程

  fork函數用於創建一個進程

    #include <unistd.h>

    pid_t fork(void);

    創建新的進程,失敗時返回-1

    成功時父進程返回子進程的進程號,子進程返回0

    通過fork的返回值區分父進程和子進程

#include <stdio.h>
#include <unistd.h>


int main(int argc, char *argv[])
{
    pid_t pid;
    pid = fork();

    printf("pid=%d\n", pid);

    if (pid < 0)
    {
        perror("fork");
        return -1;
    }    
    else if (pid == 0)
    {
        printf("child process: my pid is %d\n", getpid());
    }
    else
    {
        printf("parent process: my pid is %d\n", getpid());
    }
    return 0;
}
/*
pid=6018
parent process: my pid is 6017
pid=0
child process: my pid is 6018

*/

父子進程

  子進程繼承父進程的內容

  父子進程有獨立的地址空間,互不影響

  若父進程先結束

    子進程稱為孤兒進程,被init進程收養(linux啟動內核之后自動創建的用戶態進程PID為1)

    子進程變成后台進程

  若子進程先結束

    父進程如果沒有及時回收,子進程變成僵屍進程

  子進程從何處開始運行?

    從fork下一句指令開始,並沒有執行fork語句

  父子進程誰先執行?

    不一定,一般父進程創建子進程后時間片沒有用完則繼續執行

  父進程能否多次調用fork?子進程呢?

    可以

結束進程

  exit/_exit

    #include <stdlib.h>

    #include <unistd.h>

    void exit(int status);

    void _exit(int status);

    結束當前進程並將status(低八位)返回

    exit結束結束進程時會刷新(流)緩沖區

#include <stdio.h> 
#include <stdlib.h> // for exit
#include <unistd.h>  //for _exit

int main(int argc, char *argv[])
{
    printf("this process will exit");
    //exit(0);//會打印上一句
    _exit(0);//不會打印
    printf("never be displayed");
    return 0;
}

exec函數族

  進程調用exec函數執行某個程序

  進程當前內容被指定的程序替換

  實現讓父子進程執行不同的程序

    父進程創建子進程

    子進程調用exec函數族

    父進程不受影響

  execl/execlp

    #include <unistd.h>

    int execl(const char *path, const char *arg, ...);

    int execlp(const char *file, const char *arg, ...);

    成功執行指定的程序;失敗時返回EOF

    path 執行的程序的名稱,包含路徑

    arg... 傳遞給執行的程序的參數列表

    file執行的程序的名稱,在PATH中查找

    執行ls命令,顯示/etc目錄下所有文件的詳細信息

#include <stdio.h>
#include <unistd.h>


int main(int argc, char *argv[])
{
    pid_t pid;
    pid = fork();

    printf("pid=%d\n", pid);

    if (pid < 0)
    {
        perror("fork");
        return -1;
    }    
    else if (pid == 0)
    {
        printf("child process: my pid is %d\n", getpid());
        if (execl("/bin/ls", "ls", "-a", "-l", "/etc", NULL) < 0)
        {
            perror("execl");
            return -1;
        }
    }
    else
    {
        printf("parent process: my pid is %d\n", getpid());
        if (execlp("ls", "ls", "-al", "/etc", NULL) < 0 )
        {
            perror("execlp");
            return -1;
        }
    }
    return 0;
}
/*
pid=6018
parent process: my pid is 6017
pid=0
child process: my pid is 6018

*/

  execv/execvp

    #include <unistd.h>

    int execv(const char *path, char *const argv[]);

    int execvp(const char *file, char *const argv[]);

    成功時執行指定的程序;失敗時返回EOF

    arg...封裝成指針數組的形式

    char *arg[] = {"ls", "-a", "-l", "/etc", NULL};

    if(execv("/bin/ls", arg) < 0){

      perror("execv");

    }

    if(execvp("ls", arg) < 0){

      perror("execvp");

    }

system

  #include <stflib.h>

  int system(const char *command);

  成功時返回命令command的返回值;失敗時返回EOF

  當前進程等待command執行結束后才繼續執行

進程回收

  子進程結束時由父進程回收

  孤兒進程由init進程回收

  若沒有及時回收會出現僵屍進程

  wait函數

    #include <unistd.h>

    pid_t wait(int *status);

    成功時返回回收的子進程的進程號;失敗時返回EOF

    若子進程沒有結束,父進程一直阻塞

    若有多個子進程,哪個先結束就先回收

    status指定保存子進程返回值和結束方式的地址

    status為NULL表示直接釋放子進程PCB,不接收返回值

  wait pid函數

    #include <unistd.h>

    pid_t waitpid(pid_t pid, int *status, int option);

    成功時返回回收的子進程的pid或0(表示子進程還沒有結束);失敗時返回EOF

    pid可用於指定回收哪個子進程或任意子進程(-1表示任意子進程)

    status指定用於保存子進程返回值和結束方式

    option指定回收方式,0(阻塞方式)或WNOHANG(非阻塞)

進程返回值和結束方式

  子進程通過exit/_exit/return 返回某個值(0-255)

  父進程調用wait(&status)回收

  WIFEXITED(status) : 判斷子進程是否正常結束

  WEXITSTATUS(status): 獲取子進程返回值

  WIFSIGALED(status): 判斷子進程是否被信號結束

  WTERMSIG(status): 獲取結束子進程的信號類型

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>

int main(int argc, char *argv[])
{
    int status;
    pid_t pid;

    if ((pid = fork()) < 0)
    {
        perror("fork");
        exit(-1);
    }
    else if (pid == 0)
    {
        sleep(1);
        exit(2);
    }
    else
    {
        wait(&status);
        printf("%d\n", status);
    }
    return 0;
}
/*
200
521
0010 0000 0000
低7為(0~7)表示進程結束的類型,0表示正常結束,非零表示信號結束
高8為(8~15)表示子進程正常結束時的返回值
*/

守護進程

  守護進程(Daemon)是Linux三種進程類型之一

  通常在系統啟動時運行,系統關閉時結束

  Linux系統中大量使用,很多服務程序以守護進程形式運行

守護進程的特點

  始終在后台運行

  獨立於任何終端

  中期性的執行某種任務或等待處理特定事件

會話、控制終端

  Linux以會話(session)、進程組的方式管理進程

  每一個進程屬於一個進程組,父子進程屬於同一個進程組

  會話是一個或多個進程組的集合。通常用戶打開一個終端時,系統會創建一個會話。所有通過該終端運行的進程都屬於這個會話

  終端關閉時,所有相關進程會被結束

創建守護進程

  1、創建子進程,父進程退出

    if (fork() > 0){

      exit(0);

    }

    子進程變成孤兒進程,被init進程收養

    子進程在后台運行

   2、子進程創建新會話

    if (setsid() < 0 ){

      exit(-1);

    }

    子進程成為新的會話組長

    子進程脫離原先的終端

  3、更改當前工作目錄

    chdir("/");

    chdir("/tmp");

    守護進程一直在后台運行,其工作目錄不能被卸載

    重新設定當前工作目錄cwd

  4、重設文件權限掩碼

    if (umask(0) < 0){

      exit(-1);

    }

    文件權限掩碼設置為0

    值影響當前進程

  5、關閉打開的文件描述符

    int i;

    for (i = 0; i < getdtablesize(); i++){

      close(i);

    }

    關閉所有從父進程繼承的打開文件

    已脫離終端,stdio,stdout,stderr無法在使用

創建守護進程,每隔1秒將系統時間寫入文件time.log

#include <stdio.h>
#include <unistd.h>
#include <time.h>
#include <stdlib.h>
#include <sys/stat.h> // for umask()

int main(int argc, char *argv[])
{
    pid_t pid;
    FILE *fp;
    time_t t;
    int i;

    if ((pid = fork()) < 0)
    {
        perror("fork");
        exit(-1);
    }
    else if (pid > 0)
    {
        exit(0);
    }
    setsid();
    umask(0);
    chdir("/tmp");
    for (i = 0; i < getdtablesize(); i++)
    {
        close(i);
    }
    if ((fp = fopen("time.log", "a")) == NULL)
    {
        perror("fopen");
        exit(-1);
    }
    while (1)
    {
        time(&t);
        fprintf(fp, "%s", ctime(&t));
        fflush(fp);
        sleep(1);
    }
    return 0;
}

 

    

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM