首頁 > 軟體

Linux進程間通訊-管道深入理解

2020-06-16 16:53:35

Linux進程通訊系列文章將詳細介紹各種通訊方式的機制和區別

1.進程間通訊

每個進程各自有不同的使用者地址空間,任何一個進程的全域性變數在另一個進程中都看不到,所以進程之間要交換資料必須通過核心,在核心中開闢一塊緩衝區,進程A把資料從使用者空間拷到核心緩衝區,進程B再從核心緩衝區把資料讀走,核心提供的這種機制稱為進程間通訊。

2、進程間通訊方式

一、進程間通訊-管道 https://www.linuxidc.com/Linux/2018-04/151680.htm

二、進程間通訊-命名管道 https://www.linuxidc.com/Linux/2018-04/151681.htm

三、進程間通訊-訊息佇列 https://www.linuxidc.com/Linux/2018-04/151682.htm

四、進程間通訊-共用記憶體 https://www.linuxidc.com/Linux/2018-04/151683.htm

3、進程間通訊-管道(pipe)

 3.1 管道是如何通訊的  

(1)父進程建立管道,得到兩個?件描述符指向管道的兩端

(2)父進程fork出子進程,?進程也有兩個?件描述符指向同?管道。

(3)父進程關閉fd[0],子進程關閉fd[1],即?進程關閉管道讀端,?進程關閉管道寫端(因為管道只支援單向通訊)。?進程可以往管道?寫,?進程可以從管道?讀,管道是?環形佇列實現的,資料從寫端流?從讀端流出,這樣就實現了進程間通訊。

3.2 利用管道實現通訊

#include "stdio.h"
#include "unistd.h"
#include "string.h"

int main(int argc, char* argv[])
{
    int fd[2];
    int ret = pipe(fd);
    if (ret == -1)
    {
        perror("pipe errorn");
        return 1;
    }

    pid_t pid = fork();
    if (pid == 0)  // child
    {
        close(fd[0]);
        int i = 0;
        char* msg = "i am childn";
        while (i < 5)
        {
            write(fd[1],msg,strlen(msg));
            sleep(2);
            i++;
        }

    }
    else if(pid > 0)
    {
        close(fd[1]);
        char buf[256] = {0};
        int j = 0;
        while (j < 5)
        {
            int n = read(fd[0],buf,256);
            if (n>0) 
            { 
                buf[n] = ''; 
            } 
            printf("%s",buf);
            j++; 
        }
    }
    else
    {
        perror("fork errorn");
        return 1;
    }
    return 0;
}

執行結果:

 3.3 管道讀取資料的四種的情況

(1)讀端不讀,寫端一直寫

(2)寫端不寫,讀端一直讀

(3)讀端一直讀,寫端關閉

(4)寫端一直寫,讀端關閉

#include "stdio.h"
#include "unistd.h"
#include "string.h"
#include "sys/wait.h"
#include "sys/types.h"

int main(int argc, char* argv[])
{
    int fd[2];
    int status = 0;
    int ret = pipe(fd);
    if (ret == -1)
    {
        perror("pipe errorn");
        return 1;
    }

    pid_t pid = fork();
    if (pid == 0)  // child
    {
        close(fd[0]);
        int i = 0;
        char* msg = "i am childn";
        while (i < 10)
        {
            write(fd[1],msg,strlen(msg));
            sleep(1);
            i++;
        }

    }
    else if(pid > 0)
    {
        close(fd[1]);
        char buf[256] = {0};
        int j = 0;
        while (j < 5)
        {
            int n = read(fd[0],buf,256);
            if (n>0) 
            { 
                buf[n] = ''; 
            } 
            printf("%s",buf);
            j++; 
        }
        close(fd[0]);
        ret = waitpid(pid,&status,0);
        printf("exit single(%d),exit(%d)n", status & 0xff, (status >> 8) & 0xff); 
    }
    else
    {
        perror("fork errorn");
        return 1;
    }
    return 0;
}

執行結果:

使用kill -l 檢視13號信號,可以知道13號信號代表SIGPIPE。

4、管道的特點

(1)管道只允許具有血緣關係的進程間通訊,如父子進程間的通訊。

(2)管道只允許單向通訊。

(3)管道內部保證同步機制,從而保證存取資料的一致性。

5、管道的容量

測試管道容量大小只需要將寫端一直寫,讀端不讀且不關閉fd[0],即可。

#include "stdio.h"
#include "unistd.h"
#include "string.h"
#include "sys/wait.h"
#include "sys/types.h"

int main(int argc, char* argv[])
{
    int fd[2];
    int status = 0;
    int ret = pipe(fd);
    if (ret == -1)
    {
        perror("pipe errorn");
        return 1;
    }

    pid_t pid = fork();
    if (pid == 0)  // child
    {
        close(fd[0]);
        int i = 1;
        while (i)
        {
            write(fd[1],"A",1);
            printf("pipe capacity: %dn",i++);
        }
        close(fd[1]);

    }
    else if(pid > 0)
    {
        close(fd[1]);
        waitpid(pid,NULL,0);
        close(fd[0]);
    }
    else
    {
        perror("fork errorn");
        return 1;
    }
    return 0;
}

執行結果:

由此可見,管道大小為64k

本文永久更新連結地址https://www.linuxidc.com/Linux/2018-04/151680.htm


IT145.com E-mail:sddin#qq.com