进程与进程间的通信,进程间通讯机制

  进程与进程间的通信,进程间通讯机制

  到目前为止,我们只能在两个相关的程序之间传输数据,即由一个共同的祖先进程启动的程序。这通常不太方便,因为我们希望不相关的进程也能交换数据。

  我们可以用FIFO来实现这个操作,通常称为命名管道。命名管道是一种特殊的文件类型,在文件系统中作为名称存在(记住,在Linux中一切都是文件),但它的行为类似于我们已经知道的未知管道。

  我们可以通过命令行或在程序中创建命名管道。出于历史原因,用于创建命名管道的命令是mknod:

  $ mknod文件名p

  但是,mknod命令不在X/Open命令列表中,因此它在所有类似Unix的系统上都不可用。更好的命令行方法是使用

  $ mkfifo文件名

  在程序内部,我们可以使用两个不同的命令:

  #包含sys/types.h

  #包含系统/统计信息

  int mkfifo(const char *filename,mode _ t mode);

  int mknod(const char *filename,mode_t mode S_IFIFO,(dev _ t)0);

  类似于mknod命令,我们可以使用mknod函数创建许多特殊的文件类型。使用dev_t类型的0值和使用S _ IFIFO的或文件访问模型是创建命名管道函数的唯一可移植方法。在我们的例子中,我们将使用简化的mkfifo。

  测试-创建一个著名的管道。

  作为fifo1.c,我们只需要输入以下代码:

  #包含stdio.h

  #包含stdlib.h

  #包含字符串. h

  #包含sys/types.h

  #包含系统/统计信息

  int main()

  {

  int res=mkfifo(/tmp/my_fifo ,0777);

  if (res==0)

  printf(FIFO已创建/n );

  退出(EXIT _ SUCCESS);

  }

  我们可以使用以下命令查看该管道:

  $ ls -lF /tmp/my_fifo

  prw xr-xr-x 1 Rick users 0 7月10日14:55 /tmp/my_fifo

  注意,输出的第一个字符是p,表示它是一个管道。下面的是由ls命令的-F选项添加的,这也表明它是一个管道。

  使用程序的mkfifo功能创建一个特殊文件。虽然我们请求了0777模式,但是这个模式被用户掩码设置修改了,就像在普通文件创建中一样,所以最终的文件模式是755。如果我们的umask设置不同,比如0002,我们会看到不同的权限。

  我们可以通过使用rm命令或者在程序中使用unlink系统调用来删除FIFO,就像删除一个普通文件一样。

  访问FIFO

  众所周知的管道的一个非常有用的特性是,因为它们出现在文件系统中,所以我们可以像通常使用文件名一样使用它们。在使用我们为更多编程而创建的FIFO之前,我们可以使用常见的文件命令来探索FIFO文件的行为。

  实验-访问FIFO文件

  1.首先,让我们试着读取(空的)FIFO文件:

  $ cat /tmp/my_fifo

  现在尝试将数据写入FIFO。我们需要使用不同的终端,因为第一个终端现在被挂起,等待数据出现在FIFO中。

  $ echo sdsdfasdf /tmp/my_fifo

  我们可以看到cat命令输出的结果。如果我们不向FIFO发送任何数据,cat命令将挂起,直到我们中断它,通常使用Ctrl C。

  3通过将第一个命令放在后端,我们可以同时执行两个操作:

  $ cat /tmp/my_fifo

  [1] 1316

  $ echo "sdsdfasdf" /tmp/my_fifo

  sdsdfasdf

  [1]完成cat /tmp/my_fifo

  $

  因为FIFO中没有数据,所以cat和echo程序会阻塞,分别等待数据到达和一个进程读取数据。

  看第三步。cat进程最初在后台被阻塞。当echo命令使一些数据可用时,cat命令读取数据并将其输出到标准输出。请注意,cat命令将立即退出,而不会等待更多数据。他没有阻塞,因为第二个命令把数据完全放入FIFO后管道就会关闭,所以cat程序中的read调用会返回0,表示文件结束。

  现在我们已经看到了命令行程序FIFO是如何工作的,让我们学习更多关于程序接口的知识,它允许我们在访问FIFO时对读和写执行更多的控制。

  注意:与管道调用创建的管道不同,FIFO是作为一个命名文件存在的,而不是一个打开的文件描述符,它必须在被读写后才能打开。当我们操作文件时,我们可以通过打开和关闭函数来打开和关闭FIFO。Open调用需要传递FIFO的路径名,而不是常规文件。

  带Open的Open FIFO

  打开FIFO的主要限制是使用O_RDWR模型的程序可能会也可能不会打开一个FIFO进行读写。如果一个程序这样做,结果是不确定的。这是一个合理的限制,因为通常我们只使用FIFO单向传输数据,所以不需要O _ RDWR模型。该进程将读取自己的输出。

  如果我们想在程序之间进行双向数据传输,最好使用一对FIFO或管道,每个管道用于一个方向,或者通过关闭和重新打开FIFO来改变数据流的方向。我们将在本章后面讨论双向数据交换。

  打开FIFO和打开常规文件的另一个区别是带有O_NONBLOCK选项的open标志(open的第二个参数)。使用这种open模式不仅会改变open调用的处理方式,还会改变对返回的文件描述符的读写请求的处理方式。

  O_RDONLY、O_WRONLY和O_NONBLOCK标记有四种合法的组合。我们将依次考虑每一个问题。

  open(const char *path,O _ rd only);

  在这种情况下,open调用将阻塞;直到一个进程打开同一个FIFO进行写操作,这个调用才会返回。这种情况类似于前面的cat示例。

  open(const char *path,O _ rd only O _ non block);

  open调用将成功并立即返回,即使FIFO没有被任何进程打开以写入数据。

  open(const char *path,O _ wr only);

  在这种情况下,open调用将阻塞,直到一个进程打开同一个FIFO进行读取。

  open(const char *path,O _ WRONLY O _ non block);

  这个调用会立即返回,但是如果没有进程打开FIFO进行读取,open会返回error -1,FIFO不会被打开。如果一个进程打开FIFO进行读取,那么返回的文件描述符可以用来写FIFO。

  注意:O_NONBLOCK与使用O_RDONLY和O_WRONLY的区别在于,在非阻塞情况下,如果没有进程打开管道进行读操作,那么写操作的open调用会失败,但非阻塞读操作调用不会失败。O_NONBLOCK标志不影响关闭调用的行为。

  测试-打开FIFO文件

  让我们来看看如何使用open with O_NONBLOCK标签同步两个进程。我们将编写一个测试程序fifo2.c,而不是使用多个示例程序,它将让我们通过传递不同的参数来讨论fifo的行为。

  1我们从头文件和#define开始,检查所提供的命令行参数的正确数量:

  #包含stdio.h

  #包含stdlib.h

  #包含字符串. h

  #包括unistd.h

  #包含fcntl.h

  #包含sys/types.h

  #包含系统/统计信息

  #define FIFO_NAME /tmp/my_fifo

  int main(int argc,char **argv)

  {

  int res

  int open _ mode=0;

  if (argc 2)

  {

  fprintf(stderr,用法:% s O _ RDONLY O _ WRONLY O _ non block/n ,* argv);

  退出(EXIT _ FAILURE);

  }

  2假设程序传递了正确的参数,我们现在通过这些参数设置open_mode的值:

  argv

  if (strncmp(*argv, O_RDONLY ,8)==0)open _ mode =O _ rd only;

  if (strncmp(*argv, O_WRONLY ,8)==0)open _ mode =O _ wr only;

  if (strncmp(*argv, O_NONBLOCK ,10)==0)open _ mode =O _ non block;

  argv

  if(*argv)

  {

  if(strncmp(*argv, O_RDONLY ,8)==0)open _ mode =O _ rd only;

  if(strncmp(*argv, O_WRONLY ,8)==0)open _ mode =O _ wr only;

  if(strncmp(*argv, O_NONBLOCK ,10)==0)open _ mode =O _ non block;

  }

  我们现在检查FIFO是否存在,如果需要,我们将创建它。然后,FIFO将被打开,并产生一个输出。FIFO终于要关闭了。

  if (access(FIFO_NAME,F_OK)==-1)

  {

  res=mkfifo(FIFO_NAME,0777);

  if(res!=0)

  {

  fprintf(stderr,无法创建fifo %s/n ,FIFO _ NAME);

  退出(EXIT _ FAILURE);

  }

  }

  printf(Process %d open FIFO/n ,getpid());

  res=open(FIFO_NAME,open _ mode);

  printf(进程%d结果%d/n ,getpid(),RES);

  睡眠(5);

  if(res!=-1)(void)close(RES);

  printf(Process %d finished/n ,getpid());

  退出(EXIT _ SUCCESS);

  }

  这个程序允许我们在命令行中指定O_RDONLY、O_WRONLY和O_NONBLOCK的组合。通过比较已知字符串和命令行参数,如果字符串匹配,将设置相应的标志。这个程序使用access函数来检查FIFO是否已经存在,如果需要,它会创建这个文件。

  我们没有破坏FIFO,因为没有办法判断其他程序是否在使用FIFO。

  O _ RDONLY和O_WRONLY与O_NONBLOCK

  现在我们有了测试程序,让我们尝试一些组合;请注意,我们将文件程序放在后台:

  $ ./FIFO O _ r仅

  [1] 152

  进程152打开FIFO

  $ ./FIFO O _ wr only

  过程153打开FIFO

  过程152结果3

  流程153结果3

  过程152结束

  过程153完成

  这是众所周知的管道的最常见用法。他将使用read进程在open调用中启动并等待,然后当第二个程序打开FIFO时,两个程序都将继续。请注意,当调用open时,读取过程和写入过程是同步的。

  注意:当一个Linux进程被阻塞时,它不会消耗CPU资源,所以这种进程同步的方法非常有效。

  O _ RDONLY和O_WRONLY与O_NONBLOCK

  这一次,读取过程将执行open调用并立即继续,尽管没有写入过程。写过程也将在open调用后立即继续,因为FIFO已经打开用于读取。

  $ ./FIFO O _ r only O _ non block

  [1] 160

  过程160打开FIFO

  $ ./FIFO O _ wr only

  进程161打开FIFO

  过程160结果3

  过程161结果3

  过程160结束

  过程161完成

  [1]Done FIFO O _ r only O _ non block

  这两个例子是开放模式最常见的组合。我们可以用一些其他的组合来试验这个程序。

进程与进程间的通信,进程间通讯机制