IPC和管道簡介
 
本文出自:http://www.china-pub.com 作者: 黃浩 (2001-08-08 09:00:00)

1 Interprocess Communication (IPC)和管道Pipes 

  在UNIX的內核環境中,要解決的一個首要問題是:如何控制和處理不同進程之間的通信和數據交換。
本章中我們將通過研究一個簡單的實例,看看在同一臺機器的UNIX環境下多個進程是如何運行和被我們控制的
(使用fork()方法)。 
  能夠實現進程間通信的方法有:
  · Pipes 
  · Signals 
  · Message Queues 
  · Semaphores 
  · Shared Memory 
  · Sockets 
  本文先學習如何使用Pipes 方法來實現兩個進程間的通信,而其它進程間通信的方法我們將在接下來的文章
中詳細討論。
  在UNIX環境下可以使用兩種方式打開一個管道:Formatted Piping方式和Low Level Piping方式。 

1.1 popen() -- Formatted Piping 

  FILE *popen(char *command, char *type)
  描述了打開一個I/O管道的方法。
  其中command參數描述創建管道的進程,type參數描述了管道打開的類型:"r"表示以讀方式打開,"w"表示以
寫方式打開。
  popen()的返回值是一個指針流或NULL指針(出現錯誤時)。
  使用popen()方法打開的管道,在使用完畢后必須用pclose(FILE *stream)方法關閉。

  用戶界面可以通過fprintf()fscanf()方法來實現和管道的通信。

1.2 pipe() -- Low level Piping 

  int pipe(int fd[2]) 
  將創建一個管道和兩個文件描述符:fd[0], fd[1]
  其中fd[0] 文件描述符將用于讀操作,而fd[1] 文件描述符將用于寫操作。 
  pipe()的成功返回值是0,如果創建失敗將返回-1并將失敗原因記錄在errno中。 
  使用int pipe(int fd[2])創建管道的標準編程模式如下:
  1 創建管道;
  2 使用fork( )方法創建兩個(或多個)相關聯的進程;
  3 使用read()write()方法操作管道;
  4 管道使用完畢后用close(int fd)方法關閉管道。

  下一段程序中使用了該種Low Level Piping的方法,實現了父進程對子進程的寫操作:

  int pdes[2];
  pipe(pdes);
  if ( fork() == 0 )
  {/* child */
  close(pdes[1]); /* not required */
  read( pdes[0]); /* read from parent */
  .....
  }
  else
  { close(pdes[0]); /* not required */
  write( pdes[1]); /* write to child */
  .....
  }

1.4 
應用實例分析

  本節提供了一個完整的管道應用實例,其結構說明如下: 
  1 實例含有兩個程序模塊plot.c (主程序)plotter.c 
  2 程序運行在Solaris2.6環境下并必須預先安裝了GNU的免費畫圖軟件gnuplot 在以下目錄:/usr/local/bin/
  3 程序plot.c調用gnuplot 
  4 Plot將產生兩個數據流: 
  y = sin(x) 
  y = sin(1/x) 
  5 程序將創建兩個管道:每個數據流對應一個管道。
  本實例在Solaris2.6UNIX環境下調試通過。

plot.c
程序的源代碼如下:
/* plot.c - example of unix pipe. Calls gnuplot graph drawing package to draw
graphs from within a C program. Info is piped to gnuplot */
/* Creates 2 pipes one will draw graphs of y=0.5 and y = random 0-1.0 */
/* the other graphs of y = sin (1/x) and y = sin x */

/* Also user a plotter.c module */
/* compile: cc -o plot plot.c plotter.c */

#include "externals.h"
#include 
#define DEG_TO_RAD(x) (x*180/M_PI)

double drand48();
void quit();
FILE *fp1, *fp2, *fp3, *fp4, *fopen();

main()
{ float i;
float y1,y2,y3,y4;

/* open files which will store plot data */
if ( ((fp1 = fopen("plot11.dat","w")) == NULL) ||
((fp2 = fopen("plot12.dat","w")) == NULL) ||
((fp3 = fopen("plot21.dat","w")) == NULL) ||
((fp4 = fopen("plot22.dat","w")) == NULL) )
{ printf("Error can't open one or more data files\n");
exit(1);
}

signal(SIGINT,quit); /* trap ctrl-c call quit fn */
StartPlot();
y1 = 0.5;
srand48(1); /* set seed */
for (i=0;;i+=0.01) /* increment i forever use ctrl-c to quit prog */
{ y2 = (float) drand48();
if (i == 0.0)
y3 = 0.0;
else
y3 = sin(DEG_TO_RAD(1.0/i));
y4 = sin(DEG_TO_RAD(i));

/* load files */
fprintf(fp1,"%f %f\n",i,y1);
fprintf(fp2,"%f %f\n",i,y2);
fprintf(fp3,"%f %f\n",i,y3);
fprintf(fp4,"%f %f\n",i,y4);
/* make sure buffers flushed so that gnuplot */
/* reads up to data file */ 
fflush(fp1);
fflush(fp2);
fflush(fp3);
fflush(fp4);

/* plot graph */
PlotOne();
usleep(250); /* sleep for short time */
}
}

void quit()
{ printf("\nctrl-c caught:\n Shutting down pipes\n");
StopPlot();

printf("closing data files\n");
fclose(fp1);
fclose(fp2);
fclose(fp3);
fclose(fp4);
printf("deleting data files\n");
RemoveDat();
}
The plotter.c module is as follows: 
/* plotter.c module */
/* contains routines to plot a data file produced by another program */
/* 2d data plotted in this version */
/**********************************************************************/
#include "externals.h"

static FILE *plot1,
*plot2,
*ashell;

static char *startplot1 = "plot [] [0:1.1]'plot11.dat' with lines, 
'plot12.dat' with lines\n";
static char *startplot2 = "plot 'plot21.dat' with lines, 
'plot22.dat' with lines\n";
static char *replot = "replot\n";
static char *command1= "/usr/local/bin/gnuplot> dump1";
static char *command2= "/usr/local/bin/gnuplot> dump2";
static char *deletefiles = "rm plot11.dat plot12.dat plot21.dat plot22.dat";
static char *set_term = "set terminal x11\n";

void
StartPlot(void)
{ plot1 = popen(command1, "w");
fprintf(plot1, "%s", set_term);
fflush(plot1);
if (plot1 == NULL)
exit(2);
plot2 = popen(command2, "w");
fprintf(plot2, "%s", set_term);
fflush(plot2);
if (plot2 == NULL)
exit(2);
}
void 
RemoveDat(void)
{ ashell = popen(deletefiles, "w");
exit(0);
}
void
StopPlot(void)
{ pclose(plot1);
pclose(plot2);
}
void
PlotOne(void)
{ fprintf(plot1, "%s", startplot1);
fflush(plot1);
fprintf(plot2, "%s", startplot2);
fflush(plot2);
}
void
RePlot(void)
{ fprintf(plot1, "%s", replot);
fflush(plot1);
}
The header file externals.h contains the following: 
/* externals.h */
#ifndef EXTERNALS
#define EXTERNALS

#include 
#include 
#include 

/* prototypes */

void StartPlot(void);
void RemoveDat(void);
void StopPlot(void);
void PlotOne(void);
void RePlot(void);
#endif 


作者的電子郵件地址是:vong@21cn.com