进程间通信1_管道通信

进程间通信是指进程间的信息交换,其所交换的信息量少者是一个状态或数值,多者则是成千上万的字节。进程间通信的目的主要是为了实现数据输(一个进程需要将他的数据发给另外一个进程),资源共享(多个进程之间共享同样的资源),通知事件(一个进程需要向另一个或一组进程发送消息,通知他们发生某种事情(如进程终止时要通知父进程)),进程控制。

进程间通信的分类:

管道:匿名管道pipe

           命名管道

System V IPC:System V 消息队列

                          System V 共享内存

                          System V 信号量

POSIX IPC: 消息队列

                    共享内存 

                    信号量

                    互斥量

                    条件变量

                    读写锁

 

一.管道

什么是管道?

我们把一个进程连接到另一个进程的一个数据流程为管道

pipe操作中,读写操作互斥进行。

读进程和写进程同步

匿名管道:

#include<unistd.h>
//功能:创建一个匿名管道
//原型
int pipe(int int fd[2]);
//参数
//fd 文件描述符数组,其中fd[0]表示读端,fd[1]表示写端
//返回值:成功返回0,失败返回错误代码

代码实现一个匿名管道

父进程实现写端,子进程实现读端并将读取的数据打印到屏幕上

  1 #include<unistd.h>
  2 #include<stdio.h>
  3 #include<string.h>
  4 #include<stdlib.h>
  5 #include<errno.h>
  6 int main()
  7 {
  8     int fd[2];
  9     if(pipe(fd)<0)
 10     {
 11         perror("pipe error");
 12         return -1;
 13     }
 14     int pid =fork();
 15     if(pid<0)
 16     {
 17         return -1;
 18     }
 19     else if(pid ==0)
 20     {
 21         //子进程来读数据
 22         close(fd[1]);//关闭写端
 23         char buf[1024]={0};
 24         read(fd[0],buf,1024);
 25         printf("%s",buf);
 26         close(fd[0]);
 27     }
 28     else
 29     {
 30         close(fd[0]);
 31         write(fd[1],"hello",5);
 32         close(fd[1]);
 33     }
 34 }

 

用fork来共享管道原理

1.父进程创建出管道

2.父进程fork出子进程

3.父进程关闭fd[0],子进程关闭fd[1]

 

管道的读写规则:

当没有数据可读时
O_NONBLOCK disable:read调用阻塞,即进程暂停执行,一直等到有数据来到为止。
O_NONBLOCK enable:read调用返回-1,errno值为EAGAIN。

当管道满的时候
O_NONBLOCK disable: write调用阻塞,直到有进程读走数据
O_NONBLOCK enable:调用返回-1,errno值为EAGAIN

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

 

管道的特点

只能用于具有共同祖先的进程(具有亲缘关系的进程)之间进行通信;通常,一个管道由一个进程
创建,然后该进程调用fork,此后父子进程之间就可应用该管道。
管道提供流式服务
一般而言,进程退出,管道释放,所以管道的生命周期随进程
一般而言,内核会对管道操作进行同步与互斥
管道是半双工的,数据只能向一个方向流动;需要双方通信时,建立起俩个管道。

 

命名管道:

管道应用的一个限制就是只能在具有相同祖先的进程间通信。如果我们想在不相关的进程之间交换数据,可以使用FIFO文件来做这项工作,它经常被称为命名管道。命名管道是一种特殊类型的文件。

1.创建一个命名管道

命名管道可以使用以下指令经行创建

$ mkfifo filename

命名管道也可以使用相关函数进行创建

int mkfifo(const char*filename,mode_t mode);

创建一个命名管道

int main()
{
    mkfifo("p2",0644);
    return 0;
}

 

命名管道和匿名管道的区别:

匿名管道由pipe函数创建并打开

命名管道由mkfifo函数创建,打开用open

FIFO与pipe之间唯一的区别在它们创建于打开的方式不同,一旦这些工作完成后,它们具有相同的语意。

 

命名管道的打开规则:

如果当前打开操作是为读而打开FIFO时
O_NONBLOCK disable:阻塞直到有相应进程为写而打开该FIFO
O_NONBLOCK enable:立刻返回成功
如果当前打开操作是为写而打开FIFO时
O_NONBLOCK disable:阻塞直到有相应进程为读而打开该FIFO
O_NONBLOCK enable:立刻返回失败,错误码为ENXIO

下面通过代码来实现一个匿名管道

读端

  1 /*这是一个命名管道的操作代码,从命名管道中读取数据打印
  2  * 1. 创建一个命名管道:
  3  *      int mkfifo(const char *pathname, mode_t mode);
  4  *          pathname:   管道文件的路径名
  5  *          mode:      管道文件的权限
  6  *      成功:0     失败:-1
  7  * 2. 打开管道 open
  8  * 3. 从管道读取数据 read
  9  * 4. 不玩了则关闭管道文件close
 10  */
 11 
 12 #include <stdio.h>
 13 #include <unistd.h>
 14 #include <stdlib.h>
 15 #include <errno.h>
 16 #include <string.h>
 17 #include <fcntl.h>
 18 
 19 int main()
 20 {
 21     umask(0);
 22     //为了防止因为管道文件已经存在,每次创建失败
 23     //unlink("./test.fifo")----删除文件
 24     //1.创建命名管道
 25     if(mkfifo("./test.fifo", 0664) < 0) {
 26         if (errno == EEXIST) {
 27             //代表文件已经存在了
 28             //这时候不退出
 29         }else {
 30             perror("mkfifo error");
 31             return -1;
 32         }
 33     }
 34     //2. 打开管道文件
 35     //打开特性:
 36     //  如果以只读打开命名管道,那么open函数将阻塞等待
 37     //  直到有其他进程以写的方式打开这个命名管道
 38     //
 39     //  如果以只写打开命名管道,那么open函数将阻塞等待
 40     //  直到有其他进程以读的方式打开这个命名管道
 41     //
 42     //  如果命名管道以读写方式打开,则不会阻塞
 43     int fd = open("./test.fifo", O_RDONLY);
 44     if (fd < 0) {
 45         perror("open fifo error");
 46         return -1;
 47     }
 48     printf("open fifo file sucess!! read start!!\n");
 49     while(1) {
 50         char buff[1024] = {0};
 51         int ret = read(fd, buff, 1023);
 52         if (ret > 0) {
 53             printf("client say:[%s]\n", buff);
 54         }else if (ret == 0){
 55             //管道特性:如果所有写端被关闭,那么读取时返回0
 56             printf("all write point close!!\n");
 57             sleep(1);
 58         }
 59     }
 60     close(fd);
 61     return 0;
 62 }

​

写端

  1 /*这是一个命名管道的操作代码,从命名管道中读取数据打印
  2  * 1. 创建一个命名管道:
  3  *      int mkfifo(const char *pathname, mode_t mode);
  4  *          pathname:   管道文件的路径名
  5  *          mode:      管道文件的权限
  6  *      成功:0     失败:-1
  7  * 2. 打开管道 open
  8  * 3. 从管道读取数据 read
  9  * 4. 不玩了则关闭管道文件close
 10  */
 11 
 12 #include <stdio.h>
 13 #include <unistd.h>
 14 #include <stdlib.h>
 15 #include <errno.h>
 16 #include <string.h>
 17 #include <fcntl.h>
 18 
 19 int main()
 20 {
 21     umask(0);
 22     //为了防止因为管道文件已经存在,每次创建失败
 23     //unlink("./test.fifo")----删除文件
 24     //1.创建命名管道
 25     if(mkfifo("./test.fifo", 0664) < 0) {
 26         if (errno == EEXIST) {
 27             //代表文件已经存在了
 28             //这时候不退出
 29         }else {
 30             perror("mkfifo error");
 31             return -1;
 32         }
 33     }
 34     //2. 打开管道文件
 35     //打开特性:
 36     //  如果以只读打开命名管道,那么open函数将阻塞等待
 37     //  直到有其他进程以写的方式打开这个命名管道
 38     //
 39     //  如果以只写打开命名管道,那么open函数将阻塞等待
 40     //  直到有其他进程以读的方式打开这个命名管道
 41     //
 42     //  如果命名管道以读写方式打开,则不会阻塞
 43     int fd = open("./test.fifo", O_WRONLY);
 44     if (fd < 0) {
 45         perror("open fifo error");
 46         return -1;
 47     }
 48     printf("open fifo file sucess!! read start!!\n");
 49     while(1) {
 50         char buff[1024] = {0};
 51         scanf("%s", buff);
 52         write(fd, buff, strlen(buff));
 53     }
 54     close(fd);
 55     return 0;
 56 }

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值