Linux进程间通信之管道

来源:互联网 发布:mac 种子下载器 编辑:程序博客网 时间:2024/05/18 16:14

linux下进程间通信的几种主要手段:
1.管道(Pipe)及有名管道(named pipe):管道可用于具有亲缘关系进程间的通信,有名管道克服了管道没有名字的限制,因此,除具有管道所具有的功能外,它还允许无亲缘关系进程间的通信;
2.信号(Signal):信号是比较复杂的通信方式,用于通知接受进程有某种事件生,除了用于进程间通信外,进程还可以发送信号给进程本身;linux除了支持Unix早期 信号语义函数sigal外,还支持语义符合Posix.1标准的信号函数sigaction(实际上, 该函数是基于BSD的,BSD为了实现可靠信号机制,又能够统一对外接口,sigaction函数重新实现了signal函数);
3. 消息队列:消息队列是消息的链接表,包括Posix消息队列system V消息队列。有足够权限的进程可以向队列中添加消息,被赋予读权限的进程则可以读走队列中的消息。消息队列克服了信号承载信息量少,管道只能承载无格式字节流以及缓冲区大小受限等缺点。
4.共享内存:使得多个进程可以访问同一块内存空间,是最快的可用IPC形式。是针其他通信机制运行效率较低设计的。往往与其它通信机制,如信号量结合使用, 来达到进程间的同步及互斥。
5.信号量(semaphore):主要作为进程间以及同一进程不同线程之间的同步手段。
6.套接字(Socket):更为一般的进程间通信机制,可用于不同机器之间的进程间通信。起初是由Unix系统的BSD分支开发出来的,但现在一般可以移植到其它类Unix 系统上:Linux和System V的变种都支持套接字。

(以上来自网络)

管道

管道是由内核管理的一个缓冲区(buffer)。管道的一端连接一个进程的输出。这个进程会向管道中放入信息。管道的另一端连接一个进程的输入,这个进程取出被放入管道的信息。一个缓冲区不需要很大,它被设计成为环形的数据结构,以便管道可以被循环利用。当管道中没有信息的话,从管道中读取的进程会等待,直到另一端的进程放入信息。当管道被放满信息的时候,尝试放入信息的进程会等待,直到另一端的进程取出信息。当两个进程都终结的时候,管道也自动消失。
这里写图片描述

使用实例:

#include <stdio.h>#include <unistd.h>  //fork pipe io read/write #include <error.h>#include <string.h>int main(){       // fd[0] 读, fd[1]写    int fd[2];    pid_t pid;    int ret;    //建立管道    ret = pipe(fd);    if (ret < 0) {        printf("create pipe failed.\n");        return 1;    }    //创建子进程    pid = fork();    printf("pid is %d\n", pid);    if (pid < 0) {        printf("fork sub thread failed.\n");        return 1;    } else if (pid == 0) {        //子进程相关操作        //关闭读        printf("current is child write!\n");        close(fd[0]);        char *message = "hello, world!\n";        write(fd[1], message, strlen(message));    } else {        //父进程读        printf("current is parent read!\n");        close(fd[1]);        char readMessage[1024];        memset(readMessage, 0, sizeof(readMessage));        ret = read(fd[0], readMessage, sizeof(readMessage));        printf("%d %s\n", ret, readMessage);    }    return 0;}

1.两个进程通过一个管道只能实现单向通信。比如上面的例子,父进程写子进程读,如果有时候也需要子进程写父进程读,就必须另开一个管道。
2.管道的读写端通过打开的文件描述符来传递,因此要通信的两个进程必须从它们的公共祖先那里继承管道文件描述符。上面的例子是父进程把文件描述符传给子进程之后父子进程之间通信,也可以父进程fork两次,把文件描述符传给两个子进程,然后两个子进程之间通信, 总之需要通过fork传递文件描述符使两个进程都能访问同一管道,它们才能通信。 也就是说,管道通信是需要进程之间有关系。

命名管道

由于基于fork机制,所以管道只能用于父进程和子进程之间,或者拥有相同祖先的两个子进程之间 (有亲缘关系的进程之间)。为了解决这一问题,Linux提供了FIFO方式连接进程。FIFO又叫做命名管道(named PIPE)。

FIFO (First in, First out)为一种特殊的文件类型,它在文件系统中有对应的路径。当一个进程以读(r)的方式打开该文件,而另一个进程以写(w)的方式打开该文件,那么内核就会在这两个进程之间建立管道,所以FIFO实际上也由内核管理,不与硬盘打交道。之所以叫FIFO,是因为管道本质上是一个先进先出的队列数据结构,最早放入的数据被最先读出来(好像是传送带,一头放货,一头取货),从而保证信息交流的顺序。FIFO只是借用了文件系统(file system, 参考Linux文件管理背景知识)来为管道命名。写模式的进程向FIFO文件中写入,而读模式的进程从FIFO文件中读出。当删除FIFO文件时,管道连接也随之消失。FIFO的好处在于我们可以通过文件的路径来识别管道,从而让没有亲缘关系的进程之间建立连接。

创建命名管道的系统函数有两个:mknod和mkfifo。两个函数均定义在头⽂文件sys/stat.h。

#include <sys/types.h>  #include <sys/stat.h>  int mkfifo(const char *filename, mode_t mode);  int mknod(const char *filename, mode_t mode | S_IFIFO, (dev_t)0); 

这两个函数都能创建一个FIFO文件,注意是创建一个真实存在于文件系统中的文件,filename指定了文件名,而mode则指定了文件的读写权限。

mknod是比较老的函数,而使用mkfifo函数更加简单和规范,所以建议在可能的情况下,尽量使用mkfifo而不是mknod。

与打开其他文件一样,FIFO文件也可以使用open调用来打开。

open(const char *path, O_RDONLY);//1  open(const char *path, O_RDONLY | O_NONBLOCK);//2  open(const char *path, O_WRONLY);//3  open(const char *path, O_WRONLY | O_NONBLOCK);//4 

使用实例:
1.写

#include <unistd.h>  #include <stdlib.h>  #include <stdio.h>  #include <string.h>  #include <fcntl.h>  #include <limits.h>  #include <sys/types.h>  #include <sys/stat.h>  #define MSG_SIZE 1024// WRITEint main(){    int ret;    int pipe_fd;    int send_msg = 0;    const char *fifo_name = "/tmp/my_fifo";    char buffer[PIPE_BUF] = "hello, name pipe!";    //检测文件是否存在    if (access(fifo_name, F_OK) == -1) {    //创建fifo管道        ret = mkfifo(fifo_name, 0777);        if (ret <  0) {            printf("create fifo failed.\n");            return 1;        }    }       //打开fifo管道(只写阻塞方式打开)    pipe_fd = open(fifo_name, O_WRONLY);    printf("now pipe id is %d.\n", pipe_fd);    if (pipe_fd != -1) {        while (send_msg < MSG_SIZE) {            ret = write(pipe_fd, buffer, sizeof(buffer));            if (ret < 0) {                printf("write pipe failed.\n");                return 1;            }            send_msg += ret;        }        close(pipe_fd);    } else {        printf("open pipe failed.\n");        return 1;    }    return 0;}

2.读

#include <stdio.h>  #include <stdlib.h>  #include <string.h>  #include <fcntl.h>  #include <limits.h>  #include <sys/types.h>  #include <sys/stat.h>  //read int main()  {      char *fifo_name = "/tmp/my_fifo";    int pipe_fd;      int ret;      char buffer[PIPE_BUF + 1];      int bytes = 0;      memset(buffer,'\0', sizeof(buffer));      pipe_fd = open(fifo_name, O_RDONLY);      printf("pipe fd is %d\n", pipe_fd);      if (pipe_fd != -1) {          do {              ret = read(pipe_fd, buffer, PIPE_BUF);              bytes += ret;            printf("read from fifo is %s\n", buffer);        } while(ret > 0);          close(pipe_fd);      } else {          printf("open pipe fd failed!\n");        return 1;     }      printf("Process %d finished, %d bytes read\n", getpid(), bytes);      return 0; }  

[root@localhost workspace]# ./namepipe &
[1] 5128
[root@localhost workspace]# ./namepipe2
now pipe id is 3.
pipe fd is 3
read from fifo is hello, name pipe!
read from fifo is hello, name pipe!
Process 5129 finished, 4096 bytes read
[1]+ Done ./namepipe

原创粉丝点击