Linux 系統應用程式設計——程序間通訊(上)
現在再Linux應用較多的程序間通訊方式主要有以下幾種:
1)無名管道(pipe)及有名管道(fifo):無名管道可用於具有親緣關係程序間的通訊;有名管道除具有管道相似的功能外,它還允許無親緣關係程序使用;
2)訊號(signal):訊號是在軟體層次上對中斷機制的一種模擬,它是比較複雜的通訊方式,用於通知程序某事件發生。一個程序收到一個訊號與處理器收到一箇中斷請求處理的過程類似;
3)訊息佇列(message queue):訊息佇列是訊息的連結表,包括POSIX訊息佇列和System V 訊息佇列。它克服了前兩種通訊方式中資訊量有限的缺點。具有寫許可權的程序可以按照一定的規則向訊息佇列中新增新訊息;對訊息佇列有讀許可權的程序則可以從訊息佇列中讀取訊息。
4)共享記憶體(shared memory):可以說這時最有效的程序間通訊方式。它使得多個程序可以訪問同一塊記憶體空間,不同程序可以及時檢視對方程序中對共享資料的更新。這種通訊方式需要依靠某種同步機制,如互斥鎖和訊號量等。
5)訊號量(semaphore):主要作為程序之間以及統一程序的不同執行緒之間的同步和互斥手段。
6)套接字(socket):這時一種使用更廣泛的程序間通訊機制,它可用於網路中不同主機之間的程序間通訊,應用非常廣泛。
管道通訊
管道是Linux 中程序間通訊的一種方式,它把一個程式的輸出直接連線到另一個程式的輸入,Linux 的管道主要包括兩種:無名管道和有名管道。
一、無名管道
無名管道是Linux中管道通訊的一種原始方法,他有如下特點:
1)只能用於具有親緣關係的程序之間的通訊(也就是父子程序或兄弟程序之間);
2)是一個單工的通訊模式,具有固定的讀端和寫端;
3)管道也可以看成一種特殊的檔案,對於它的讀寫也可是使用普通的read() 、write()等函式,但是它不屬於任何檔案系統,並且只存在於記憶體中;(其位元組大小為0)
1、無名管道的建立與關閉
無名管道是基於檔案描述符的通訊方式。當一個管道建立時,它會建立兩個檔案描述符:fd[0] 、fd[1] 。其中 fd[0] 固定用於讀管道,而 fd[1] 固定用於寫管道,如下圖,這樣就構成了一個單向的資料通道:
管道關閉時只需要用 close() 函式將這兩個檔案描述符關閉即可。
2、管道建立函式
建立管道可以通過 pipe() 來實現,其語法如下:
所需標頭檔案 | #include <unistd.h> |
函式原型 | int pipe(int fd[]); |
函式傳入值 | fd :包含兩個元素的整型陣列,存放管道對應的檔案描述符 |
函式返回值 | 成功:0 出錯:-1 |
3、管道讀寫說明
用pipe() 函式建立的管道兩端處於一個程序中。由於管道主要是用於不同程序間的通訊,通常是先建立一個管道,再呼叫 fork () 函式建立一個子程序,該子程序會繼承父程序所建立的管道。
需要注意的是,無名管道是單工的工作方式,即程序要麼只能讀管道,要麼只能寫管道。父子程序雖然都擁有管道的讀端和寫端,但是隻能使用其中一個(例如,可以約定父程序讀管道,而子程序寫管道)。這樣就應該把不使用的讀端或寫端檔案描述符關閉。
例如:如果將父程序的寫端 fd[1] 和子程序的讀端 fd[0] 關閉。此時,父子程序之間就建立了一條“子程序寫入 父程序讀取”的通道。同樣,也可以關閉父程序的 fd[0] 和子程序的fd[1] ,這樣就可以建立一條“父程序寫入子程序讀取”的通道。另外,父程序也可以建立 多個子程序,各個子程序都繼承了管道的fd[0] 和 fd[1] ,這樣就建立子程序之間的資料通道。
4、管道讀寫注意:
1)只有管道的讀端存在時,向管道寫入資料才有意義,否則,向管道中寫入資料的程序將收到核心傳來的 SIGPIPE 訊號 (通常為Broken Pipea錯誤)。
2)向管道寫入資料時,Linux 將不保證寫入的原子性 , 管道緩衝區只要有空間,寫程序就會試圖向管道寫入資料。如果管道緩衝區已滿,那麼寫操作將一直阻塞。
3)父程序在執行時,它們的先後次序必不能保證。為了確保父子程序已經關閉了相應的檔案描述符,可在兩個程序中呼叫 sleep() 函式,當然,用互斥和同步會更好;
下面是一個例項:
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
int pid,pid1,pid2;
int main(int argc, const char *argv[])
{
int fd[2];
char outpipe[100],inpipe[100];
if(pipe(fd) < 0)
{
perror("pipe error!");
return -1;
}
if((pid1 = fork()) < 0)
{
perror("fork pid1 error");
return -1;
}
else if(pid1 == 0)
{
printf("Child1's pid is %d\n",getpid());
close(fd[0]);
strcpy(outpipe,"Child 1 is sending a message!");
if(write(fd[1],outpipe,50) == -1)
{
perror("Child 1 write to outpipe error");
return -1;
}
exit(0);
}
if((pid2 = fork()) < 0)
{
perror("fork pid2 error");
return -1;
}
else if(pid2 == 0)
{
printf("Child2's pid is %d\n",getpid());
close(fd[0]);
strcpy(outpipe,"Child 2 is sending a message!");
sleep(1);
if(write(fd[1],outpipe,50) == -1)
{
perror("Child 2 write to outpipe error");
return -1;
}
exit(0);
}
close(fd[1]);
pid = wait(NULL);
printf("%d process is over!\n",pid);
if(read(fd[0],inpipe,50) == -1)
{
perror("read Child1 pipe error");
return -1;
}
printf("%s\n",inpipe);
pid = wait(NULL); //回收第二個結束的子程序
printf("%d process is over!\n",pid);
if(read(fd[0],inpipe,50) == -1)
{
perror("read Child1 pipe error");
return -1;
}
printf("%s\n",inpipe);
return 0;
}
執行結果如下:
fs@ubuntu:~/qiang/pipe$ ./pipe
Child2's pid is 8504
Child1's pid is 8503
8503 process is over!
Child 1 is sending a message!
8504 process is over!
Child 2 is sending a message!
fs@ubuntu:~/qiang/pipe$
二、有名管道
有名管道(FIFO)是對無名管道的一種改進,它具有如下特點:
1)它可以使互不相關的兩個程序實現彼此通訊;
2)該管道可以通過路徑名來指出,並且在檔案系統中是可見的。在建立了管道之後,兩個程序就可以把它當做普通檔案一樣進行讀寫操作,使用非常方便;
3)FIFO嚴格地遵循先進先出規則,對管道及 FIFO 的讀總是從開始處返回資料,對它們的寫則把資料新增到末尾。有名管道不支援如lseek()等檔案定位操作;
有名管道(FIFO)的建立可以使用 mkfifo() 函式,該函式類似檔案中的open() 操作,可以指定管道的路徑和訪問許可權 (使用者也可以在命令列使用 “mknod <管道名>”來建立有名管道)。
在建立管道成功以後,就可以使用open()、read() 和 write() 這些函數了。與普通檔案一樣,對於為讀而開啟的管道可在 open() 中設定 O_RDONLY,對於為寫而開啟的管道可在 open() 中設定O_WRONLY。
1、對於讀程序
預設情況下,如果當前FIFO內沒有資料,讀程序將一直阻塞到有資料寫入或是FIFO寫端都被關閉。
2、對於寫程序
只要FIFO有空間,資料就可以被寫入。若空間不足,寫程序會阻塞,知道資料都寫入為止;
mkfifo() 函式語法如下:
所需標頭檔案 | #include <sys/types.h> #include <sys/state.h> |
函式原型 | int mkfifo( const char *filename,mode_t mode) |
引數 | mode:管道的訪問許可權 |
函式返回值 | 成功:0 出粗:-1 |
下面是個例項,來學習有名管道的使用
create.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <string.h>
#include <errno.h>
int main(int argc,char *argv[])
{
if(argc < 2)
{
printf("Usage:%s <filename>",argv[0]);
return -1;
}
if(mkfifo(argv[1],0664) < 0)
{
perror("mkfifo fails");
exit(-1);
}
return 0;
}
write_fifo.c#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#define BUFFER_SIZE 1024
int main(int argc, const char *argv[])
{
int fd;
if(argc < 2)
{
printf("Usage:%s <filename>",argv[0]);
return -1;
}
if((fd = open(argv[1],O_WRONLY)) < 0)
{
perror("open error");
exit(-1);
}
printf("open fifo %s for writing success!\n",argv[0]);
char buffer[BUFFER_SIZE];
ssize_t n;
while(fgets(buffer,BUFFER_SIZE,stdin))
{
if((n = write(fd,buffer,strlen(buffer))) == -1)
{
perror("write fails");
break;
}
}
return 0;
}
read_fifo.c#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#define BUFFER_SIZE 1024
int main(int argc, const char *argv[])
{
int fd;
if(argc < 2)
{
printf("Usage:%s <filename>",argv[0]);
return -1;
}
if((fd = open(argv[1],O_RDONLY)) < 0)
{
perror("open error");
exit(-1);
}
printf("open fifo %s for reading success!\n",argv[0]);
char buffer[BUFFER_SIZE];
ssize_t n;
while(1)
{
if((n = read(fd,buffer,BUFFER_SIZE)) == -1)
{
perror("read fails");
return -1;
}
else if(n == 0)
{
printf("peer close fifo\n");
break;
}
else
{
buffer[n] = '\0';
printf("read %d bytes from fifo:%s\n",n,buffer);
}
}
return 0;
}
執行結果如下:
寫端:
fs@ubuntu:~/qiang/fifo$ ./create_fifo tmp
fs@ubuntu:~/qiang/fifo$ ./write_fifo tmp
open fifo ./write_fifo for writing success!
xiao
zhi
qiang
^C
fs@ubuntu:~/qiang/fifo$
讀端:
fs@ubuntu:~/qiang/fifo$ ./read_fifo tmp
open fifo ./read_fifo for reading success!
read 5 bytes from fifo:xiao
read 4 bytes from fifo:zhi
read 6 bytes from fifo:qiang
peer close fifo
fs@ubuntu:~/qiang/fifo$
這裡執行時,可以看到,單獨開啟讀或寫,二者會一直阻塞,直到都開啟,才會列印第一句話,當寫端關閉時,讀端也會停止。
三、訊號通訊
訊號是在軟體層次上對中斷機制的一種模擬。在原理上,一個程序收到一個訊號與處理器收到一箇中斷請求可以說是一樣的。訊號是非同步的:一個程序不必通過任何操作在等待訊號的到達。事實上,程序也不知道訊號到底什麼時候到達。事實上,程序也不知道訊號到底什麼時候到達。訊號可以直接進行使用者空間程序和核心程序之間的互動,核心程序也可以利用它來通知使用者空間程序發生了那些系統事件。它可以在任何時候發給某一程序,而無需知道該程序的狀態。如果該程序當前並未處於執行態,則該訊號就由核心儲存起來,知道該程序回恢復行再傳遞給它為止;如果一個訊號被程序設定為阻塞,則該訊號的傳遞被延遲,直道阻塞被取消時才被傳遞給程序。
1、訊號的生存週期:
2、程序可以通過3種方式來響應一個訊號
1)忽略訊號
即對訊號不做任何處理,其中,有兩個訊號不能忽略:SIGKILL及 SIGSTOP;
2)捕捉訊號
定義訊號處理函式,當訊號發生時,執行相應的處理函式。
3)執行預設操作
Linux 對每種訊號都規定了預設操作;(後面會給出訊號列表)
這裡介紹幾個常用的訊號
訊號名 | 含義 | 預設操作 |
SIGINT | 該訊號在使用者輸入INTR字元(通常是Ctrl + C)時發出, 終端驅動程式傳送該訊號並送到前臺程序中的每一個程序 |
終止程序 |
SIGQUIT | 該訊號和SIGINT類似,但由QUIT字元(通常是Ctrl + \)來 控制 |
終止程序 |
SIGKILL | 該訊號用來立即結束程式的執行; 不能被阻塞、處理和忽略; |
終止程序 |
SIGALARM | 該訊號當一個定時器到時的時候發出; | 終止程序 |
SIGSTOP | 該訊號用於暫停一個程序; 不能被阻塞、處理和忽略; |
暫停程序 |
SIGTSTP | 該訊號用於互動停止程序(掛起),由Ctrl + Z 來發出 | 終止程序 |
3、訊號處理流程
下面是核心如何實現訊號機制,即核心如何向一個程序傳送訊號、程序如何接收一個訊號、程序怎樣控制自己對訊號的反應、核心在什麼實際處理和怎樣處理程序收到的訊號。
核心對訊號的基本處理方法
核心給一個程序傳送軟中斷訊號的方法是,在程序所在的程序表項的訊號域設定對於該訊號的位(核心通過在程序的 struct task_struct 結構中的訊號域中設定相應的位來實現向一個程序傳送訊號)。這裡要補充的是,如果訊號傳送給一個正在睡眠的程序,那麼要看該程序進入睡眠的優先順序,如果程序睡眠在可被中斷的優先順序上,則喚醒程序;否則僅設定程序表中訊號域相應的位,而不喚醒程序。這一點比較重要,因為程序檢查是否收到訊號的時機是:一個程序在即將從核心態返回到使用者態時;或者,在一個程序要進入或離開一個適當的低排程優先順序睡眠狀態時。
核心處理一個程序收到的訊號的時機是一個程序從核心態返回使用者態時。所以,當一個程序在核心態執行時,軟中斷訊號並不立即起作用,要等到將返回使用者態時才處理。程序只有處理完訊號才會返回使用者態,程序在使用者態下不會有未處理完的訊號。
核心處理一個程序收到的軟中斷訊號是在該程序的上下文中,因此,程序必須處於執行狀態。處理訊號有三種類型:程序接收到訊號後退出;程序忽略該訊號;程序收到訊號後執行使用者自定義的使用系統呼叫signal() 註冊的函式。當程序接收到一個它忽略的訊號時,程序丟棄該訊號,就像從來沒有收到該訊號似得,而繼續執行。如果程序收到一個要捕捉的訊號,那麼程序從核心態返回使用者態時執行使用者定義的函式。而且執行使用者定義的函式的方法很巧妙,核心是在使用者棧上建立一個新的層,該層中將返回地址的值設定成使用者定義的處理函式的地址,這樣程序從核心返回彈出棧頂時就返回到使用者定義的處理函式處,從函式返回再彈出棧頂時,才返回原來進入核心的地方。這樣做的原因是使用者定義的處理函式不能且不允許在核心態下執行(如果使用者定義的函式在核心態下執行的話,使用者就可以獲得任何許可權)。
在訊號的處理方法中有幾點特別要引起注意:
1)在一些系統中,當一個程序處理完中斷訊號返回使用者態之前,核心清除使用者區中設定的對該訊號的處理例程的地址,即下一次程序對該訊號的處理方法又改為預設值,除非在下一次訊號到來之前再次呼叫 signal() 系統呼叫。這可能會使得程序在呼叫 signal() 之前又得到該訊號而導致退出。在BSD系統中,核心不再清除該地址。但不清楚該地址可能使得程序因為過多過快的得到某個訊號而導致堆疊溢位。為了避免出現上述情況。在BSD中,核心模擬了對硬體中斷的處理方法,即在處理某個中斷時,阻止接收新的該類中斷。
4、訊號相關函式
1)訊號傳送:kill() 和 raise()
kill() 函式同讀者熟知的kill 系統命令一樣,可以傳送訊號給程序或程序組(實際上,kill 系統命令就是由 kill () 函式實現的)。需要注意的是,它不僅可以終止程序,也可以向程序傳送其他訊號;
與kill() 函式不同的是,raise() 函式只允許程序向自身傳送訊號;
kill() 函式語法
所需標頭檔案 | #include <signal.h> #include <sys/types.h> |
函式原型 | int kill(pid_t pid,int sig); |
函式傳入值 | pid 為正數: 傳送訊號給程序號為pid 的程序 pid 為 0 : 訊號被髮送到所有和當前程序在同一個程序組的程序 pid 為 -1 :訊號傳送給所有程序表中的程序(除了程序號最大的程序外) pid 為 < -1 :訊號傳送給程序組號為 -pid 的每一個程序 sig :訊號型別 |
函式返回值 | 成功 :0 出錯: -1 |
raise() 函式的語法
所需標頭檔案 | #include <signal.h> #include <sys/types.h> |
函式原型 | int raise(int sig); |
函式傳入值 | sig :訊號型別 |
函式返回值 | 成功:0 出錯: -1 |
這裡 raise() 等價於 kill ( getpid() , sig) ;
下面舉一個例項:
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
pid_t pid;
int ret;
if((pid = fork()) < 0)
{
perror("fork error");
exit(-1);
}
if(pid == 0)
{
printf("child(pid : %d)is waiting for any signal\n",getpid());
raise(SIGSTOP);
exit(0);
}
sleep(1);
if((waitpid(pid,NULL,WNOHANG)) == 0)
{
kill(pid,SIGKILL);
printf("parent kill child process %d\n",pid);
}
waitpid(pid,NULL,0);
return 0;
}
執行結果如下:fs@ubuntu:~/qiang/signal$ ./kill
child(pid : 9977)is waiting for any signal
parent kill child process 9977
fs@ubuntu:~/qiang/signal$
2)、定時器訊號:alarm() 、pause()
alarm() 也稱鬧鐘訊號,它可以在程序中設定一個定時器。當定時器指定的時間到時,它就向程序傳送SIGALRAM訊號。要注意的是,一個程序只能有一個鬧鐘時間,如果在呼叫alarm()函式之前已設定過鬧鐘訊號,則任何以前的鬧鐘時間都被新值所代替。
pause()函式是用於將呼叫程序掛起直至收到訊號為止。
alarm()函式語法:
所需標頭檔案 | #include <unistd.h> |
函式原型 | unsigned int alarm(unsigned int second); |
函式傳入值 | seconds:指定秒數,系統經過seconds秒之後向該程序傳送SIGALARM訊號 |
函式返回值 | 成功:如果呼叫次alarm()前,程序中已經設定了鬧鐘時間, 則返回上一個鬧鐘剩餘的時間,否則返回 0; 出錯: -1 |
pause() 函式語法
所需標頭檔案 | #include <unistd.h> |
函式原型 | int pause(void); |
函式返回值 | -1;並且把 errno值設為RINTR |
下面一個例項,完成一個簡單的sleep() 函式的功能,由於SIGALARM 預設的系統動作為終止該程序,因此程式在列印資訊之前就已經結束了;
執行結果如下:
fs@ubuntu:~/qiang/signal$ ./alarm
Alarm clock
fs@ubuntu:~/qiang/signal$
可以看到printf() 裡面的內容並沒有被列印, Alarm clock 是SIGALARM訊號預設處理函式列印。
3)、訊號的設定 signal() 和 sigaction()
signal() 函式
要對一個訊號進行處理,就需要給出此訊號發生時系統所呼叫的處理函式。可以為一個特定的訊號(除去無法捕捉的SIGKILL和SIGSTOP訊號)註冊相應的處理函式。如果正在執行的程式原始碼裡註冊了針對某一特定訊號的處理程式,不論當時程式執行到何處,一旦程序接收到該訊號,相應的呼叫就會發生。
signal()函式使用時,只需要指定的訊號型別和訊號處理函式即可。它主要用於前32種非實時訊號的處理,不支援訊號傳遞資訊。
其語法格式如下:
所需標頭檔案 | #include <signal.h> |
函式原型 | typeef void (*sighandle_t)(int) ; 函式指標型別 sighandle_t signal(int signum,sighandle_t handler); |
函式傳入值 | signum:指定訊號程式碼 Handler:SIG_IGN:忽略該訊號 SIG_DFL:採用系統預設方式處理訊號 自定義的訊號處理函式; |
函式返回值 | 成功:以前的訊號處理函式 出錯:-1 |
該函式第二個引數和返回值型別都是指向一個無返回值並且帶一個整型引數的函式的指標;且只要signal() 呼叫了自定義的訊號處理函式,即使這個函式什麼也不做,這個程序也不會被終止;
下面一個程式利用signal來實現傳送訊號和接受訊號的原理:
程式內容:建立子程序代表售票員,父程序代表司機,同步過程如下:
售票員捕捉 SIGINT(代表開車),傳送訊號SIGUSR1給司機,司機列印(“let's gogogo!”);
售票員捕捉 SIGQUIT(代表停止),傳送訊號SIGUSR2給司機,司機列印(“stop the bus!”);
司機捕捉 SIGTSTP (代表車到總站),發SIGUSR1給售票員,售票員列印(“Please get off the bus”);
程式碼如下:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
pid_t pid;
void driver_handler(int signo);
void saler_handler(int signo);
int main(int argc,char *argv[])
{
if((pid = fork()) < 0)
{
perror("fork error");
return -1;
}
if(pid > 0)
{
signal(SIGTSTP,driver_handler);
signal(SIGINT,SIG_IGN);
signal(SIGQUIT,SIG_IGN);
signal(SIGUSR1,driver_handler);
signal(SIGUSR2,driver_handler);
while(1)
pause();
}
if(pid == 0)
{
signal(SIGINT,saler_handler);
signal(SIGTSTP,SIG_IGN);
signal(SIGQUIT,saler_handler);
signal(SIGUSR1,saler_handler);
signal(SIGUSR2,SIG_IGN);
while(1)
pause();
}
return 0;
}
void driver_handler(int signo)
{
if(signo == SIGUSR1)
printf("Let's gogogo!\n");
if(signo == SIGUSR2)
printf("Stop the bus!\n");
if(signo == SIGTSTP)
kill(pid,SIGUSR1);
}
void saler_handler(int signo)
{
pid_t ppid = getppid();
if(signo == SIGINT)
kill(ppid,SIGUSR1);
if(signo == SIGQUIT)
kill(ppid,SIGUSR2);
if(signo == SIGUSR1)
{
printf("please get off the bus\n");
kill(ppid,SIGKILL);
exit(0);
}
}
執行結果如下:
fs@ubuntu:~/qiang/signal$ ./signal
^CLet's gogogo!
^\Stop the bus!
^CLet's gogogo!
^\Stop the bus!
^CLet's gogogo!
^\Stop the bus!
^CLet's gogogo!
^\Stop the bus!
^Zplease get off the bus
Killed
fs@ubuntu:~/qiang/signal$
sigaction() 函式
sigaction() 函式的功能是檢查或修改(或兩者)與指定訊號相關聯的處理動作,此函式可以完全代替signal 函式。
函式原型如下:
所需標頭檔案 | #include <signal.h> |
函式原型 | int sigaction(int signum, const struct sigaction *act , struct sigaction *oldact ); |
函式傳入值 | signum:可以指定SIGKILL和SIGSTOP以外的所有訊號 act :act 是一個結構體,裡面包含訊號處理函式的地址、 處理方式等資訊; oldact :引數oldact 是一個傳出引數,sigaction 函式呼叫成功後, oldact 裡面包含以前對 signum 訊號的處理方式的資訊; |
函式返回值 | 成功:0 出錯:-1 |
其中引數signo 是要檢測或修改其具體動作的訊號編號。若act 指標非NULL,則要修改其動作。如果oact 指標非空,則系統經由 oact 指標返回該訊號的上一個動作;
引數結構sigaction定義如下:
struct sigaction
{
void (*sa_handler) (int);
void (*sa_sigaction)(int, siginfo_t *, void *);
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer) (void);
}
① sa_handler:此引數和signal()的引數handler相同,此引數主要用來對訊號舊的安裝函式signal()處理形式的支援;
② sa_sigaction:新的訊號安裝機制,處理函式被呼叫的時候,不但可以得到訊號編號,而且可以獲悉被呼叫的原因以及產生問題的上下文的相關資訊。
③ sa_mask:用來設定在處理該訊號時暫時將sa_mask指定的訊號擱置;
④ sa_restorer: 此引數沒有使用;
⑤ sa_flags:用來設定訊號處理的其他相關操作,下列的數值可用。可用OR 運算(|)組合:
ŸA_NOCLDSTOP:如果引數signum為SIGCHLD,則當子程序暫停時並不會通知父程序
SA_ONESHOT/SA_RESETHAND:當呼叫新的訊號處理函式前,將此訊號處理方式改為系統預設的方式
SA_RESTART:被訊號中斷的系統呼叫會自行重啟
SA_NOMASK/SA_NODEFER:在處理此訊號未結束前不理會此訊號的再次到來
SA_SIGINFO:訊號處理函式是帶有三個引數的sa_sigaction。