Linux进程间通讯——管道

管道

管道是一种最基本的进程间通讯机制,由pipe函数建立:函数

#include <unistd.h>
int pipe(int filedes[2]);
复制代码

调用pipe函数时在内核中开辟一块缓冲区(称为管道)用于通讯,它有一个读端一个写端,而后经过filedes参数传出给用户程序两个文件描述符,filedes[0]指向管道的读端,filedes[1]指向管道的写端。向这个文件读写数据实际上是在读写内核缓冲区。pipe函数调用成功返回0,调用失败返回-1。ui

管道代码示例

子进程经过管道向父进程发送数据。限制在父子进程间通讯。spa

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

int main () {
    char* msg;
    char buf[20];
    int pipe_filed[2];
    pipe(pipe_filed);
    pid_t pid = fork();
    if(pid < 0) {
        perror("fork errir.");
        exit(1);
    } else if (0 == pid) {
        msg = "child";
        write(pipe_filed[1], msg, sizeof(msg));
        printf("child process send: %s\n", msg);
    } else {
        read(pipe_filed[0], buf, sizeof(buf));
        printf("parent process recv: %s\n", buf);

        int status;
        wait(&status);
        if (WIFEXITED(status))
            printf("Child exited with code %d\n", WEXITSTATUS(status));
        else if (WIFSIGNALED(status))
            printf("Child terminated abnormally, signal %d\n", WTERMSIG(status));
    }

    return 0;
}
复制代码

两个进程经过一个管道只能实现单向通讯,好比上面的例子,子进程写父进程读,若是有时候也须要父进程写子进程读,就必须另开一个管道。code

命名管道

管道的使用有个限制,就是必须是父子进程间才能够通讯,若是不是父子进程是不能使用上面的管道通讯的,须要命名管道。orm

#include<sys/types.h>
#include<sys/stat.h>

int mkfifo(const char * pathname,mode_t mode);
复制代码

依参数pathname创建特殊的FIFO文件,参数mode为该文件的权限。若成功则返回0,不然返回-1,错误缘由存于errno中。进程

命名管道代码示例

没必要是父子进程间。进程A向进程B发送信息:事件

/* send process*/
#include<stdlib.h>
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<errno.h>
#include<string.h>

int main () {
    if (-1 == mkfifo("comm", 0666)) {
        if (EEXIST != errno) {
            perror("mkfifo failure.");
            exit(EXIT_FAILURE);
        }
    }

    int fd = open("comm", O_WRONLY);
    if (fd < 0) {
        perror("open pipe failure.");
    }
    
    char* msg = "process of send.";
    write(fd, msg, strlen(msg));
    close(fd);

    return 0;
}

复制代码
/* recv process*/
#include<stdlib.h>
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<errno.h>
#include<string.h>

int main () {
    if (-1 == mkfifo("comm", 0666)) {
        if (EEXIST != errno) {
            perror("mkfifo failure.");
            exit(EXIT_FAILURE);
        }
    }

    int fd = open("comm", O_RDONLY);
	if (fd < 0) {
        perror("open pipe failure.");
    }
    char* buf = (char*)malloc(80);
    bzero(buf, 80);
	read(fd, buf, 80);
	printf("recv from other process: %s\n", buf);
    close(fd);
	free(buf);

    return 0;
}
复制代码

特殊状况

使用管道须要注意如下4种特殊状况(假设都是阻塞I/O操做,没有设置O_NONBLOCK标志):ip

  • 若是全部指向管道写端的文件描述符都关闭了(管道写端的引用计数等于0),而仍然有进程从管道的读端读数据,那么管道中剩余的数据都被读取后,再次read会返回0,就像读到文件末尾同样。同步

  • 若是有指向管道写端的文件描述符没关闭(管道写端的引用计数大于0),而持有管道写端的进程也没有向管道中写数据,这时有进程从管道读端读数据,那么管道中剩余的数据都被读取后,再次read会阻塞,直到管道中有数据可读了才读取数据并返回。string

  • 若是全部指向管道读端的文件描述符都关闭了(管道读端的引用计数等于0),这时有进程向管道的写端write,那么该进程会收到信号SIGPIPE,一般会致使进程异常终止。

  • 若是有指向管道读端的文件描述符没关闭(管道读端的引用计数大于0),而持有管道读端的进程也没有从管道中读数据,这时有进程向管道写端写数据,那么在管道被写满时再次write会阻塞,直到管道中有空位置了才写入数据并返回。

其实和其余I/O事件的阻塞与同步是同样的。

相关文章
相关标签/搜索