Linux下的匿名管道与命名管道

管道是Unix中最古老的进程间通信的形式。

既然是通信,那么也逃不脱通信的共有的特点那就是中间的进程之间的公共部分。在管道通信中,管道就充当这个公共的部分。

管道分两种,一种是匿名管道,一种是命名管道

匿名管道

在命令行下输入的|都是匿名管道。

那么除去指令,如何在程序中创建一个匿名管道呢?

#include

int pipe(int fd[2]);
//fd:文件描述符数组
//fd[0]表示读端
//fd[1]表示写端
//成功返回0,失败返回错误代码

前面已经聊过文件描述符的概念:用系统调用open打开文件的返回值就是文件描述符。如果想看文件描述符概念的朋友请点这里

而看待pipe也如同看待一个文件,所以也是通过文件描述符来进行操作的,正迎合了Linux一切皆文件的思想。

下面来写一个小案例,父子进程间的通信:

#include
#include
#include
#include
#include

#define ERR_EXIT(m)\
    do\
    {\
        perror(m);\
        exit(EXIT_FAILURE);\
    }while(0)
//父子进程间通信
int main(int argc, char *argv[])
{
    int pipefd[2];
    if(pipe(pipefd) == -1)
        ERR_EXIT("pipe");

    pid_t pid = fork();
    char buf[128];
    memset(buf, 0x00, sizeof(buf));
    if( pid == -1)
        ERR_EXIT("fork");

    if( pid == 0){//写端
        close(pipefd[0]);
        strcpy(buf, "Hey, Coder");
        write(pipefd[1], buf, strlen(buf));
        close(pipefd[1]);
        exit(EXIT_SUCCESS);
    }

    close(pipefd[1]);
    read(pipefd[0], buf, 128);//读端
    printf("%s\n", buf);

    return 0;
}

这个代码完成的主要功能是子进程在写,父进程在读,通过匿名管道完成了父子进程间的通信。

匿名管道其具有一下特点:只能用于具有共同祖先的进程(具有血缘关系的进程)之间进行通信。
管道提供流式服务。
一般而言,进程退出,管道释放,所以管道生命周期随进程。
内核会对管道操作进行同步与互斥。
管道是半双工的,数据只能单向流动;所以需要双方通信的时候,需要建立两个管道。

命名管道

由于匿名管道只能在具有亲缘关系的进程间通信。如果想在不相关进程之间交换数据,可以使用FIFO文件来做这项工作,它就是命名管道。
命名管道是一种特殊类型的文件。

创建命名管道:

mkfifo("mypipe",0644);
//相关函数:int mkfifo(const *filename, mode_t mode)
//第一个参数很好理解,就是文件名,第二个是管道文件的权限

命名管道与匿名管道之间只有创建与打开的区别,其他意义相同。

这里我放上一个不同进程之间通信的案例:有兴趣的朋友可以点这里

管道之间的读写还有一些规则:

  1. 无数据可读
    read调用阻塞,直到有数据来为止
    read调用返回-1,errno职位EAGAIN
  2. 管道满的时候
    write调用阻塞,直到有进程读走数据
    调用返回-1,errno值为EAGAIN

如果所有管道写端对应的文件描述符被关闭,read返回0
如果所有管道读端对应的文件描述符被关闭,write操作会产生信号SIGPIPE(管道破裂),进而导致write进程退出
当写入数据量不大于PIPE_BUF时,linux保证写入原子性
当写入数据量大于PIPE_BUF时,linux不保证写入原子性

你可能感兴趣的:(管道,匿名管道,命名管道,进程通信)