Linux管道通信

来源:互联网 发布:cms程序 编辑:程序博客网 时间:2024/06/05 06:40

现在在Linux 中使用较多的进程间通信方式主要有以下几种。
    (1)管道 (Pipe )及有名管道 (named pipe ):管道可用于具有亲缘关系进程间的通信,有名管道,除具有管道所具有的功能外,它还允许无亲缘关系进程间的通信。
    (2)信号(Signal):信号是在软件层次上对中断机制的一种模拟,它是比较复杂的通信方式,用于通知接受进程有某事件发生,一个进程收到一个信号与处理器收到一个中断请求 效果上可以说是一样的。
    (3)消息队列:消息队列是消息的链接表,包括Posix 消息队列systemV 消息队列。它克服了前两种通信方式中信息量有限的缺点,具有写权限的进程可以向消息队列中按照一定的规则添加新消息;对消息队列有读权限的进程则可以从消息队列中读取消息。
    (4)共享内存:可以说这是最有用的进程间通信方式。它使得多个进程可以访问同一块内存空间,不同进程可以及时看到对方进程中对共享内存中数据的更新。这种通信方式需要依靠某种同步机制,如互斥锁和信号量等。
    (5)信号量:主要作为进程间以及同一进程不同线程之间的同步手段。
    (6)套接字(Socket):这是一种更为一般的进程间通信机制,它可用于不同机器之间的进程间通信,应用非常广泛。 


    管道是Linux 中进程间通信的一种方式。这里所说的管道主要指无名管道,它具有如下特点:
       它只能用于具有亲缘关系的进程之间的通信(也就是父子进程或者兄弟进程之间)。
       它是一个半双工的通信模式,具有固定的读端和写端。
       管道也可以看成是一种特殊的文件,对于它的读写也可以使用普通的read、write等函数。但是它不是普通的文件,并不属于其他任何文件系统,并且只存在于内存中。

把子进程的写端fd[1] 和父进程的读端fd[0]关闭。这时,父子进程之间建立起了一条“子进程读父进程写”的通道。

#include <unistd.h>
#include <sys/types.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>

int main()
{
 int pipe_fd[2];
 pid_t pid;
 char buf_r[100];
 char* p_wbuf;
 int r_num;
 memset(buf_r,0,sizeof(buf_r));
 if(pipe(pipe_fd)<0)
 {
 printf("pipe create error\n");
 return -1;
 }
 if((pid=fork())==0)
 {
  printf("\n");
  close(pipe_fd[1]);
  sleep(2);
  if((r_num=read(pipe_fd[0],buf_r,100))>0){
   printf(   "%d numbers read from the pipe is %s\n",r_num,buf_r);
  } 
  close(pipe_fd[0]);
  exit(0);
   }
 else if(pid>0)
 {
  close(pipe_fd[0]);
  if(write(pipe_fd[1],"Hello",5)!=-1)
   printf("parent write1 success!\n");
  if(write(pipe_fd[1]," Pipe",5)!=-1)
   printf("parent write2 success!\n");
  close(pipe_fd[1]);
  sleep(3);
  waitpid(pid,NULL,0);
  exit(0);
 }
}

    管道读写注意点

       只有在管道的读端存在时向管道中写入数据才有意义。否则,向管道中写入数据的进程将收到内核传来的SIFPIPE 信号 (通常Broken pipe 错误)。
       向管道中写入数据时,linux 将不保证写入的原子性,管道缓冲区一有空闲区域,写进程会试图向管道写入数据。如果读进程不读取管道缓冲区中的数据,那么写操作将会一直阻塞。
       父子进程在运行时,它们的先后次序并不能保证,因此,在这里为了保证父进程已经关闭了读描述符,可在子进程中调用sleep 函数。 


    有名管道说明

   前面介绍的管道是无名管道,它只能用于具有亲缘关系的进程之间,这就大大地限制了管道的使用。有名管道的出现突破了这种限制,它可以使互不相关的两个进程实现彼此通信。该管道可以通过路径名来指出,并且在文件系统中是可见的。在建立了管道之后,两个进程可以把它当作普通文件一样进行读写操作,使用非常方便。不过值得注意的是,FIFO 是严格地遵循先进先出规则的,对管道及FIFO 的读总是从开始处返回数据,对它们的写则把数据添加到末尾,它们不支持如lseek()等文件定位操作。
    有名管道的创建可以使用函数mkfifo(),该函数类似文件中的open()操作,可以指定管道的路径和打开的模式。

    用户还可以在命令行使用“ mknod 管道名 P”来创建有名管道。

   在创建管道成功之后, 可以使用open、read、write 这些函数了。与普通文件的开发设置一样,对于为读而打开的管道可在open中设置O_RDONLY,对于为写而打开的管道可在open中设置O_WRONLY,在这里与普通文件不同的是阻塞问题。由于普通文件的读写时不会出现阻塞问题,而在管道的读写中却有阻塞的可能,这里的阻塞标志可以在open 函数中设定为O_NONBLOCK。下面分别对阻塞打开和 阻塞打开的读写进行一定的讨论。

    对于读进程
       若该管道是阻塞打开,且当前FIFO内没有数据,则对读进程而言将一直阻塞直到有数据写入。
       若该管道是 阻塞打开,则不论FIFO内是否有数据,读进程都会立即执行读操作。
    对于写进程
       若该管道是阻塞打开,则写进程而言将一直阻塞直到有读进程读出数据。
       若该管道是 阻塞打开,则当前FIFO内没有读操作,写进程都会立即执行读操作。


    下面的实例包含了两个程序,一个用于读管道,另一个用于写管道。其中在写管道的程序里创建管道,并且作为main 函数里的参数由用户输入要写入的内容。读管道读出了用户写入管道的内容,这两个函数用的是 阻塞读写管道。

写管道:
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define FIFO_SERVER "/tmp/myfifo"

main(int argc,char** argv)
{
 int fd;
 char w_buf[100];
 int nwrite;
 if(fd==-1)
  if(errno==ENXIO)
   printf("open error; no reading process\n");
 fd=open(FIFO_SERVER,O_WRONLY|O_NONBLOCK,0);
 if(argc==1)
  printf("Please send something\n");
 strcpy(w_buf,argv[1]);
 if((nwrite=write(fd,w_buf,100))==-1)
 {
  if(errno==EAGAIN)
   printf("The FIFO has not been read yet.Please try later\n");
 }
 else
  printf("write %s to the FIFO\n",w_buf);
}


读管道:
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define FIFO "/tmp/myfifo"

main(int argc,char** argv)
{
 char buf_r[100];
 int  fd;
 int  nread;
 if((mkfifo(FIFO,O_CREAT|O_EXCL)<0)&&(errno!=EEXIST))
  printf("cannot create fifoserver\n");
 printf("Preparing for reading bytes...\n");
 memset(buf_r,0,sizeof(buf_r));
 fd=open(FIFO,O_RDONLY|O_NONBLOCK,0);
 if(fd==-1)
 {
  perror("open");
  exit(1); 
 }
 while(1)
 {
  memset(buf_r,0,sizeof(buf_r));
  if((nread=read(fd,buf_r,100))==-1){
   if(errno==EAGAIN)
    printf("no data yet\n");
  }
  printf("read %s from FIFO\n",buf_r);
  sleep(1);
 } 
 pause();
 unlink(FIFO);
}