浅谈进程间的通信(一)

为什么要进行进程间通信?

      Linux作为一个多任务多进程的操作系统,各个进程之间的信息交互(事件通知,数据传输,进程控制)是不可避免的。进程间通信就是要在不同的进程之间传播或者交换消息。但是由于进程的独立性,所以导致进程间进行数据通信将变得非常麻烦。操作系统不得不提供方法来使进程间能够通信 。

           操作系统为我们提供进程间通信的方式其实不止一种,因为通信的公共介质有所不同,因此进程间的通信方式不止一种。Linux的进程间通信方法有管道、消息队列、信号量、共享内存、套接口等。其中管道分为命名管道和无名管道。消息队列、信号量、共享内存通称为系统(POSIX 和 System V系统)IPC。管道、消息队列、信号量、共享内存用于本地进程间通信,而套接口用于远程进程间通信。

管道(Pipe)及命名管道(named pipe):管道可用于具有亲缘关系间的通信,命名管道克服了管道没有名字这个限制,因此,除具有管道所具有的功能外,它还允许无亲缘关系的进程间的通信

进程间通信方式

01 管道

概念

管道(Pipe):也称为匿名管道,是Linux下的最常见的进程间通信方式之一,是一种很经典的进程间通信方式。其优点在于简单易用,其缺点在于功能简单,有很多限制。

管道的创建与关闭

创建函数原型: int pipe(int fd[2]);

返回值:成功返回0,失败返回-1。参数fd[2]是一个长度为2的文件描述符数组,fd[0]是读出端的文件描述符,fd[1]是写入端的文件描述符。当函数成功返回后,则自动维护了一个从fd[1]到fd[0]的数据通道。

比如下面这段代码就是用pipe函数创建管道和关闭管道

管道的读写

从上面的实例可以看出,单独一个进程操作管道是没有意义的,管道的应用一般体现在父子进程或者兄弟进程之间的通信。使用read函数和write函数对管道进行读写操作,但是要注意管道的两端是固定了任务的,即管道的读出端只能用于读写数据,管道的写入端只能用于写入数据。

  • 读写特性

              管道的读写操作默认是阻塞操作

              管道自带同步与互斥:读写大小不超过PIPE_BUF大小

               如果管道没有数据,那么read则一直等待,直到有数据

               如果管道数据满了,那么write则一直等待,直到有数据被取出去

               如果所有写入端关闭了,read读完所有数据之后,返回0

              如果所有的管道读取端关闭了,那么写入端会触发异常  ,操作系统发送SIGPIPE信号,通知我们,读取端关闭了,(这个信号会导致write端进程退出)

              读取数据量不大于PIPE_BUF时,管道可以保证读写的原子性(读写操作不会被打断,造成数据混乱的问题)

下面这个例子就是父进程利用管道向子进程发送消息

#include<stdio.h>
#include<unistd.h>
#include <errno.h>
#include <string.h>

//函数原型:
//int pipe(int fd[2])
int main()
{
    pid_t pid;
    int fd[2] = {0};
    if(pipe(fd) < 0)
    {
        perror("create fail:");
        return -1;
    }
    //创建子进程 fork()
    if((pid = fork()) < 0)
    {
        perror("fork fail:");
        return -1;
    }
    //父进程 写
    else if(pid > 0)
    {
        char* str = "troye sivan";
         close(fd[0]);
         write(fd[1],str,strlen(str));
         close(fd[1]);
     }
     //子进程 读
     else
     {
         close(fd[1]);
         char buff[256] = {0};
         read(fd[0],buff,11);
         printf("%s",buff);
         close(fd[0]);
 
     }
     return 0;
 }
                                        

:pipe函数一定要在fork之前,因为子进程自动继承父进程的数据段,所以父子进程可以同时拥有管道的操作权。 

02 命名管道

概念

命名管道(named pipe)也称为FIFO,它是一种文件类型,在文件系统中可以看到它,创建一个FIFO文件类似于创建一个普通文件。管道的一个很大限制就是它没有名字。因此,只能用于具有亲缘关系的进程间通信。命名管道提出后克服了该限制,FIFO不同于管道之处在于它提供了一个路径名与之关联,以FIFO 文件形式存在于文件系统中。这样,即使与FIFO的创建进程不存在亲缘关系的进程,只要可以访问该路径,就能够彼此通过FIFO相互通信。因此,通过FIFO不相关的进程也能交换数据。

命名管道的创建

两种方式:

  1. 在shell中可以使用mkfifo 命令建立一个管道                                                                                                                      mkfifo[-m mode] name   mode指出将创建FIFO的八进制模式,注意它也会受到umask修正。
  2.  通过函数创建                                                                                                                                                                                         函数形式  :          int mkfifo(const char* pathname,mode_t mode);  pathname是一个普通的路径名,也就是创建后FIFO文件的名字。mode与open函数中的mode参数相同。如果mkfifo的第一个参数是已经存在的路径名时,则返回EEXIST错误,所以一般典型的调用代码首先会检查是否返回该错误,如果确实返回该错误,那么只要调用打开FIFO函数就可以了。

下面这个例子是使用FIFO来进行两个进程间的通信

read_fifo.c

#include <stdio.h>                        
#include <fcntl.h>                        
#include <string.h>                       
#include <unistd.h>                       
#include <errno.h>                        
int main()                                
{                                         
                                          
    umask(0);                             
    if(mkfifo("troye",0664) < 0)          
    {                                     
        if(errno == EEXIST)               
        {                                 
            printf("fifo exit.\n");       
        }                                 
        perror("fifo:");                  
        return -1;                        
    }                                     
    int fd = open("troye",O_RDONLY);      
    if(fd < 0)                            
    {                                     
        perror("open error:");            
        return -1;                        
    }                                     
        printf("open fifo successful!\n");
    while(1){                             
        char buf[1024];                   
        memset(buf, 0x00, 1024);          
        int ret = read(fd,buf,1024);      
        if(ret > 0)                             
        {                                       
            printf("client say:%s\n",buf);      
        }                                       
    }                                           
    close(fd);                                  
    return 0;                                   
}                                               

 

write_fifo.c


#include <string.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
int main()
{
    int fd = open("troye",O_WRONLY);
    if(fd < 0)
    {
        perror("open error:");
        return -1;
    }
    printf("open fifo successful!\n");
   while(1){
    char buf[1024] = {0};
    printf("input:");
    fflush(stdout);
    scanf("%s",buf);
    write(fd,buf,1024);
    }
    close(fd);
    return 0;
}

             在程序read_fifo.c中先通过mkfifo创建一个FIFO,然后打开一个名为troye的FIFO的管道。读取数据并输出到标准输出在程序write_fifo.c中先打开troye文件,再通过标准输入写入数据。

 

 

 

            

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


 

 

 

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值