在進行堵塞式系統調用時。為避免進程陷入無限期的等待,能夠為這些堵塞式系統調用設置定時器。Linux提供了alarm系統調用和SIGALRM信號實現這個功能。
要使用定時器。首先要安裝SIGALRM信號。假設不安裝SIGALRM信號,則進程收到SIGALRM信號后。缺省的動作就是終止當前進程。
SIGALRM信號成功安裝后,在什么情況下進程會收到該信號呢?這就要依賴於Linux提供的定時器功能。在Linux系統下,每一個進程都有惟一的一個定時器,該定時器提供了以秒為單位的定時功能。在定時器設置的超時時間到達后,調用alarm的進程將收到SIGALRM信號。
alarm系統調用的原型為:
#include <unistd.h>
unsigned int alarm(unsigned int seconds);
參數說明:
1)seconds:要設定的定時時間,以秒為單位。在alarm調用成功后開始計時。超過該時間將觸發SIGALRM信號。
返回值:
返回當前進程曾經設置的定時器剩余秒數。
例8-10:編程利用SIGALRM信號實現秒定時器。
代碼例如以下:
#include <stdio.h>
#include <signal.h>
//全局計數器變量
int Cnt=0;
//SIGALRM信號處理函數
void CbSigAlrm(int signo)
{
//輸出定時提示信息
printf(" seconds: %d",++Cnt);
printf("\r");
//又一次啟動定時器,實現1秒定時
alarm(1);
}
void main()
{
//安裝SIGALRM信號
if(signal(SIGALRM,CbSigAlrm)==SIG_ERR)
{
perror("signal");
return;
}
//關閉標准輸出的行緩存模式
setbuf(stdout,NULL);
//啟動定時器
alarm(1);
//進程進入無限循環,僅僅能手動終止
while(1)
{
//暫停,等待信號
pause();
}
}
8.5.2 SIGCLD信號
在Linux的多進程編程中,SIGCLD是一個很重要的信號。當一個子進程退出時。並非馬上釋放其占用的資源,而是通知其父進程,由父進程進行興許的工作。
在這一過程中,系統將依次產生下列事件。
1)向父進程發送SIGCLD信號,子進程進入zombie(僵屍)狀態。
2)父進程接收到SIGCLD信號,進行處理。
假設在上述過程中父進程既沒有忽略SIGCLD信號。也未捕獲該信號進行處理,則子進程將進入僵屍狀態。僵屍狀態的進程不能被操作系統調用,也沒有不論什么可執行代碼,它只是是占用了進程列表中的一個位置而已。
假設僅有幾個僵屍進程不會影響系統的執行,可是假設僵屍進程過多。則將會嚴重影響系統的執行。因此,在編程過程中應避免產生僵屍進程。有兩種主要的處理方法能夠避免產生僵屍進程:一是父進程忽略SIGCLD信號;二是父進程捕獲SIGCLD信號,在信號處理函數中獲取子進程的退出狀態。忽略信號的方式比較簡單。僅僅須要調用signal(SIGCLD,SIG_IGN)語句就可以完畢。假設要捕獲信號並處理。那么先要安裝SIGCLD信號,然后在信號處理函數中調用wait或者waitpid等函數獲取子進程的退出狀態。
例8-11:編程捕獲SIGCLD信號。輸出各子進程的ID和退出狀態碼。
代碼例如以下:
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
//SIGCLD信號處理函數
void CbSigCld(int signo)
{
//保存退出進程的ID
int pid;
//保存退出進程的退出狀態碼
int status;
//等待不論什么一個子進程退出
pid=waitpid(-1,&status,0);
//輸出退出的子進程ID和退出代碼
printf("Child process %d exit with status %d\n",pid,status);
}
void main()
{
int i,pid;
//安裝SIGCLD信號
if(signal(SIGCLD,CbSigCld)==SIG_ERR)
{
perror("signal");
return;
}
//循環創建子進程
for(i=0;i<5;i++)
{
pid=fork();
//假設是子進程
if(pid==0)
{
//退出子進程,退出狀態碼為0
exit(0);
}
//假設是父進程
else
{
sleep(1);
}
}
}