Linux进程间通信——管道

Linux进程间通信——管道调用pipe函数时在内核中开辟一块缓冲区(称为管道)用于通信,它有一个读端一个写端,然后通过filedes参数传出给用户程序两个文件描述符,filedes[0]指向管道的读端,filedes[1]指向管道的写端。向这个文件读写数据其实是在读写内核缓冲区。pipe函数调用成功返回…

管道

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

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

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

管道代码示例

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

#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;
}

两个进程通过一个管道只能实现单向通信,比如上面的例子,子进程写父进程读,如果有时候也需要父进程写子进程读,就必须另开一个管道。

命名管道

管道的使用有个限制,就是必须是父子进程间才可以通信,如果不是父子进程是不能使用上面的管道通信的,需要命名管道。

#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标志):

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

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

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

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

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

今天的文章Linux进程间通信——管道分享到此就结束了,感谢您的阅读。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/14689.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注