1. 程式人生 > >Linux環境程式設計--程序通訊

Linux環境程式設計--程序通訊

實驗內容

編寫程式實現程序的管道通訊。用系統呼叫pipe( )建立一管道,二個子程序P1和P2分別向管道各寫一句話:

    Child 1 is sending a message!

    Child 2 is sending a message!

父程序從管道中讀出二個來自子程序的資訊並顯示(要求先接收P1,後P2)。

實驗指導

一、什麼是管道

UNIX系統在OS的發展上,最重要的貢獻之一便是該系統首創了管道(pipe)。這也是UNIX系統的一大特色。

所謂管道,是指能夠連線一個寫程序和一個讀程序的、並允許它們以生產者—消費者方式進行通訊的一個共享檔案,又稱為pipe檔案。由寫程序從管道的寫入端(控制代碼1)將資料寫入管道,而讀程序則從管道的讀出端(控制代碼0)讀出資料。

控制代碼fd[0]

控制代碼fd[1]

 讀出端

寫入端

二、管道的型別:

1、有名管道

一個可以在檔案系統中長期存在的、具有路徑名的檔案。用系統呼叫mknod( )建立。它克服無名管道使用上的侷限性,可讓更多的程序也能利用管道進行通訊。因而其它程序可以知道它的存在,並能利用路徑名來訪問該檔案。對有名管道的訪問方式與訪問其他檔案一樣,需先用open( )開啟。

2、無名管道

一個臨時檔案。利用pipe( )建立起來的無名檔案(無路徑名)。只用該系統呼叫所返回的檔案描述符來標識該檔案,故只有呼叫pipe( )的程序及其子孫程序才能識別此檔案描述符,才能利用該檔案(管道)進行通訊。當這些程序不再使用此管道時,核心收回其索引結點。

二種管道的讀寫方式是相同的,本文只講無名管道。

3、pipe檔案的建立

分配磁碟和記憶體索引結點、為讀程序分配檔案表項、為寫程序分配檔案表項、分配使用者檔案描述符

4、讀/寫程序互斥

核心為地址設定一個讀指標和一個寫指標,按先進先出順序讀、寫。

為使讀、寫程序互斥地訪問pipe檔案,需使各程序互斥地訪問pipe檔案索引結點中的直接地址項。因此,每次程序在訪問pipe檔案前,都需檢查該索引檔案是否已被上鎖。若是,程序便睡眠等待,否則,將其上鎖,進行讀/寫。操作結束後解鎖,並喚醒因該索引結點上鎖而睡眠的程序。

三、所涉及的系統呼叫   

1、pipe( )

建立一無名管道。

系統呼叫格式

              pipe(filedes)

引數定義

int  pipe(filedes);

int  filedes[2];

其中,filedes[1]是寫入端,filedes[0]是讀出端。

該函式使用標頭檔案如下:

#include <unistd.h>

#inlcude <signal.h>

#include <stdio.h>

   2、read( )

  系統呼叫格式

                  read(fd,buf,nbyte)

  功能:從fd所指示的檔案中讀出nbyte個位元組的資料,並將它們送至由指標buf所指示的緩衝區中。如該檔案被加鎖,等待,直到鎖開啟為止。

  引數定義

                  int  read(fd,buf,nbyte);

                  int  fd;

                  char *buf;

                  unsigned  nbyte;

  3、write( )

系統呼叫格式

                  read(fd,buf,nbyte)

功能:把nbyte 個位元組的資料,從buf所指向的緩衝區寫到由fd所指向的檔案中。如檔案加鎖,暫停寫入,直至開鎖。

引數定義同read( )。

〈任務〉

    編制一段程式,實現程序的管道通訊。使用系統呼叫pipe()建立一條管道線。兩個子程序p1和p2分別向通道個寫一句話:

  child1 process is sending message!

child2 process is sending message!

而父程序則從管道中讀出來自兩個程序的資訊,顯示在螢幕上。

程式一:

#include<unistd.h>
#include<sys/types.h>
#include<errno.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.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 writel 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);
}
}

效果:

parent writel success!
parent write2 success!
10 numbers read from the pipe is Hellopipe

更復雜的例子,程式二:

#include <unistd.h>
#include <signal.h>
#include <stdio.h>
#include<string.h>
int pid1,pid2;
 
main( )
{ 
int fd[2];
char outpipe[100],inpipe[100];
pipe(fd);                       /*建立一個管道*/
while ((pid1=fork( ))==-1);
/*lockf()函式允許將檔案區域用作訊號量(監視鎖),或用於控制對鎖定程序的訪問(強制模式記錄鎖定)。試圖訪問已鎖定資源的其他程序將返回錯誤或進入休眠狀態,直到資源解除鎖定為止。當關閉檔案時,將釋放程序的所有鎖定,即使程序仍然有開啟的檔案。當程序終止時,將釋放程序保留的所有鎖定。*/
if(pid1==0)
  {
lockf(fd[1],1,0);
    sprintf(outpipe,"child 1 process is sending message!"); 
	/*把串放入陣列outpipe中*/
    write(fd[1],outpipe,50);     /*向管道寫長為50位元組的串*/
    sleep(5);                 /*自我阻塞5秒*/
    lockf(fd[1],0,0);
    exit(0);
   }
else
  {
while((pid2=fork( ))==-1);
    if(pid2==0)
{ 
lockf(fd[1],1,0);           /*互斥*/
        sprintf(outpipe,"child 2 process is sending message!");
        write(fd[1],outpipe,50);
        sleep(5);
        lockf(fd[1],0,0);
        exit(0);
     }
     else
     {  
wait(0);              /*同步*/
         read(fd[0],inpipe,50);   /*從管道中讀長為50位元組的串*/
         printf("%s\n",inpipe);
         wait(0);
         read(fd[0],inpipe,50);
         printf("%s\n",inpipe);
        exit(0);
    }
  }
}

執行結果:

child 1 process is sending message!
child 2 process is sending message!