进程间通信_1_管道

首先对进程间通信做一个基本介绍,进程有独立性,独享各种资源,运行期间互不干扰,每个进程的数据都保存在自己的PCB中,但是问题就来了,进程需要相互之间共享传递数据时,例如父子进程,父进程需要子进程的一个处理数据,这个时候就需要进程间的通信。进程通信指的就是在进程间传输数据,实质上就是进行通信的进程(可以是两个进程互相通信,也可以是多个)可以打开同一份资源。

提到进程间通信还有几个基本的概念要说:
    1. 临界资源:进程间进行通信时,会共同访问一份资源,这份称为临界资源
    2. 临界区:访问临界区的代码
    3. 数据不一致:两个(或多个)进程访问临界资源时可能发生读写内容不一致
    4. 原子性:在访问临界资源时,将正在做的事情做完不受打扰(避免数据不一致)
    5. 互斥:临界区访问临界资源时,当前临界资源仅只有当前访问者
    6. 同步:访问临界资源时,维持合理的顺序

进程间通信的目的有:
  • 数据传输:进程需要将数据发送给其他的进程
  • 资源共享:多个进程之间共享同样的资源
  • 通知事件:一个进程向其他进程(一个或多个)发送消息,通知某件事情的发生。
  • 进程控制:一个进程控制另一个进程的执行过程,知道被控制进程的运行状态,大多希望能阻拦被控制进程异常

进程间通信的基本分类:
  • 管道 :匿名管道pipe、命名管道
  • System V 进程间通信:System V 消息队列 、System V 共享内存、 System V 信号量
  • POSIX 进程间通信:消息队列、共享内存、信号量、互斥量、条件变量、读写锁

这里先说一下管道的基本概念
    把一个进程连接到另一个进程的一个数据流称为“管道“(数据传输过程在内核中完成)。管道是由内核管理的一个缓冲区,它的一端连接一个进程的输出,另一端连接一个进程的输入,一个管道 只能在两个进程之间单向传输数据,若需要双向传输,还需要另一个人管道。大致如下图:
一、匿名管道(pipe)
    1. 管道的创建
// 头文件
#include<unstid.h>

// 原型
int pipe(int fd[2]);
// 参数:fd 文件描述符数组,fd[0]表示读端,fd[1]表示写端
// 返回值: 成功返回 0 ,失败返回错误码
    (1)例1:从标准输入(键盘)读取数据,写入管道,从管道中读取数据输出到标准输出(显示器)
  // pipe.c

  1 #include<stdio.h>                                                                                                                 
  2 #include<unistd.h>                                                                                                                
  3 #include<string.h>                                                                                                                
  4 #include<stdlib.h>                                                                                                                
  5                                                                                                                                   
  6 int main()                                                                                                                        
  7 {                                                                                                                                 
  8     int fd[2];                                                                                                                    
  9     char buf[64];                                                                                                                 
10     int len;                                                                                                                      
11                                                                                                                                   
12     if( pipe(fd) == -1){        //  创建管道                                                                                                       
13         perror("make pipe error.\n");                                                                                             
14     }                                                                                                                             
15                                                                                                                                   
16     // 从标准输入读取                                                                                                             
17     while( fgets( buf, 64, stdin)){                                                                                               
18         len = strlen(buf);                                                                                                        
19                                                                                                                                   
20         // 写入管道                                                                                                               
21         if(write( fd[1], buf, len) != len){                                                                                       
22             perror("write to pipe error.\n");                                                                                     
23             break;                                                                                                                
24         }     
25                                                                                                                                   
26         memset( buf, 0x00,  sizeof(buf));                                                                                         
27                                                                                                                                   
28         // 从管道读取                                                                                                             
29         if( ( len = read( fd[0], buf, 64)) == -1){                                                                                
30             perror("read from pipe error.\n");                                                                                    
31             break;                                                                                                                
32         }                                                                                                                         
33                                                                                                                                   
34         // 往标准输出写                                                                                                         
35         if( write( 1, buf, len) != len){                                                                                          
36             perror("read error.\n");                                                                                              
37             break;                                                                                                                
38         }                                                                                                                         
39     }                                                                                                                             
40     return 0;                                                                                                                     
41 }                         
    
    (2)例2:使用 fork 创建子进程,父子进程通过管道传输数据。父进程先创建管道,再创建子进程,子进程写时拷贝父进程的数据,所以就会看到同一个管道文件,父进程关闭读端,子进程关闭写端,这样就可以进行管道传输。大致如下图:
                    父进程创建管道
                        父进程创建子进程
                        父进程关闭读端,子进程关闭写端
  //  fork_pipe.c

  1 #include <stdio.h>                                                                                                                
  2 #include <stdlib.h>                                                                                                               
  3 #include <unistd.h>                                                                                                               
  4 #include <string.h>                                                                                                               
  5                                                                                                                                   
  6 int main()                                                                                                                        
  7 {                                                                                                                                 
  8     int pipe_fd[2];                                                                                                               
  9     if(pipe(pipe_fd) == -1){                                                                                                      
10         // 创建管道失败                                                                                                           
11         perror("make pipe error.\n");                                                                                             
12     }                                                                                                                             
13                                                                                                                                   
14     pid_t pid;                                                                                                                    
15     pid = fork();                                                                                                                 
16     if(pid == -1){                                                                                                                
17         // 创建子进程错误                                                                                                         
18         perror("make fork error.\n");                                                                                             
19     }                                                                                                                             
20     if(pid == 0){                                                                                                                 
21         // 子进程,关掉读端,写入hello, world!之后关闭写端,退出                                                                   
22         close(pipe_fd[0]);                                                                                                        
23         write(pipe_fd[1], "hello, world!",13);                                                                                    
24         close(pipe_fd[1]);  
25         exit(1);                                                                                                                  
26     }                                                                                                                             
27                                                                                                                                   
28     //父进程关闭写端,将管道内容读取到缓冲区
29     close(pipe_fd[1]);                                                                         
30     char buf[16] = {0};                                                                                                           
31     read(pipe_fd[0], buf, 13);                                                                                                    
32                                                                                                                                   
33     // 打印缓冲区内容                                                                                                             
34     printf("%s\n",buf);                                                                                                           
35                                                                                                                                   
36     return 0;                                                                                                                     
37 }                                             


2.   管道的特点
  • 单向传输数据
  • 适用于有亲属关系的进程(父子进程、兄弟进程,要有共同的祖先)
  • 自带同步互斥机制
  • 管道生命周期随进程,进程结束,管道消失
  • 管道提供面向字节流的服务

3.   管道的读写规则
    (1)在读写操作进行时,linux会保证操作的原子性。
    (2)当管道内没有数据可读,读端阻塞式等待数据
    (3)当管道内已经写满时,写端阻塞等待,直到有进程读走数据,当写入数据量大于 PIPE_BUF 时,linux不在保证写入的原子性
    (4)写端写一段时间后关闭,读端一直读完数据后关闭管道
    (5)写端一直写,读端读一段时间后关闭,写端会被操作系统处理(发送13号信号)异常退出












猜你喜欢

转载自blog.csdn.net/Cherubim1/article/details/79840925