天天看点

Linux 进程间通信 | 管道

管道的基本概述及其特性

管道是Unix中最古老的进程间通信的形式。 我们把从一个进程连接到另一个进程的一个数据流称为一个“管道”

特性:半双工通信---可以选择方向的单向通信。

本质:在内核中开辟的一段缓冲区(内核空间中的一块内存)

原理:多个进程通过访问同一块内核中的缓冲区实现通信

分类:

        匿名管道:缓冲区没有标识符,只能用于具有亲缘关系的进程间通信

        命名管道:缓冲区具有标识符(在文件系统中会显示一个管道文件),可用于同一主机上任意的进程间通信。

匿名管道

管道的创建

函数原型

#include <unistd.h>
       int pipe(int pipefd[2]);
           

pipefd[0]:用于读

        pipefd[1]:用于写

        返回值:成功返回0 失败返回-1;。

父子进程间实现匿名管道通信

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <string.h>
int main()
{
    int pipefd[2];

    int ret = pipe(pipefd);
    if (ret < 0)
    {
        perror("pipe");
        return -1;
    }

    ret = fork();
    if (ret < 0)
    {
        perror("fork");
        return -1;
    }
    else if (ret == 0)
    {
        //child  read
        close(pipefd[1]);
        char buf[1024] = {0};
        ret = read(pipefd[0], buf, 1023);
        if (ret < 0)
        {
            perror("read");
            return -1;
        }
        printf("child read:%s\n", buf);
        close(pipefd[0]);
    }
    //父进程  write
    close(pipefd[0]);

    printf("father write:");
    char buf[128] = {0};
    fgets(buf, 127, stdin);
    write(pipefd[1], buf, strlen(buf));

    close(pipefd[1]);

    wait(NULL);
    return 0;
}
           
Linux 进程间通信 | 管道

命名管道

命名管道的标识符就是一个可见于文件系统的管道类型文件。 多个进程通过打开同一个管道文件,访问同一块内核中的缓冲区实现通信,当mkfifo一个管道文件后 内核不会立刻开辟内存 而是等到进行读写操作的时候会进行内存开辟。

 命令操作:mkfifo filename 创建一个命名管道文件

 函数操作:int mkfifo(const char *pathname, mode_t mode);

                mode 创建权限

                返回值 成功返回0 失败返回-1;

 创建一个命名管道进行进程间通信

//write.c
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>

int main()
{
    int ret = mkfifo("./test.fifo", 0664);
    if (ret < 0 && errno != EEXIST)
    {
        perror("mkfifo");
        return -1;
    }

    int fd = open("./test.fifo", O_WRONLY);
    if (fd < 0)
    {
        perror("open");
        return -1;
    }
    printf("creat fifo!!\n");
    char buf[1024] = {0};
    while (1)
    {
        //sprintf(buf,"%s",stdin);
        //scanf("%s",buf);
        fgets(buf,1023,stdin);
        ret = write(fd, buf, strlen(buf));
        if(ret<0)
        {
            perror("write");
            return -1;
        }
    }
    close(fd);
    return 0;
}
           
//read.c
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <memory.h>

int main()
{
    int ret = mkfifo("./test.fifo", 0664);
    if (ret < 0 && errno != EEXIST)
    {
        perror("mkfifo");
        return -1;
    }

    int fd = open("./test.fifo", O_RDONLY);
    if (fd < 0)
    {
        perror("open");
        return -1;
    }
    printf("creat fifo!!\n");
    char buf[1024] = {0};
    while (1)
    {
        ret = read(fd, buf, 1023);
        if (ret < 0)
        {
            perror("read");
            return -1;
        }
        printf("%s\n", buf);
        memset(buf, 0, 1024);
    }
    close(fd);
    return 0;
}
           
Linux 进程间通信 | 管道

管道的读写特性

匿名管道

特性:只能用于具有亲缘关系的进程间通信 因为缓冲区没有标识符,无法被其它进程找到,只能通过子进程父进程的方式获取到操作句柄实现通信。

读写特性:若管道中没有数据则read会阻塞,若管道中数据满了,则write会阻塞。

所有的管道读端被关闭,则继续write则会触发异常导致进程崩溃退出  哪个进程去写数据哪个进程就会崩溃(原因会给进程发送SIGPIPE信号)。

所有的管道写端被关闭,则继续read则会读完数据后返回0,不再阻塞。

管道提供字节流传输服务:有序的,可靠的,基于连接的流式传输。

命名管道

打开特性:若是以只读方式打开管道文件则会阻塞,直到管道被其它进程以写的方式打开 则会继续,反之亦是如此。

特性:半双工通信 可以选择方向的单向通信

管道提供字节流传输服务 ---- 有序的 可靠的 基于连接的一种流式传输

基于连接:所有read关闭则write异常,所有write关闭则read返回

互斥的体现:对管道进行写入操作大小不超过PIPE_BUF-4096大小,则保证操作的原子性

同步的体现:若管道没有数据则read阻塞 若管道数据满了 则write阻塞。

生命周期随进程---不人为干预,所有打开管道的进程退出了后则管道缓冲区被释放。 

同步与互斥

           互斥:同一时间进程对临界资源的唯一访问实现访问操作安全

            同步:通过一些条件判断让进程对临界资源的访问更加合理有序

临界资源:公共资源,大家都能访问到的资源

临界区:对临界资源访问的这段代码区域

继续阅读