进程间通信——管道

一、进程间通信

1、本质:让两个不相干的进程看到同一块资源,这个资源肯定是操作系统提供的

2、目的

    ·数据传输:一个进程需要将它的数据发给另一个进程

    ·资源共享:多个进程之间共享同样的资源

    ·通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(例如:子进程终止时要通知父进程)

    ·进程控制:有些进程希望完全控制另一个进程的执行,此时控制进程希望能够拦截另一个进程所有陷入和异常,并能够及时知道它的状态改变

3、分类

(1)管道

    ·匿名管道

    ·命名管道

(2)System V IPC

    ·System V 消息队列

    ·System V 共享内存

    ·System V 信号量

(3)POSIX IPC

    ·消息队列

    ·共享内存

    ·信号量

    ·互斥量

    ·条件变量

    ·读写锁

二、管道

1、概念

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

    ·从一个进程链接到另一个进程间的一个数据流


2、匿名管道:创建一个无名管道

int pipe(int fd[2]);

其中:fd为文件描述符数组,fd[0]表示读端,fd[1]表示写端

          成功返回0,失败返回错误代码


  1 #include<stdio.h>
  2 #include<stdlib.h>
  3 #include<string.h>
  4 #include<unistd.h>
  5 
  6 int main()
  7 {
  8     int fds[2];
  9     char buf[100];
 10     int len;
 11 
 12     if(pipe(fds) == -1){
 13         perror("make pipe"),exit(1);
 14     }
 15     //read from stdin
 16     while(fgets(buf,100,stdin)){
 17         len = strlen(buf);
 18         //write into pipe
 19         if(write(fds[1],buf,len) != len){
 20             perror("write to pipe");
 21             break;
 22         }
 23         memset(buf,0x00,sizeof(buf));
 24         //read from pipe
 25         if((len = read(fds[0],buf,100)) == -1){
 26             perror("read from pipe");
 27             break;
 28         }
 29         //write to stdout
 30         if(write(1,buf,len) != len){
 31             perror("write to stdout");
 32             break;
 33         }
 34     }
 35 }

3、用fork来共享管道原理

(1)父进程创建管道(父进程调用pipe开辟管道,得到两个文件描述符分别指向管道的读端和写端)


(2)父进程fork出子进程(父进程调用fork创建子进程,那么子进程也有两个文件描述符指向同一个管道)


(3)父进程关闭fd[0],子进程关闭fd[1](父进程关闭管道读端,子进程关闭管道写端。父进程往管道里写,子进程从管道里读。管道是用环形队列实行的,数据从写端流入,从读端流出,这样就实现了进程间通信)


 1 #include<stdio.h>
  2 #include<stdlib.h>
  3 #include<string.h>
  4 #include<unistd.h>
  5 
  6 int main()
  7 {
  8     int fds[2];
  9     if(pipe(fds) == -1){
 10         perror("pipe");
 11         return 1;
 12     }
 13     pid_t pid = fork();
 14     if(pid < 0){
 15         perror("fork");
 16         return 2;
 17     }
 18     else if(pid > 0){//parent write
 19         close(fds[0]);
 20         char *argv = "child , I am your father";
 21         int i = 4;
 22         while(i--){
 23             write(fds[1],argv,strlen(argv));
 24             sleep(1);
 25         }
 26     }
 27     else{//child read
 28         close(fds[1]);
 29         char buf[1024];
 30 
 31         while(1){
 32             size_t s = read(fds[0],buf,sizeof(buf));
 33             if(s > 0){
 34                 buf[s] = 0;
 35                 printf("child : %s\n",buf);
 36             }
 37             else if(s == 0){
 38                 printf("child : parent did not speak!\n");
 39                 break;
 40             }
 41             else{
 42                 perror("read");
 43                 return 3;
 44             }
 45         }
 46     }
 47     return 0;
 48 }

4、管道读写规则


5、匿名管道的特点

(1)只有用于具有共同祖先的进程(具有亲缘关系的进程)之间通信;通常一个管道由一个进程创建,然后该进程调用fork,此后,父子进程之间就可以应用该管道

(2)管道可以提供流式服务

(3)一般情况,进程退出,管道释放,所以管道的生命周期随进程

(4)一般而言,内核会对管道操作进行同步和互斥

(5)管道是半双工的,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道

6、命名管道

·管道应用的一个限制就是只能在具有共同祖先(具有亲缘关系)的进程间通信

·如果我们想在不想关的进程之间交换数据,可以使用FIFO文件来工作,它被称为命名管道

·命名管道是一种特殊类型的文件

创建命名管道

*可以从命令行上创建,命令行方法是使用命令:

mkfifo filename


*可以从程序里创建,相关函数:

  1 #include<sys/types.h>
  2 #include<sys/stat.h>
  3 
  4 int mkfifo(const char *filename,mode_t mode);

返回值:成功返回0,失败返回-1

参数:filename为创建出的管道名称

          mode为这个管道具有的权限

  1 #include<sys/types.h>
  2 #include<sys/stat.h>
  3 #include<stdio.h>
  4 
  5 
  6 int main()
  7 {
  8     if(mkfifo("fifo",0644) == -1){
  9         perror("mkfifo");
 10         return 1;
 11     }   
 12     return 0;
 13 }

7、匿名管道和命名管道的区别

(1)匿名管道由pipe函数创建

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

(3)FIFO(命名管道)与pipe(匿名管道)之间唯一的区别是打开的方式不同,一旦这些工作完成之后,它们具有相同的语义

例:用命名管道实现

Makefile文件

  1 .PHONY:all
  2 all:client server
  3 client:client.c
  4     gcc client.c -o client
  5 server:server.c
  6     gcc server.c -o server
  7 .PHONY:clear
  8 clear:
  9     rm -f client server

client.c

 1 #include<stdio.h>
  2 #include<sys/types.h>
  3 #include<sys/stat.h>
  4 #include<fcntl.h>
  5 #include<unistd.h>
  6 #include<stdlib.h>
  7 #include<string.h>
  8 
  9 #define ERR_EXIT(m)\
 10 do{\
 11     perror(m);\
 12     exit(EXIT_FAILURE);\
 13 }while(0)
 14 int main()
 15 {
 16     int wfd = open ("mypipe",O_WRONLY);
 17     if ( wfd < 0 ){
 18         ERR_EXIT("open");
 19     }
 20     char buf[1024];
 21     while(1){
 22         buf[0] = 0;
 23         printf("please enter : ");
 24         fflush(stdout);
 25         ssize_t s = read (0,buf,sizeof(buf)-1);
 26         if ( s > 0 ){
 27             buf[s] = 0;
 28             write(wfd,buf,strlen(buf));
 29         }
 30         else if(s <= 0){
 31             ERR_EXIT("read");
 32         }
 33     }
 34     close(wfd);
 35     return 0;
 36 }

server.c

  1 #include<stdio.h>
  2 #include<sys/types.h>
  3 #include<sys/stat.h>
  4 #include<fcntl.h>
  5 #include<unistd.h>
  6 #include<stdlib.h>
  7 
  8 #define ERR_EXIT(m)\
  9 do{\
 10     perror(m);\
 11     exit(EXIT_FAILURE);\
 12 }while(0)
 13 int main()
 14 {
 15     umask(0);
 16     if ( mkfifo("mypipe",0644) < 0){
 17         ERR_EXIT("mkfifo");
 18     }
 19     int rfd = open ("mypipe",O_RDONLY);
 20     if ( rfd < 0 ){
 21         ERR_EXIT("open");
 22     }
 23     char buf[1024];
 24     while(1){
 25         buf[0] = 0;
 26         printf("please wait...\n");
 27         ssize_t s = read (rfd,buf,sizeof(buf)-1);
 28         if ( s > 0 ){
 29             buf[s-1] = 0;
 30             printf("client say : %s\n",buf);
 31         }
 32         else if(s == 0){
 33             printf("client quit,exit now!\n");
 34             exit(EXIT_SUCCESS);
 35         }
 36         else{
 37             ERR_EXIT("read");
 38         }
 39     }
 40     close(rfd);
 41     return 0;
 42 }

测试结果:

(1)先运行server,创建出mypipe

(2)再运行client,输入数据进行交流


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值