Linux環境程式設計程式間通訊機制理解

Charzueus發表於2020-08-29

一、Linux系統呼叫主要函式

二、建立程式

  1、建立子程式系統呼叫fork()

  2、驗證fork()建立子程式效果

  3、系統呼叫fork()與掛起系統呼叫wait()

三、模擬程式管道通訊

四、pipe()下生產者與消費者問題

總結

一、Linux系統呼叫主要函式

首先,認識一下Linux下系統呼叫的主要函式,為後面程式與通訊等做好鋪墊。

以下是 Linux 系統呼叫的一個列表,包含了大部分常用系統呼叫和由系統呼叫派生出的函式。

fork 建立一個新程式
clone 按指定條件建立子程式
execve 執行可執行檔案
exit 中止程式
_exit 立即中止當前程式
sleep(n) 睡眠(等待/阻塞),n 為秒的單位
getpid   獲取程式標識號
getppid 獲取父程式標識號
pause  掛起程式,等待訊號
wait(引數)  等待子程式終止
waitpid 等待指定子程式終止
kill 向程式或程式組發訊號
pipe 建立管道

二、建立程式

接下來這部分相當於程式設計,通過系統呼叫建立程式,然後根據執行順序進行判斷,理解主程式和子程式的關係。

1、建立子程式系統呼叫fork()

#include <unistd.h>
#include <sys/types.h>
#include<stdio.h>
int main ()
{
    pid_t pid; /* pid_t 是 short 型別 */
    pid=fork();
    if (pid < 0)
        printf("error in fork!");
    else if (pid == 0)
        printf("i am the child process, my process id is %d\n",getpid());
    else
        printf("i am the parent process, my process id is %d\n",getpid());
    return 0;
}

這個程式很好理解:

fork()返回值0,進入子程式,返回值1,進入父程式,-1則是建立失敗。

主要理解父子程式執行順序是否與程式結構(判斷語句前後)有關,所以再次進行以下測試,修改了判斷語句順序:

#include <unistd.h>
#include <sys/types.h>
#include<stdio.h>
int main ()
{
    pid_t pid; /* pid_t 是 short 型別 */
    pid=fork();
    if (pid < 0)
        printf("error in fork!");
    else if (pid > 0)
        printf("i am the parent process, my process id is %d\n",getpid());
    else
        printf("i am the child process, my process id is %d\n",getpid());
    return 0;
}

根據實驗結果如圖,對比之後發現執行結果相同,說明父子程式執行順序與判斷語句次序無關,始終是父程式優先執行。

2、驗證fork()建立子程式效果

首先,我們再次對以上程式進行修改,第一次,測試列印語句放於fork()之前,觀察結果:

#include <unistd.h>
#include <sys/types.h>
#include<stdio.h>
int main ()
{
    pid_t pid; /* pid_t 是 short 型別 */
    printf("see the print times\n");
    pid=fork();
    if (pid < 0)
        printf("error in fork!");
    else if (pid > 0)
        printf("i am the parent process, my process id is %d\n",getpid());
    else
        printf("i am the child process, my process id is %d\n",getpid());
    return 0;
}

第二次,將列印測試放在fork()之後,結果:

由此可見得,在fork()建立子程式之後,有兩個程式進行執行操作,所以執行了兩次測試列印。

3、系統呼叫fork()與掛起系統呼叫wait()

實現程式碼如下:

#include <unistd.h>
#include <sys/types.h>
#include<sys/wait.h>
#include<stdio.h>
#include<stdlib.h>
int i, st;
pid_t pid1, pid2;
int sub_proc1(int id);
int sub_proc2(int id);
int main()
{
    printf("\n\n\n");
    if ((pid1 = fork()) == 0)
    {
        printf("The first child process id is %d\n", getpid());
        sub_proc1(1);}
    else 
        printf("i am the parent process id=%d \n",getpid());
    if ((pid2 = fork()) == 0)
    {
        printf("The second child process id is %d\n", getpid());
        sub_proc2(2);}
    else 
        printf("i am the parent process id=%d \n",getpid());
    i = wait(&st);
    i = wait(&st);
    printf("\n all finished!!!!!!!!!!!!!!!!!!\n");
    exit(0);
    return 0;
}
int sub_proc1(int id)
{
    printf("sub_proc%d is running !\n",id);
    printf("sub_proc%d finish \n",id);
    exit(id);
}
int sub_proc2(int id)
{
    printf("sub_proc%d is running !\n",id);
    printf("sub_proc%d finish \n",id);
    exit(id);
}

解析:初始化建立了兩個主程式,系統呼叫wait()更直觀看出,程式先執行第一個主程式,再執行其子程式;然後執行第二個主程式,再執行其子程式。按照這個執行順序,感知父子程式直接的關係,以及兩個程式之間的執行順序。證明結果:

三、模擬程式管道通訊

我們知道,管道是程式之間的通訊方法之一, 接下來通過程式的建立、管道的設定,實現程式間通過管道進行通訊,然後認識在 Linux下程式間通過管道通訊的程式設計。

原理:pipe() 把 p[ ] 與兩個檔案描述符緊密聯絡起來:
p[0] 用於從管道讀
p[1]用於向管道寫

使用到系統呼叫函式pipe()  和 fork()

#include <stdio.h>
#include<stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#define MSGSIZE 16 
int pipe(int p[2]);
char *msg1="hello, world #1";
char *msg2="hello, world #2";
char *msg3="hello, world #3";
int main()
{ 
    char inbuf[MSGSIZE];
    int p[2], i, pid;
    
    if (pipe(p) < 0) /* 開啟管道 */
    { 
        perror("pipe error!"); /* 無法開啟一個管道錯*/
        exit(1); 
    }
    if ( (pid = fork()) < 0) 
    { 
        perror("fork");
        exit(2); 
    }
    if (pid > 0) /* 父程式 */
    { 
        close(p[0]); /* 關閉讀端(連線) */
        write(p[1], msg1, MSGSIZE);
        write(p[1], msg2, MSGSIZE);
        write(p[1], msg3, MSGSIZE);
        wait((int *)0);
    }
    if (pid == 0) /* 子程式 */
    { 
        close(p[1]); /* 關閉寫端 */
        for (i=0; i < 3; i++) 
        {
            read(p[0], inbuf, MSGSIZE);
            printf("%s\n", inbuf);
        }
    }
    return 0;
}

結果實現不同進行之間的通訊,通過不同管道進行讀寫操作:

 

程式管道通訊需要注意下面幾個問題:

(1)關閉了一個方向

(2)管道的大小(Size)
每個管道都有一個大小限制,通常大約為 5k (5120) 個位元組

(3)寫問題:

write() 將被掛起(阻塞),如果管道中無法存放正文(容量不夠),可能在寫的過程中發生阻塞;

如果向一個沒有讀連線的管道寫,將返回 -1, 且 errno 被置成 EPIPE。errno 是一個全域性變數。

(4)讀問題:

read() 將被阻塞,如果管道空,且有一個 寫連線開放,read() 將返回 0 。
如果為空,且沒有任何寫連線,就此, 讀程式可以知道通訊結束。

四、pipe()下生產者與消費者問題

經典同步互斥問題,現在初始化有一個生產者,兩個消費者,只有生產者有生產物資後,消費者才能使用,否則進入等待。

Linux環境程式設計程式間通訊機制理解
#include<sys/types.h>
#include<sys/file.h>
#include<string.h>
#include<sys/wait.h>
#include<unistd.h>
#include<stdlib.h>
#include <stdio.h>
char r_buf[4]; //讀緩衝
char w_buf[4]; //寫緩衝
int pipe_fd[2];
pid_t pid1, pid2;
int producer(int id);
int consumer(int id);
int i,pid,status; /* status 用來表示程式退出的狀態 */
int main()
{ 
    if(pipe(pipe_fd)<0)
    { 
        printf("pipe create error \n"); 
        exit(-1); 
    }
    else
    { 
        printf("pipe is created successfully!\n");
        if((pid1=fork())==0) 
            producer(1);
        if((pid2=fork())==0) 
            consumer(2);
    }
    close(pipe_fd[0]);//否則會有讀者或者寫者永遠等待
    close(pipe_fd[1]);
    for(i=0;i<2;i++)
        pid=wait(&status); /* 等待子程式完成(取返回狀態資訊),主程式後
    面未利用子程式的什麼結果,直接在子程式完成後退出 */
    exit(0);
}
int producer(int id)
{ 
    int i;
    printf("producer %d is running!\n",id);
    close(pipe_fd[0]);
    for(i=1;i<10;i++)
    { 
        sleep(3);
        strcpy(w_buf,"aaa\0");
        if(write(pipe_fd[1],w_buf,4)==-1)
            printf("write to pipe error\n"); 
    }
    close(pipe_fd[1]);
    printf("producer %d is over!\n",id);
    exit(id);
}
int consumer(int id)
{ 
    close(pipe_fd[1]);
    printf("consumer %d is running!\n",id);
    strcpy(w_buf,"bbb\0");
    while(1)
    { 
        sleep(1);
        strcpy(r_buf,"eee\0");
        if(read(pipe_fd[0],r_buf,4)==0) 
            break;
        printf("consumer %d get %s, while the w_buf is %s\n",id,r_buf,w_buf);
    }
    close(pipe_fd[0]);
    printf("consumer %d is over!\n", id);
    exit(id);
}
View Code

程式執行如下:

總結

學習之後的一篇總結,玩轉Linux程式設計,更加深刻地去體驗其中的原理,掌握基本的知識和操作。這篇記錄總結學習,可以認識到程式與程式的區別,還有程式間通訊機制,程式的執行順序和關係。Linux系統呼叫函式的認識和使用更進一步掌握相關Linux知識。

另外,程式間的通訊機制還有其他方法,程式訊號中斷通訊內容,互斥同步問題,銀行家演算法等期待後續的學習總結記錄,學習分享記錄每一步,希望也能夠幫助到有需要的朋友,大佬也多多指正!

我的CSDN部落格:https://blog.csdn.net/Charzous/article/details/108287075

我的部落格園:https://www.cnblogs.com/chenzhenhong/p/13580356.html


版權宣告:本文為博主原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處連結和本宣告。

本文連結:https://blog.csdn.net/Charzous/article/details/108287075

相關文章