zoukankan      html  css  js  c++  java
  • Linux下的进程间通信(一)

    一、     Linux进程间通信概述

    主要分为以下几种:管道(无名管道pipe和命名管道FIFO)、信号(signal)、消息队列、共享内存、信号量、套接字(socket)等。

    主要分为以下4个领域

    (1)消息传递(管道,FIFO,消息队列)

    (2)同步(互斥锁,条件变量,读写锁,信号量)

    (3)共享内存区(匿名共享内存区,有名共享内存区)

    (4)过程调用(Solaris门,Sun RPC)

    二、     无名管道PIPE

    普通Linux都允许重定向,而重定向就是使用的管道。管道是单向的,先进先出,固定大小,一个进程向管道里进行输入,另一个进程从管道里获取输出。一旦数据被读出,则从管道里面删除,其它进程无法再读到该数据。

    管道采用了简单的流控制模式,进程在读空管道时,会一直阻塞到有进程对管道进行写时;同样,进行在写一个已满的管道时,会阻塞到有进程将管道数据取出时。

    1.主要函数

    l  int pipe(int fd[2])

    返回值:如果成功,返回0;否则返回-1

    errno=EMFILE(没有空亲的文件描述符)

    EMFILE(系统文件表已满)

          EFAULT(fd数组无效)
    注意:fd[0]用于读取管道,fd[1]用于写入管道。

    l  int read(int fd, char *buf, int len)

    int write(int fd, char *buf, int len)

    返回值:如果成功,则返回读出或写入的字节数;否则,返回-1

    l  FILE *popen(const char *command, const char *type)

    与linux中文件操作有文件流的标准I/O一样,管道的操作也支持基于文件流的模式。

    返回值:如果成功,返回一个新的文件流;否则返回NULL

    参数:command为输入的命令,type为r或w,表示读或写,不能同时为读写。如果输入rw,则只读取第一个字符作为type,即为读。

    l  int pclose(FILE *stream)

    返回值:返回系统调用wait4()的状态。如果stream无效或调用wait4()失败,则返回1。

    2. 实例

    (1)管道实例

    #include <iostream.h>
    
    #include <stdlib.h>
    
    #include <unistd.h>
    
    #include <stdio.h>
    
    #include <errno.h>
    
    #include <sys/types.h>
    
    #include <sys/wait.h>
    
          
    
    using namespace std;
    
     
    
    int main(int argc, char *argv[])
    
    {
    
             int pipe_fd1[2], pipe_fd2[2];
    
             pid_t pid;
    
             char buf_r[100], buf_pr[100];
    
             char* p_wbuf;
    
             int r_num, pr_num;
    
             if(pipe(pipe_fd1) < 0 || pipe(pipe_fd2) < 0)
    
             {
    
                     cout<<"Create pipe failed!"<<endl;
    
                     return -1;
    
             }
    
            
    
             if((pid = fork()) < 0)
    
             {
    
                     cout<<"Create process failed!"<<endl;
    
                     return -1;
    
             }
    
             else if(pid == 0)         //child
    
             {
    
                     close(pipe_fd1[1]);
    
                     if((r_num=read(pipe_fd1[0],buf_r,100))>0){
    
                              cout<<"Child receive : "<<buf_r<<endl;
    
                              close(pipe_fd2[0]);
    
                              if(write(pipe_fd2[1], "I receive the data", strlen("I receive the data")) < 0)
    
                                       cout<<"child write failed"<<endl;
    
                              close(pipe_fd2[1]);
    
                     }
    
                     else
    
                     {
    
                              close(pipe_fd2[0]);
    
                              if(write(pipe_fd2[1], "I cannot receive the data", strlen("I cannot receive the data")) < 0)
    
                                       cout<<"child write failed"<<endl;
    
                              close(pipe_fd2[1]);
    
                     }
    
                     close(pipe_fd1[0]);
    
                     exit(0);
    
             }
    
             else                                        //parent
    
             {
    
                     close(pipe_fd1[0]);
    
                     if(write(pipe_fd1[1], "Hello child, I am parent!", strlen("Hello child, I am parent!")) < 0)
    
                              cout<<"parent write failed"<<endl;
    
                     close(pipe_fd1[1]);
    
                     close(pipe_fd2[1]);
    
                      if((pr_num=read(pipe_fd2[0],buf_pr,100))>0){
    
                              cout<<"Parent receive : "<<buf_pr<<endl;
    
                     }
    
                     close(pipe_fd2[0]);
    
                     waitpid(pid, NULL, 0);
    
                     exit(0);
    
             }
    
             return 0;
    
    }

    (2)popen实例

    #include <iostream.h>
    
    #include <stdlib.h>
    
    #include <unistd.h>
    
    #include <stdio.h>
    
    #include <fcntl.h>
    
     
    
    #define BUFSIZE 1024
    
     
    
    int main(int argc, char *argv[])
    
    {
    
             FILE *fp = NULL;
    
             char *cmd = "ps -ef";
    
             char buf[BUFSIZE];
    
             buf[BUFSIZE]='\0';
    
             if((fp = popen(cmd, "r")) < 0)
    
             {
    
                     cout<<"popen failed!"<<endl;
    
                     return -1;
    
             }
    
             while(fgets(buf, BUFSIZE, fp) != NULL)
    
             {
    
                     cout<<buf<<endl;
    
             }
    
             pclose(fp);
    
             exit(0);
    
    }

    3.协同进程

    定义:当一个程序产生某个过滤程序的输入,同时又读取该过滤程序的输出,该过滤程序就是协同进程。

     

    图:协同进程

           实例的第一个例子中的子进程就是一个协同进程。

    三、     有名管道FIFO

    FIFO有时被称为命名管道。管道(无名管道)只能由相关进程使用,这些相关进程的共同祖先创建了管道。但是,FIFO可以使不相干的进程也能交换数据。

    FIFO其实也是一种文件类型,存在于文件系统中,所以创建FIFO类似于创建一个文件。

    当共享管道的进程执行完所有的I/O操作以后,命名管道将继续保存在文件系统中以便以后使用。

    1.函数

    Int mkfifo(const char *pathname, mode_t mode);

    头文件:#include <sys/stat.h>

    参数:mode与open函数中的mode相同,有各种权限。

    返回值:如果创建成功,则返回0;否则返回-1

    一旦已经用mkfifo创建了一个FIFO,就可以用open打开它。一般的文件操作函数都能够用于FIFO(close,read,write和unlink等)。

    当打开一个FIFO时,非阻塞标志(O_NONBLOCK)产生下列影响:

    ü  没有指定O_NONBLOCK(一般情况下),只读open要阻塞到某个进程以写的方式打开此FIFO时,同样,只写open要阻塞到某个进程以读的方式打开此FIFO。

    ü  如果指定O_NONBLOCK,则只读open立即返回;而如果没有进程已经为读打开一个FIFO,那么只写open将会出错,返回-1,errno是ENXIO。

    类似管道PIPE,如果用write写一个尚无进程为读而打开的FIFO,则产生一个SIGPIPE信号。若某个FIFO的最后一个写进程关闭该FIFO,则将为该FIFO的读进程产生一个文件结束标识。

          一个FIFO可能有多个写进程,这可能会产生数据的穿插,可以用原子写操作来避免这个问题。

    2.实例

    (1)    用FIFO复制输出流

    FIFO可被用于复制串行管道命令之间的输出流,这样就不需要写数据到临时磁盘文件中。

    大家都知道,linux管道命令只能有一个进程来接收上一个进程的输出作为输入,如果有两个以上进程需要上一个进程的输出作为输入呢?这时就可以用FIFO了。

    图:a为目标,b为通过FIFO实现目标

    # mkfifo fifo1
    
    # wc –l < fifo1 &
    
    # ls -al | tee fifo1 | grep fifo1

    注意:wc -l < fifo1(FIFO的读操作)一定要写向fifo1写操作的前面,否则会阻塞住。

    (2)    客户进程-服务器进程使用FIFO进行通信

    FIFO的另一个应用是在客户端进程和服务器进程之间传送数据。多个客户进程同时向一个FIFO中进行写操作,即向服务器进程发送请求。如果写入长度小于PIPE_BUF字节,则无需担心数据的交错;否则则需要采用原子写操作。

    图:客户进程-服务器进程使用FIFO进行通信

    这种设计是有缺陷的,一旦客户进程退出,很有可能留下一个只有写进程而没有读进程的客户专用FIFO。另外,如果服务进程只对众所周知的FIFO只读,那么,当客户进程减到0时,服务进程将读到文件结束标识,从而结束对FIFO的监听,解决办法是服务进程对众所周知的FIFO有读-写方式。

    (1)代码-一个进程向FIFO写数据,另一个进程从FIFO读数据

    write_fifo.cpp

    #include<sys/types.h>
    #include<sys/stat.h>
    #include<errno.h>
    #include<fcntl.h>
    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>
    #include <iostream.h>
    
    #define FIFO "/tmp/myfifo" 
    
    using namespace std;
    
    int main(int argc, char *argv[])
    {
             char *buf = "Hello, I am a writer for FIFO!";
             int fd;
             int nwrite = 0;
             if((mkfifo(FIFO,O_CREAT|O_EXCL) < 0) && (errno!=EEXIST))
             {
                     cout<<"Create FIFO failed and the FIFO is not existed!"<<endl;
                     return -1;
             }
    
             if((fd = open(FIFO, O_WRONLY,0777)) == -1)
             {
                     cout<<"Open failed!"<<endl;
                     cout<<"Errno is "<<errno<<endl;
                     return -1;      
             }
             if((nwrite = write(fd, buf, strlen(buf))) < 0)
             {
                     cout<<"Write Failed!"<<endl;
                     return -1;
             }
             cout<<"Write:"<<buf<<endl;
             return 0;
    }

     read_fifo.cpp

    #include<sys/types.h>
    #include<sys/stat.h>
    #include<errno.h>
    #include<fcntl.h>
    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>
    #include <iostream.h>
     
    #define FIFO "/tmp/myfifo"
    
    using namespace std;
    
    int main(int argc, char *argv[])
    {
             char buf_r[100];
             int fd;
             int nread;
            
             if((mkfifo(FIFO,O_CREAT|O_EXCL) < 0) && (errno!=EEXIST))
             {
                     cout<<"Create FIFO failed and the FIFO is not existed!"<<endl;
                     return -1;
             }
            
             if((fd = open(FIFO, O_RDONLY, 0777)) == -1)
             {
                     cout<<"Open failed!"<<endl;
                     cout<<"Errno is "<<errno<<endl;
                      return -1;     
             }
             while(1){
                     memset(buf_r, 0, sizeof(buf_r));
                     if((nread = read(fd, buf_r, 100))== -1)
                     {
                              if(errno == EAGAIN)
                                       cout<<"no data yet\n"<<endl;
                     }
                     else if(nread > 0)
                             break;
             }
             cout<<"I receive the data:"<<buf_r<<endl;
             return 0;
    }
  • 相关阅读:
    列式存储hbase系统架构学习
    使用Phoenix通过sql语句更新操作hbase数据
    分布式实时日志系统(四) 环境搭建之centos 6.4下hbase 1.0.1 分布式集群搭建
    布式实时日志系统(三) 环境搭建之centos 6.4下hadoop 2.5.2完全分布式集群搭建最全资料
    GDI+绘制五星红旗
    C#模拟登录后请求查询
    ubuntu下安装mysql
    配置nginx实现windows/iis应用负载均衡
    23种设计模式之原型模式(Prototype)
    23种设计模式之访问者模式(Visitor)
  • 原文地址:https://www.cnblogs.com/geekma/p/2588568.html
Copyright © 2011-2022 走看看