进程间通信实例(IPC)之命名管道

  在linux进程是系统调度的基本单位,一个进程完成一定的任务,但进程在执行任务时有时也需要其他进程提供信息,我们把它称为进程之间的通信。那进程是采取何种方式来进行通信呢?带着这个问题我们来学习吧!
进程间的通信分有很多方式,今天来让我们看看进程是如何采用管道来进行通信的。

为此我们写了一段简单的代码来进行说明,毕竟用代码描述起来更容易使人明白,我们将会对下面的代码进行分析以来阐明其中的道理。
一.首先进行名词解释:
a.pipe:管道,常用"|" 进行表示,我们可以理解成这样 "======",一段长长的管子,一段输入,一段输出,进程
1在左边输入后,进程2就可以在右边取出,这样就实现了进程1和进程2之间的通信,呵呵,是不是很简单!

b.FIFO的意思是命名管道。因为普通的管道pipe在进程中创建后会随着进程的消亡而消亡,但命名管道不会,它建
立后会永久存在,除非是你把它给删除了。

二.源代码分析
我们主要以注释的形式来分析代码

// parentchildfifo.c
// =======================================================================
#include  < errno.h >
#include 
< fcntl.h >
#include 
< stdio.h >
#include 
< string .h >
#include 
< unistd.h >
#include 
< sys / types.h >
#include 
< sys / wait.h >
#include 
< sys / stat.h >
#include 
" dofifo.h "
int  main( int  argc,  char   * argv[])
{
  pid_t childpid;

  
if (argc != 2{
    fprintf(stderr, 
"Usage: %s pipename ", argv[0]);
    
return -1;
  }

  
// 创建命名管道管道的名字就是你输入的参数argv[1]
  if (mkfifo(argv[1], FIFO_PERM) == -1{
    
if (errno != EEXIST) {
      fprintf(stderr, 
"[%ld]: failed to create named pipe %s: %s ",
              (
long)getpid(), argv[1], strerror(errno));
      
return -1;
    }

  }

  
  
// 创建2个进程,即父进程和子进程
  if ((childpid = fork()) == -1{
    perror(
"Failed to fork");
    
return -1;
  }


  
// 在下面argv[1],就是我们在上面建立的命名管道
  if (childpid == 0)
    
// 子进程走这个分支 ,子进程向命名管道写入了"this was written by the child"字符串
    return dofifochild(argv[1], "this was written by the child");
  
else 
    
// 父进程走这个分支 ,父进程在子进程写完后读出那个字符串
    return dofifoparent(argv[1]);
}

// =======================================================================

/*
 *可见正如以上描述,子进程在管道的左边写入,而父进程在管道的右边读出,这样确实实现了进程之间
 *的通讯。接着我们来看看dofifochild()和dofifoparent又是如何实现的。
 
*/


// dofifo.c
// =======================================================================
#include  < errno.h >
#include 
< fcntl.h >
#include 
< stdio.h >
#include 
< string .h >
#include 
< unistd.h >
#include 
< sys / types.h >
#include 
< sys / stat.h >

#define  BUFSIZE 256

//  子进程的实现代码
int  dofifochild( const   char   * fifoname,  const   * idstring)
{
  
char buf[BUFSIZE];
  
int fd;
  
int rval;
  ssize_t strsize;

  fprintf(stderr, 
"[%ld]:(child) about to open FIFO %s... ",
          (
long)getpid(), fifoname);

  
// 打开命名管道,如果被信号中断则再次打开,知道成功为止
  while (((fd = open(fifoname, O_WRONLY)) == -1&& (errno == EINTR)) ;
  
if (fd == -1{
    fprintf(stderr, 
"[%ld]:failed to open named pipe %s for write:%s ",
            (
long)getpid(), fifoname, strerror(errno));
    
return -1;
  }

  
// 生成要写入管道的字符串,即上面的"this was written by the child"加上自己的pid
  rval = snprintf(buf, BUFSIZE, "[%ld]:%s ", (long)getpid(), idstring);
  
if (rval < 0{
    fprintf(stderr, 
"[%ld]:failed to make the string: ", (long)getpid());
    
return -1;
  }

  strsize 
= strlen(buf)+1;
  fprintf(stderr, 
"[%ld]:about to write... ", (long)getpid());
  
// 向命名管道写入上面生成的字符串
  rval = write(fd, buf, strsize);
  
if (rval != strsize) {
    fprintf(stderr, 
"[%ld]:failed to write to pipe:%s  ",
            (
long)getpid(), strerror(errno));
    
return -1;
  }


  fprintf(stderr, 
"[%ld]:finishing... ", (long)getpid());
  
return 0;

}

// 父进程的实现代码
int  dofifoparent( const   char   * fifoname)
{
  
char buf[BUFSIZE];
  
int fd;
  
int rval;

  fprintf(stderr, 
"[%ld]:(parent)about to open FIFO %s... ",
         (
long)getpid(), fifoname);
  
// 打开命名管道,如果被信号中断则再次打开,知道成功为止  
  while(((fd = open(fifoname, O_RDONLY))) == -1 && (errno == EINTR)) ;
  
if (fd == -1{
    fprintf(stderr, 
"[%ld]:failed to open named pipe %s for read: %s ",
            (
long)getpid(), fifoname, strerror(errno));
    
return -1;
  }


  fprintf(stderr, 
"[%ld]:about to read... ", (long)getpid());

  
//读出子进程写入管道的字符串
  rval = read(fd, buf, BUFSIZE);
  
if (rval == -1{
    fprintf(stderr, 
"[%ld]:failed to read from pipe: %s ",
            (
long)getpid(), strerror(errno));
    
return -1;
  }


  fprintf(stderr, 
"[%ld]:read %.*s ", (long)getpid(), rval, buf);
  
return 0;
}

// =======================================================================

//  dofifo.h
#ifndef __DO_FILO__
#define  __DO_FILO__

int  dofifochild( const   char   * fifoname,  const   char   * idstring);
int  dofifoparent( const   char   * fifoname);

#endif


至此我们就知道了进程之间的通信方法。如果你还是不太了解,那就拷贝代码来实践吧!看看结果相信你就会明白的!

你可能感兴趣的:(嵌入式Linux)