linux下程序間通訊的幾種主要手段:

  1. 管道(Pipe)及有名管道(named pipe):管道可用於具有親緣關係程序間的通訊,有名管道克服了管道沒有名字的限制,因此,除具有管道所具有的功能外,它還允許無親緣關係程序間的通訊;
  2. 訊號(Signal):訊號是比較複雜的通訊方式,用於通知接受程序有某種事件發生,除了用於程序間通訊外,程序還可以傳送訊號給程序本身;linux除了支援Unix早期訊號語義函式sigal外,還支援語義符合Posix.1標準的訊號函式sigaction(實際上,該函式是基於BSD的,BSD為了實現可靠訊號機制,又能夠統一對外介面,用sigaction函式重新實現了signal函式);
  3. 報文(Message)佇列(訊息佇列):訊息佇列是訊息的連結表,包括Posix訊息佇列system V訊息佇列。有足夠許可權的程序可以向佇列中新增訊息,被賦予讀許可權的程序則可以讀走佇列中的訊息。訊息佇列克服了訊號承載資訊量少,管道只能承載無格式位元組流以及緩衝區大小受限等缺點。
  4. 共享記憶體:使得多個程序可以訪問同一塊記憶體空間,是最快的可用IPC形式。是針對其他通訊機制執行效率較低而設計的。往往與其它通訊機制,如訊號量結合使用,來達到程序間的同步及互斥。
  5. 訊號量(semaphore):主要作為程序間以及同一程序不同執行緒之間的同步手段。
  6. 套介面(Socket):更為一般的程序間通訊機制,可用於不同機器之間的程序間通訊。起初是由Unix系統的BSD分支開發出來的,但現在一般可以移植到其它類Unix系統上:Linux和System V的變種都支援套接字。

本文講述程序間通訊方法——管道及命名管道

1、 管道概述及相關API應用

1.1 管道相關的關鍵概念

管道是Linux支援的最初Unix IPC形式之一,具有以下特點:

  • 管道是半雙工的,資料只能向一個方向流動;需要雙方通訊時,需要建立起兩個管道;
  • 只能用於父子程序或者兄弟程序之間(具有親緣關係的程序);
  • 單獨構成一種獨立的檔案系統:管道對於管道兩端的程序而言,就是一個檔案,但它不是普通的檔案,它不屬於某種檔案系統,而是自立門戶,單獨構成一種檔案系統,並且只存在與記憶體中。
  • 資料的讀出和寫入:一個程序向管道中寫的內容被管道另一端的程序讀出。寫入的內容每次都新增在管道緩衝區的末尾,並且每次都是從緩衝區的頭部讀出資料。

1.2管道的建立:

#include <unistd.h>
int pipe(int fd[2])

該函式建立的管道的兩端處於一個程序中間,在實際應用中沒有太大意義,因此,一個程序在由pipe()建立管道後,一般再fork一個子程序,然後通過管道實現父子程序間的通訊(因此也不難推出,只要兩個程序中存在親緣關係,這裡的親緣關係指的是具有共同的祖先,都可以採用管道方式來進行通訊)。

1.3管道的讀寫規則:

管道兩端可分別用描述字fd[0]以及fd[1]來描述,需要注意的是,管道的兩端是固定了任務的。即一端只能用於讀,由描述字fd[0]表示,稱其為管道讀端;另一端則只能用於寫,由描述字fd[1]來表示,稱其為管道寫端。如果試圖從管道寫端讀取資料,或者向管道讀端寫入資料都將導致錯誤發生。一般檔案的I/O函式都可以用於管道,如close、read、write等等。

從管道中讀取資料:

  • 如果管道的寫端不存在,則認為已經讀到了資料的末尾,讀函式返回的讀出位元組數為0;
  • 當管道的寫端存在時,如果請求的位元組數目大於PIPE_BUF,則返回管道中現有的資料位元組數,如果請求的位元組數目不大於PIPE_BUF,則返回管道中現有資料位元組數(此時,管道中資料量小於請求的資料量);或者返回請求的位元組數(此時,管道中資料量不小於請求的資料量)。注:(PIPE_BUF在include/linux/limits.h中定義,不同的核心版本可能會有所不同。Posix.1要求PIPE_BUF至少為512位元組,red hat 7.2中為4096)。

關於管道的讀規則驗證:

 /**************
* readtest.c *
**************/
#include <unistd.h>
#include <sys/types.h>
#include <errno.h>
main()
{
int pipe_fd[2];
pid_t pid;
char r_buf[100];
char w_buf[4];
char* p_wbuf;
int r_num;
int cmd; memset(r_buf,0,sizeof(r_buf));
memset(w_buf,0,sizeof(r_buf));
p_wbuf=w_buf;
if(pipe(pipe_fd)<0)
{
printf("pipe create error\n");
return -1;
} if((pid=fork())==0)
{
printf("\n");
close(pipe_fd[1]);
sleep(3);//確保父程序關閉寫端
r_num=read(pipe_fd[0],r_buf,100);
printf( "read num is %d the data read from the pipe is %d\n",r_num,atoi(r_buf)); close(pipe_fd[0]);
exit();
}
else if(pid>0)
{
close(pipe_fd[0]);//read
strcpy(w_buf,"111");
if(write(pipe_fd[1],w_buf,4)!=-1)
printf("parent write over\n");
close(pipe_fd[1]);//write
printf("parent close fd[1] over\n");
sleep(10);
}
}
/**************************************************
* 程式輸出結果:
* parent write over
* parent close fd[1] over
* read num is 4 the data read from the pipe is 111
* 附加結論:
* 管道寫端關閉後,寫入的資料將一直存在,直到讀出為止.
****************************************************/

向管道中寫入資料:

  • 向管道中寫入資料時,linux將不保證寫入的原子性,管道緩衝區一有空閒區域,寫程序就會試圖向管道寫入資料。如果讀程序不讀走管道緩衝區中的資料,那麼寫操作將一直阻塞。 
    注:只有在管道的讀端存在時,向管道中寫入資料才有意義。否則,向管道中寫入資料的程序將收到核心傳來的SIFPIPE訊號,應用程式可以處理該訊號,也可以忽略(預設動作則是應用程式終止)。

對管道的寫規則的驗證1:寫端對讀端存在的依賴性

#include <unistd.h>
#include <sys/types.h>
main()
{
int pipe_fd[2];
pid_t pid;
char r_buf[4];
char* w_buf;
int writenum;
int cmd; memset(r_buf,0,sizeof(r_buf));
if(pipe(pipe_fd)<0)
{
printf("pipe create error\n");
return -1;
} if((pid=fork())==0)
{
close(pipe_fd[0]);
close(pipe_fd[1]);
sleep(10);
exit();
}
else if(pid>0)
{
sleep(1); //等待子程序完成關閉讀端的操作
close(pipe_fd[0]);//write
w_buf="111";
if((writenum=write(pipe_fd[1],w_buf,4))==-1)
printf("write to pipe error\n");
else
printf("the bytes write to pipe is %d \n", writenum); close(pipe_fd[1]);
}
}

則輸出結果為: Broken pipe,原因就是該管道以及它的所有fork()產物的讀端都已經被關閉。如果在父程序中保留讀端,即在寫完pipe後,再關閉父程序的讀端,也會正常寫入pipe,讀者可自己驗證一下該結論。因此,在向管道寫入資料時,至少應該存在某一個程序,其中管道讀端沒有被關閉,否則就會出現上述錯誤(管道斷裂,程序收到了SIGPIPE訊號,預設動作是程序終止)

對管道的寫規則的驗證2:linux不保證寫管道的原子性驗證

#include <unistd.h>
#include <sys/types.h>
#include <errno.h>
main(int argc,char**argv)
{
int pipe_fd[2];
pid_t pid;
char r_buf[4096];
char w_buf[4096*2];
int writenum;
int rnum;
memset(r_buf,0,sizeof(r_buf));
if(pipe(pipe_fd)<0)
{
printf("pipe create error\n");
return -1;
} if((pid=fork())==0)
{
close(pipe_fd[1]);
while(1)
{
sleep(1);
rnum=read(pipe_fd[0],r_buf,1000);
printf("child: readnum is %d\n",rnum);
}
close(pipe_fd[0]); exit();
}
else if(pid>0)
{
close(pipe_fd[0]);//write
memset(r_buf,0,sizeof(r_buf));
if((writenum=write(pipe_fd[1],w_buf,1024))==-1)
printf("write to pipe error\n");
else
printf("the bytes write to pipe is %d \n", writenum);
writenum=write(pipe_fd[1],w_buf,4096);
close(pipe_fd[1]);
}
}
輸出結果:
the bytes write to pipe 1000
the bytes write to pipe 1000 //注意,此行輸出說明了寫入的非原子性
the bytes write to pipe 1000
the bytes write to pipe 1000
the bytes write to pipe 1000
the bytes write to pipe 120 //注意,此行輸出說明了寫入的非原子性
the bytes write to pipe 0
the bytes write to pipe 0
......

結論:

寫入數目小於4096時寫入是非原子的! 
如果把父程序中的兩次寫入位元組數都改為5000,則很容易得出下面結論: 
寫入管道的資料量大於4096位元組時,緩衝區的空閒空間將被寫入資料(補齊),直到寫完所有資料為止,如果沒有程序讀資料,則一直阻塞。

1.4管道應用例項:

例項一:用於shell

管道可用於輸入輸出重定向,它將一個命令的輸出直接定向到另一個命令的輸入。比如,當在某個shell程式(Bourne shell或C shell等)鍵入who│wc -l後,相應shell程式將建立who以及wc兩個程序和這兩個程序間的管道。考慮下面的命令列:

$kill -l 執行結果見 附一

$kill -l | grep SIGRTMIN 執行結果如下:

30) SIGPWR	31) SIGSYS	32) SIGRTMIN	33) SIGRTMIN+1
34) SIGRTMIN+2 35) SIGRTMIN+3 36) SIGRTMIN+4 37) SIGRTMIN+5
38) SIGRTMIN+6 39) SIGRTMIN+7 40) SIGRTMIN+8 41) SIGRTMIN+9
42) SIGRTMIN+10 43) SIGRTMIN+11 44) SIGRTMIN+12 45) SIGRTMIN+13
46) SIGRTMIN+14 47) SIGRTMIN+15 48) SIGRTMAX-15 49) SIGRTMAX-14

例項二:用於具有親緣關係的程序間通訊

下面例子給出了管道的具體應用,父程序通過管道傳送一些命令給子程序,子程序解析命令,並根據命令作相應處理。

#include <unistd.h>
#include <sys/types.h>
main()
{
int pipe_fd[2];
pid_t pid;
char r_buf[4];
char** w_buf[256];
int childexit=0;
int i;
int cmd; memset(r_buf,0,sizeof(r_buf));
if(pipe(pipe_fd)<0)
{
printf("pipe create error\n");
return -1;
}
if((pid=fork())==0)
//子程序:解析從管道中獲取的命令,並作相應的處理
{
printf("\n");
close(pipe_fd[1]);
sleep(2); while(!childexit)
{
read(pipe_fd[0],r_buf,4);
cmd=atoi(r_buf);
if(cmd==0)
{
printf("child: receive command from parent over\n now child process exit\n");
childexit=1;
} else if(handle_cmd(cmd)!=0)
return;
sleep(1);
}
close(pipe_fd[0]);
exit();
}
else if(pid>0)
//parent: send commands to child
{
close(pipe_fd[0]);
w_buf[0]="003";
w_buf[1]="005";
w_buf[2]="777";
w_buf[3]="000";
for(i=0;i<4;i++)
write(pipe_fd[1],w_buf[i],4);
close(pipe_fd[1]);
}
}
//下面是子程序的命令處理函式(特定於應用):
int handle_cmd(int cmd)
{
if((cmd<0)||(cmd>256))
//suppose child only support 256 commands
{
printf("child: invalid command \n");
return -1;
}
printf("child: the cmd from parent is %d\n", cmd);
return 0;
}

1.5管道的侷限性

管道的主要侷限性正體現在它的特點上:

  • 只支援單向資料流;
  • 只能用於具有親緣關係的程序之間;
  • 沒有名字;
  • 管道的緩衝區是有限的(管道制存在於記憶體中,在管道建立時,為緩衝區分配一個頁面大小);
  • 管道所傳送的是無格式位元組流,這就要求管道的讀出方和寫入方必須事先約定好資料的格式,比如多少位元組算作一個訊息(或命令、或記錄)等等;
 

回頁首

2、 有名管道概述及相關API應用

2.1 有名管道相關的關鍵概念

管道應用的一個重大限制是它沒有名字,因此,只能用於具有親緣關係的程序間通訊,在有名管道(named pipe或FIFO)提出後,該限制得到了克服。FIFO不同於管道之處在於它提供一個路徑名與之關聯,以FIFO的檔案形式存在於檔案系統中。這樣,即使與FIFO的建立程序不存在親緣關係的程序,只要可以訪問該路徑,就能夠彼此通過FIFO相互通訊(能夠訪問該路徑的程序以及FIFO的建立程序之間),因此,通過FIFO不相關的程序也能交換資料。值得注意的是,FIFO嚴格遵循先進先出(first in first out),對管道及FIFO的讀總是從開始處返回資料,對它們的寫則把資料新增到末尾。它們不支援諸如lseek()等檔案定位操作。

2.2有名管道的建立

#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char * pathname, mode_t mode)

該函式的第一個引數是一個普通的路徑名,也就是建立後FIFO的名字。第二個引數與開啟普通檔案的open()函式中的mode 引數相同。 如果mkfifo的第一個引數是一個已經存在的路徑名時,會返回EEXIST錯誤,所以一般典型的呼叫程式碼首先會檢查是否返回該錯誤,如果確實返回該錯誤,那麼只要呼叫開啟FIFO的函式就可以了。一般檔案的I/O函式都可以用於FIFO,如close、read、write等等。

2.3有名管道的開啟規則

有名管道比管道多了一個開啟操作:open。

FIFO的開啟規則:

如果當前開啟操作是為讀而開啟FIFO時,若已經有相應程序為寫而開啟該FIFO,則當前開啟操作將成功返回;否則,可能阻塞直到有相應程序為寫而開啟該FIFO(當前開啟操作設定了阻塞標誌);或者,成功返回(當前開啟操作沒有設定阻塞標誌)。

如果當前開啟操作是為寫而開啟FIFO時,如果已經有相應程序為讀而開啟該FIFO,則當前開啟操作將成功返回;否則,可能阻塞直到有相應程序為讀而開啟該FIFO(當前開啟操作設定了阻塞標誌);或者,返回ENXIO錯誤(當前開啟操作沒有設定阻塞標誌)。

對開啟規則的驗證參見 附2

2.4有名管道的讀寫規則

從FIFO中讀取資料:

約定:如果一個程序為了從FIFO中讀取資料而阻塞開啟FIFO,那麼稱該程序內的讀操作為設定了阻塞標誌的讀操作。

  • 如果有程序寫開啟FIFO,且當前FIFO內沒有資料,則對於設定了阻塞標誌的讀操作來說,將一直阻塞。對於沒有設定阻塞標誌讀操作來說則返回-1,當前errno值為EAGAIN,提醒以後再試。
  • 對於設定了阻塞標誌的讀操作說,造成阻塞的原因有兩種:當前FIFO內有資料,但有其它程序在讀這些資料;另外就是FIFO內沒有資料。解阻塞的原因則是FIFO中有新的資料寫入,不論信寫入資料量的大小,也不論讀操作請求多少資料量。
  • 讀開啟的阻塞標誌只對本程序第一個讀操作施加作用,如果本程序內有多個讀操作序列,則在第一個讀操作被喚醒並完成讀操作後,其它將要執行的讀操作將不再阻塞,即使在執行讀操作時,FIFO中沒有資料也一樣(此時,讀操作返回0)。
  • 如果沒有程序寫開啟FIFO,則設定了阻塞標誌的讀操作會阻塞。

注:如果FIFO中有資料,則設定了阻塞標誌的讀操作不會因為FIFO中的位元組數小於請求讀的位元組數而阻塞,此時,讀操作會返回FIFO中現有的資料量。

向FIFO中寫入資料:

約定:如果一個程序為了向FIFO中寫入資料而阻塞開啟FIFO,那麼稱該程序內的寫操作為設定了阻塞標誌的寫操作。

對於設定了阻塞標誌的寫操作:

  • 當要寫入的資料量不大於PIPE_BUF時,linux將保證寫入的原子性。如果此時管道空閒緩衝區不足以容納要寫入的位元組數,則進入睡眠,直到當緩衝區中能夠容納要寫入的位元組數時,才開始進行一次性寫操作。
  • 當要寫入的資料量大於PIPE_BUF時,linux將不再保證寫入的原子性。FIFO緩衝區一有空閒區域,寫程序就會試圖向管道寫入資料,寫操作在寫完所有請求寫的資料後返回。

對於沒有設定阻塞標誌的寫操作:

  • 當要寫入的資料量大於PIPE_BUF時,linux將不再保證寫入的原子性。在寫滿所有FIFO空閒緩衝區後,寫操作返回。
  • 當要寫入的資料量不大於PIPE_BUF時,linux將保證寫入的原子性。如果當前FIFO空閒緩衝區能夠容納請求寫入的位元組數,寫完後成功返回;如果當前FIFO空閒緩衝區不能夠容納請求寫入的位元組數,則返回EAGAIN錯誤,提醒以後再寫;

對FIFO讀寫規則的驗證:

下面提供了兩個對FIFO的讀寫程式,適當調節程式中的很少地方或者程式的命令列引數就可以對各種FIFO讀寫規則進行驗證。

程式1:寫FIFO的程式

#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#define FIFO_SERVER "/tmp/fifoserver"
main(int argc,char** argv)
//引數為即將寫入的位元組數
{
int fd;
char w_buf[4096*2];
int real_wnum;
memset(w_buf,0,4096*2);
if((mkfifo(FIFO_SERVER,O_CREAT|O_EXCL)<0)&&(errno!=EEXIST))
printf("cannot create fifoserver\n");
if(fd==-1)
if(errno==ENXIO)
printf("open error; no reading process\n"); fd=open(FIFO_SERVER,O_WRONLY|O_NONBLOCK,0);
//設定非阻塞標誌
//fd=open(FIFO_SERVER,O_WRONLY,0);
//設定阻塞標誌
real_wnum=write(fd,w_buf,2048);
if(real_wnum==-1)
{
if(errno==EAGAIN)
printf("write to fifo error; try later\n");
}
else
printf("real write num is %d\n",real_wnum);
real_wnum=write(fd,w_buf,5000);
//5000用於測試寫入位元組大於4096時的非原子性
//real_wnum=write(fd,w_buf,4096);
//4096用於測試寫入位元組不大於4096時的原子性 if(real_wnum==-1)
if(errno==EAGAIN)
printf("try later\n");
}

程式2:與程式1一起測試寫FIFO的規則,第一個命令列引數是請求從FIFO讀出的位元組數

#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#define FIFO_SERVER "/tmp/fifoserver"
main(int argc,char** argv)
{
char r_buf[4096*2];
int fd;
int r_size;
int ret_size;
r_size=atoi(argv[1]);
printf("requred real read bytes %d\n",r_size);
memset(r_buf,0,sizeof(r_buf));
fd=open(FIFO_SERVER,O_RDONLY|O_NONBLOCK,0);
//fd=open(FIFO_SERVER,O_RDONLY,0);
//在此處可以把讀程式編譯成兩個不同版本:阻塞版本及非阻塞版本
if(fd==-1)
{
printf("open %s for read error\n");
exit();
}
while(1)
{ memset(r_buf,0,sizeof(r_buf));
ret_size=read(fd,r_buf,r_size);
if(ret_size==-1)
if(errno==EAGAIN)
printf("no data avlaible\n");
printf("real read bytes %d\n",ret_size);
sleep(1);
}
pause();
unlink(FIFO_SERVER);
}

程式應用說明:

把讀程式編譯成兩個不同版本:

  • 阻塞讀版本:br
  • 以及非阻塞讀版本nbr

把寫程式編譯成兩個四個版本:

  • 非阻塞且請求寫的位元組數大於PIPE_BUF版本:nbwg
  • 非阻塞且請求寫的位元組數不大於PIPE_BUF版本:版本nbw
  • 阻塞且請求寫的位元組數大於PIPE_BUF版本:bwg
  • 阻塞且請求寫的位元組數不大於PIPE_BUF版本:版本bw

下面將使用br、nbr、w代替相應程式中的阻塞讀、非阻塞讀

驗證阻塞寫操作:

  1. 當請求寫入的資料量大於PIPE_BUF時的非原子性:

    • nbr 1000
    • bwg
  2. 當請求寫入的資料量不大於PIPE_BUF時的原子性:
    • nbr 1000
    • bw

驗證非阻塞寫操作:

  1. 當請求寫入的資料量大於PIPE_BUF時的非原子性:

    • nbr 1000
    • nbwg
  2. 請求寫入的資料量不大於PIPE_BUF時的原子性:
    • nbr 1000
    • nbw

不管寫開啟的阻塞標誌是否設定,在請求寫入的位元組數大於4096時,都不保證寫入的原子性。但二者有本質區別:

對於阻塞寫來說,寫操作在寫滿FIFO的空閒區域後,會一直等待,直到寫完所有資料為止,請求寫入的資料最終都會寫入FIFO;

而非阻塞寫則在寫滿FIFO的空閒區域後,就返回(實際寫入的位元組數),所以有些資料最終不能夠寫入。

對於讀操作的驗證則比較簡單,不再討論。

2.5有名管道應用例項

在驗證了相應的讀寫規則後,應用例項似乎就沒有必要了。

小結:

管道常用於兩個方面:(1)在shell中時常會用到管道(作為輸入輸入的重定向),在這種應用方式下,管道的建立對於使用者來說是透明的;(2)用於具有親緣關係的程序間通訊,使用者自己建立管道,並完成讀寫操作。

FIFO可以說是管道的推廣,克服了管道無名字的限制,使得無親緣關係的程序同樣可以採用先進先出的通訊機制進行通訊。

管道和FIFO的資料是位元組流,應用程式之間必須事先確定特定的傳輸"協議",採用傳播具有特定意義的訊息。

要靈活應用管道及FIFO,理解它們的讀寫規則是關鍵。

附1:kill -l 的執行結果,顯示了當前系統支援的所有訊號:

1) SIGHUP	 2) SIGINT	 3) SIGQUIT	 4) SIGILL
5) SIGTRAP 6) SIGABRT 7) SIGBUS 8) SIGFPE
9) SIGKILL 10) SIGUSR1 11) SIGSEGV 12) SIGUSR2
13) SIGPIPE 14) SIGALRM 15) SIGTERM 17) SIGCHLD
18) SIGCONT 19) SIGSTOP 20) SIGTSTP 21) SIGTTIN
22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO
30) SIGPWR 31) SIGSYS 32) SIGRTMIN 33) SIGRTMIN+1
34) SIGRTMIN+2 35) SIGRTMIN+3 36) SIGRTMIN+4 37) SIGRTMIN+5
38) SIGRTMIN+6 39) SIGRTMIN+7 40) SIGRTMIN+8 41) SIGRTMIN+9
42) SIGRTMIN+10 43) SIGRTMIN+11 44) SIGRTMIN+12 45) SIGRTMIN+13
46) SIGRTMIN+14 47) SIGRTMIN+15 48) SIGRTMAX-15 49) SIGRTMAX-14
50) SIGRTMAX-13 51) SIGRTMAX-12 52) SIGRTMAX-11 53) SIGRTMAX-10
54) SIGRTMAX-9 55) SIGRTMAX-8 56) SIGRTMAX-7 57) SIGRTMAX-6
58) SIGRTMAX-5 59) SIGRTMAX-4 60) SIGRTMAX-3 61) SIGRTMAX-2
62) SIGRTMAX-1 63) SIGRTMAX

除了在此處用來說明管道應用外,接下來的專題還要對這些訊號分類討論。

附2:對FIFO開啟規則的驗證(主要驗證寫開啟對讀開啟的依賴性)

#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#define FIFO_SERVER "/tmp/fifoserver"
int handle_client(char*);
main(int argc,char** argv)
{
int r_rd;
int w_fd;
pid_t pid;
if((mkfifo(FIFO_SERVER,O_CREAT|O_EXCL)<0)&&(errno!=EEXIST))
printf("cannot create fifoserver\n");
handle_client(FIFO_SERVER); }
int handle_client(char* arg)
{
int ret;
ret=w_open(arg);
switch(ret)
{
case 0:
{
printf("open %s error\n",arg);
printf("no process has the fifo open for reading\n");
return -1;
}
case -1:
{
printf("something wrong with open the fifo except for ENXIO");
return -1;
}
case 1:
{
printf("open server ok\n");
return 1;
}
default:
{
printf("w_no_r return ----\n");
return 0;
}
}
unlink(FIFO_SERVER);
}
int w_open(char*arg)
//0 open error for no reading
//-1 open error for other reasons
//1 open ok
{
if(open(arg,O_WRONLY|O_NONBLOCK,0)==-1)
{ if(errno==ENXIO)
{
return 0;
}
else
return -1;
}
return 1; }

參考資料

    • UNIX網路程式設計第二卷:程序間通訊,作者:W.Richard Stevens,譯者:楊繼張,清華大學出版社。豐富的UNIX程序間通訊例項及分析,對Linux環境下的程式開發有極大的啟發意義。
    • linux核心原始碼情景分析(上、下),毛德操、胡希明著,浙江大學出版社,當要驗證某個結論、想法時,最好的參考資料;
    • UNIX環境高階程式設計,作者:W.Richard Stevens,譯者:尤晉元等,機械工業出版社。具有豐富的程式設計例項,以及關鍵函式伴隨Unix的發展歷程。
    • http://www.linux.org.tw/CLDP/gb/Secure-Programs-HOWTO/x346.html 點明linux下sigaction的實現基礎,linux原始碼../kernel/signal.c更說明了問題;
    • pipe手冊,最直接而可靠的參考資料
    • fifo手冊,最直接而可靠的參考資料

源文:http://www.ibm.com/developerworks/cn/linux/l-ipc/part1/