1. 程式人生 > >C語言程式設計技巧——signal(訊號) 函式、訊號機制及軟中斷詳解

C語言程式設計技巧——signal(訊號) 函式、訊號機制及軟中斷詳解

  • Signal()函式使用簡介

         signal()是一種系統呼叫,用於通知執行時系統,當某種特定的“軟體中斷”發生時呼叫特定的程式。它的真正的名字應該是“Call_that_routine_when_this_interrupt_Comes_in(當該中斷髮生時呼叫那個程式)", 呼叫signal()函式,並通過引數傳遞告訴它終端型別以及用於處理中斷的程式。

         ANSIC 標準中,signal()函式的宣告如下:void (*signal (int sig ,void (*func)(int))) (int) ;

         函式返回與給定sig訊號相關聯的func的以前值

這個函式的模樣很恐怖,它的意思是:signal是一個函式,

他返回一個函式指標,後者所指向的函式接受一個int引數並返回void 。

         可以用typedef進行簡化:

         typedef void (*ptr_to_func)  (int) ;

         ptr_to_func signal (int , ptr_to_func );

        例項:捕捉段錯誤訊號的訊號處理函式:

#include <signal.h>

#include <stdio.h>

void handler(int s)

{

         if (s == SIGBUS) printf(" now got a bus error signal\n");

         if (s == SIGSEGV) printf(" now got a segmentation violation signal\n");

         if (s == SIGILL) printf(" now got an illegal instruction signal\n");

         exit(1);

}

main () 

{

         int *p=NULL;

         signal(SIGBUS, handler);

         signal(SIGSEGV, handler);

         signal(SIGILL, handler);

         *p=0;

}

      由於p是一個空指標,對空指標賦值會引發一個段錯誤,於是程式捕捉到SIGSEGV訊號,並列印訊息”now got a segmentation violation signal“後退出。

      開啟windows下的signal.h標頭檔案,發現在這個標頭檔案裡定義了很多符號,但是沒有找到SIGBUS,所以上面的程式碼在windows下編譯不通過: 

     error C2065: 'SIGBUS' : undeclared identifier

     把涉及到SIGBUS的相關部分刪去即可。

 使用setjmp、longjmp從訊號中恢復:

#include <setjmp.h>

#include <signal.h>

#include <stdio.h>

jmp_buf buf;

void handler(int s)

{

       if (s == SIGINT) printf(" now got a SIGINT signal\n");

       longjmp(buf, 1);

}

main () 

{

        signal(SIGINT, handler);

        if (setjmp(buf)) {

                 printf("back in main\n");

                 return 0;

          }else

                 printf("first time through\n");

loop:

          goto loop;

}

  • linux系統signal訊號機制詳解

訊號機制是程序之間相互傳遞訊息的一種方法,訊號全稱為軟中斷訊號,也有人稱作軟中斷。從它的命名可以看出,它的實質和使用很象中斷。所以,訊號可以說是程序控制的一部分。

  • 一. 訊號的基本概念

本節先介紹訊號的一些基本概念,然後給出一些基本的訊號型別和訊號對應的事件。基本概念對於理解和使用訊號,對於理解訊號機制都特別重要。下面就來看看什麼是訊號。

1、基本概念

軟中斷訊號(signal,又簡稱為訊號)用來通知程序發生了非同步事件。程序之間可以互相通過系統呼叫kill傳送軟中斷訊號。核心也可以因為內部事件而給程序傳送訊號,通知程序發生了某個事件。注意,訊號只是用來通知某程序發生了什麼事件,並不給該程序傳遞任何資料。

收到訊號的程序對各種訊號有不同的處理方法。處理方法可以分為三類:第一種是類似中斷的處理程式,對於需要處理的訊號,程序可以指定處理函式,由該函式來處理。第二種方法是,忽略某個訊號,對該訊號不做任何處理,就象未發生過一樣。第三種方法是,對該訊號的處理保留系統的預設值,這種預設操作,對大部分的訊號的預設操作是使得程序終止。程序通過系統呼叫signal來指定程序對某個訊號的處理行為。

在程序表的表項中有一個軟中斷訊號域,該域中每一位對應一個訊號,當有訊號傳送給程序時,對應位置位。由此可以看出,程序對不同的訊號可以同時保留,但對於同一個訊號,程序並不知道在處理之前來過多少個。

2、訊號的型別

發出訊號的原因很多,這裡按發出訊號的原因簡單分類,以瞭解各種訊號:

(1) 與程序終止相關的訊號。當程序退出,或者子程序終止時,發出這類訊號。

(2) 與程序例外事件相關的訊號。如程序越界,或企圖寫一個只讀的記憶體區域(如程式正文區),或執行一個特權指令及其他各種硬體錯誤。

(3) 與在系統呼叫期間遇到不可恢復條件相關的訊號。如執行系統呼叫exec時,原有資源已經釋放,而目前系統資源又已經耗盡。

(4) 與執行系統呼叫時遇到非預測錯誤條件相關的訊號。如執行一個並不存在的系統呼叫。

(5) 在使用者態下的程序發出的訊號。如程序呼叫系統呼叫kill向其他程序傳送訊號。

(6) 與終端互動相關的訊號。如使用者關閉一個終端,或按下break鍵等情況。

(7) 跟蹤程序執行的訊號。

Linux支援的訊號列表如下。很多訊號是與機器的體系結構相關的,首先列出的是POSIX.1中列出的訊號:

訊號 值 處理動作 發出訊號的原因 

---------------------------------------------------------------------- 

SIGHUP 1 A 終端掛起或者控制程序終止 

SIGINT 2 A 鍵盤中斷(如break鍵被按下) 

SIGQU99v 3 C 鍵盤的退出鍵被按下 

SIGILL 4 C 非法指令 

SIGABRT 6 C 由abort(3)發出的退出指令 

SIGFPE 8 C 浮點異常 

SIGKILL 9 AEF Kill訊號 

SIGSEGV 11 C 無效的記憶體引用 

SIGPIPE 13 A 管道破裂: 寫一個沒有讀埠的管道 

SIGALRM 14 A 由alarm(2)發出的訊號 

SIGTERM 15 A 終止訊號 

SIGUSR1 30,10,16 A 使用者自定義訊號1 

SIGUSR2 31,12,17 A 使用者自定義訊號2 

SIGCHLD 20,17,18 B 子程序結束訊號 

SIGCONT 19,18,25 程序繼續(曾被停止的程序) 

SIGSTOP 17,19,23 DEF 終止程序 

SIGTSTP 18,20,24 D 控制終端(tty)上按下停止鍵 

SIGTTIN 21,21,26 D 後臺程序企圖從控制終端讀 

SIGTTOU 22,22,27 D 後臺程序企圖從控制終端寫

下面的訊號沒在POSIX.1中列出,而在SUSv2列出

訊號 值 處理動作 發出訊號的原因 

-------------------------------------------------------------------- 

SIGBUS 10,7,10 C 匯流排錯誤(錯誤的記憶體訪問) 

SIGPOLL A Sys V定義的Pollable事件,與SIGIO同義 

SIGPROF 27,27,29 A Profiling定時器到 

SIGSYS 12,-,12 C 無效的系統呼叫 (SVID) 

SIGTRAP 5 C 跟蹤/斷點捕獲 

SIGURG 16,23,21 B Socket出現緊急條件(4.2 BSD) 

SIGVTALRM 26,26,28 A 實際時間報警時鐘訊號(4.2 BSD) 

SIGXCPU 24,24,30 C 超出設定的CPU時間限制(4.2 BSD) 

SIGXFSZ 25,25,31 C 超出設定的檔案大小限制(4.2 BSD)

(對於SIGSYS,SIGXCPU,SIGXFSZ,以及某些機器體系結構下的SIGBUS,Linux預設的動作是A (terminate),SUSv2 是C (terminate and dump core))。

下面是其它的一些訊號

訊號 值 處理動作 發出訊號的原因 

---------------------------------------------------------------------- 

SIGIOT 6 C IO捕獲指令,與SIGABRT同義 

SIGEMT 7,-,7 

SIGSTKFLT -,16,- A 協處理器堆疊錯誤 

SIGIO 23,29,22 A 某I/O操作現在可以進行了(4.2 BSD) 

SIGCLD -,-,18 A 與SIGCHLD同義 

SIGPWR 29,30,19 A 電源故障(System V) 

SIGINFO 29,-,- A 與SIGPWR同義 

SIGLOST -,-,- A 檔案鎖丟失 

SIGWINCH 28,28,20 B 視窗大小改變(4.3 BSD, Sun) 

SIGUNUSED -,31,- A 未使用的訊號(will be SIGSYS)

(在這裡,- 表示訊號沒有實現;有三個值給出的含義為,第一個值通常在Alpha和Sparc上有效,中間的值對應i386和ppc以及sh,最後一個值對應mips。訊號29在Alpha上為SIGINFO / SIGPWR ,在Sparc上為SIGLOST。)

處理動作一項中的字母含義如下 

A 預設的動作是終止程序 

B 預設的動作是忽略此訊號 

C 預設的動作是終止程序並進行核心映像轉儲(dump core) 

D 預設的動作是停止程序 

E 訊號不能被捕獲 

F 訊號不能被忽略

上面介紹的訊號是常見系統所支援的。以表格的形式介紹了各種訊號的名稱、作用及其在預設情況下的處理動作。各種預設處理動作的含義是:終止程式是指程序退出;忽略該訊號是將該訊號丟棄,不做處理;停止程式是指程式掛起,進入停止狀況以後還能重新進行下去,一般是在除錯的過程中(例如ptrace系統呼叫);核心映像轉儲是指將程序資料在記憶體的映像和程序在核心結構中儲存的部分內容以一定格式轉儲到檔案系統,並且程序退出執行,這樣做的好處是為程式設計師提供了方便,使得他們可以得到程序當時執行時的資料值,允許他們確定轉儲的原因,並且可以除錯他們的程式。

注意 訊號SIGKILL和SIGSTOP既不能被捕捉,也不能被忽略。訊號SIGIOT與SIGABRT是一個訊號。可以看出,同一個訊號在不同的系統中值可能不一樣,所以建議最好使用為訊號定義的名字,而不要直接使用訊號的值。


  • 二、信 號 機 制

上一節中介紹了訊號的基本概念,在這一節中,我們將介紹核心如何實現訊號機制。即核心如何向一個程序傳送訊號、程序如何接收一個訊號、程序怎樣控制自己對訊號的反應、核心在什麼時機處理和怎樣處理程序收到的訊號。還要介紹一下setjmp和longjmp在訊號中起到的作用。

1、核心對訊號的基本處理方法

核心給一個程序傳送軟中斷訊號的方法,是在程序所在的程序表項的訊號域設定對應於該訊號的位。這裡要補充的是,如果訊號傳送給一個正在睡眠的程序,那麼要看該程序進入睡眠的優先順序,如果程序睡眠在可被中斷的優先順序上,則喚醒程序;否則僅設定程序表中訊號域相應的位,而不喚醒程序。這一點比較重要,因為程序檢查是否收到訊號的時機是:一個程序在即將從核心態返回到使用者態時;或者,在一個程序要進入或離開一個適當的低排程優先順序睡眠狀態時。

核心處理一個程序收到的訊號的時機是在一個程序從核心態返回使用者態時。所以,當一個程序在核心態下執行時,軟中斷訊號並不立即起作用,要等到將返回使用者態時才處理。程序只有處理完訊號才會返回使用者態,程序在使用者態下不會有未處理完的訊號。

核心處理一個程序收到的軟中斷訊號是在該程序的上下文中,因此,程序必須處於執行狀態。前面介紹概念的時候講過,處理訊號有三種類型:程序接收到訊號後退出;程序忽略該訊號;程序收到訊號後執行使用者設定用系統呼叫signal的函式。當程序接收到一個它忽略的訊號時,程序丟棄該訊號,就象沒有收到該訊號似的繼續執行。如果程序收到一個要捕捉的訊號,那麼程序從核心態返回使用者態時執行使用者定義的函式。而且執行使用者定義的函式的方法很巧妙,核心是在使用者棧上建立一個新的層,該層中將返回地址的值設定成使用者定義的處理函式的地址,這樣程序從核心返回彈出棧頂時就返回到使用者定義的函式處,從函式返回再彈出棧頂時,才返回原先進入核心的地方。這樣做的原因是使用者定義的處理函式不能且不允許在核心態下執行(如果使用者定義的函式在核心態下執行的話,使用者就可以獲得任何許可權)。

在訊號的處理方法中有幾點特別要引起注意。第一,在一些系統中,當一個程序處理完中斷訊號返回使用者態之前,核心清除使用者區中設定的對該訊號的處理例程的地址,即下一次程序對該訊號的處理方法又改為預設值,除非在下一次訊號到來之前再次使用signal系統呼叫。這可能會使得程序在呼叫signal之前又得到該訊號而導致退出。在BSD中,核心不再清除該地址。但不清除該地址可能使得程序因為過多過快的得到某個訊號而導致堆疊溢位。為了避免出現上述情況。在BSD系統中,核心模擬了對硬體中斷的處理方法,即在處理某個中斷時,阻止接收新的該類中斷。

第二個要引起注意的是,如果要捕捉的訊號發生於程序正在一個系統呼叫中時,並且該程序睡眠在可中斷的優先順序上,這時該訊號引起程序作一次longjmp,跳出睡眠狀態,返回使用者態並執行訊號處理例程。當從訊號處理例程返回時,程序就象從系統呼叫返回一樣,但返回了一個錯誤程式碼,指出該次系統呼叫曾經被中斷。這要注意的是,BSD系統中核心可以自動地重新開始系統呼叫。

第三個要注意的地方:若程序睡眠在可中斷的優先順序上,則當它收到一個要忽略的訊號時,該程序被喚醒,但不做longjmp,一般是繼續睡眠。但使用者感覺不到程序曾經被喚醒,而是象沒有發生過該訊號一樣。

第四個要注意的地方:核心對子程序終止(SIGCLD)訊號的處理方法與其他訊號有所區別。當程序檢查出收到了一個子程序終止的訊號時,預設情況下,該程序就象沒有收到該訊號似的,如果父程序執行了系統呼叫wait,程序將從系統呼叫wait中醒來並返回wait呼叫,執行一系列wait呼叫的後續操作(找出僵死的子程序,釋放子程序的程序表項),然後從wait中返回。SIGCLD訊號的作用是喚醒一個睡眠在可被中斷優先順序上的程序。如果該程序捕捉了這個訊號,就象普通訊號處理一樣轉到處理例程。如果程序忽略該訊號,那麼系統呼叫wait的動作就有所不同,因為SIGCLD的作用僅僅是喚醒一個睡眠在可被中斷優先順序上的程序,那麼執行wait呼叫的父程序被喚醒繼續執行wait呼叫的後續操作,然後等待其他的子程序。

如果一個程序呼叫signal系統呼叫,並設定了SIGCLD的處理方法,並且該程序有子程序處於僵死狀態,則核心將向該程序發一個SIGCLD訊號。

2、setjmp和longjmp的作用

前面在介紹訊號處理機制時,多次提到了setjmp和longjmp,但沒有仔細說明它們的作用和實現方法。這裡就此作一個簡單的介紹。

在介紹訊號的時候,我們看到多個地方要求程序在檢查收到訊號後,從原來的系統呼叫中直接返回,而不是等到該呼叫完成。這種程序突然改變其上下文的情況,就是使用setjmp和longjmp的結果。setjmp將儲存的上下文存入使用者區,並繼續在舊的上下文中執行。這就是說,程序執行一個系統呼叫,當因為資源或其他原因要去睡眠時,核心為程序作了一次setjmp,如果在睡眠中被訊號喚醒,程序不能再進入睡眠時,核心為程序呼叫longjmp,該操作是核心為程序將原先setjmp呼叫儲存在程序使用者區的上下文恢復成現在的上下文,這樣就使得程序可以恢復等待資源前的狀態,而且核心為setjmp返回1,使得程序知道該次系統呼叫失敗。這就是它們的作用。


  • 三、有關訊號的系統呼叫

前面兩節已經介紹了有關訊號的大部分知識。這一節我們來了解一下這些系統呼叫。其中,系統呼叫signal是程序用來設定某個訊號的處理方法,系統呼叫kill是用來發送訊號給指定程序的。這兩個呼叫可以形成訊號的基本操作。後兩個呼叫pause和alarm是通過訊號實現的程序暫停和定時器,呼叫alarm是通過訊號通知程序定時器到時。所以在這裡,我們還要介紹這兩個呼叫。

1、signal 系統呼叫

系統呼叫signal用來設定某個訊號的處理方法。該呼叫宣告的格式如下: 
   void (*signal(int signum, void (*handler)(int)))(int); 
在使用該呼叫的程序中加入以下標頭檔案: 
   #include <signal.h>

上述宣告格式比較複雜,如果不清楚如何使用,也可以通過下面這種型別定義的格式來使用(POSIX的定義): 
   typedef void (*sighandler_t)(int); 
   sighandler_t signal(int signum, sighandler_t handler);
 
但這種格式在不同的系統中有不同的型別定義,所以要使用這種格式,最好還是參考一下聯機手冊。

在呼叫中,引數signum指出要設定處理方法的訊號。第二個引數handler是一個處理函式,或者是 
  SIG_IGN:忽略引數signum所指的訊號。 
  SIG_DFL:恢復引數signum所指訊號的處理方法為預設值。

傳遞給訊號處理例程的整數引數是訊號值,這樣可以使得一個訊號處理例程處理多個訊號。系統呼叫signal返回值是指定訊號signum前一次的處理例程或者錯誤時返回錯誤程式碼SIG_ERR。下面來看一個簡單的例子:

     #include <signal.h> 

#include <unistd.h> 

#include <stdio.h> 

void sigroutine(int dunno) { /* 訊號處理例程,其中dunno將會得到訊號的值 */ 

switch (dunno) { 

case 1: 

printf("Get a signal -- SIGHUP "); 

break; 

case 2: 

printf("Get a signal -- SIGINT "); 

break; 

case 3: 

printf("Get a signal -- SIGQU99 "); 

break; 

return; 

int main() { 

printf("process id is %d ",getpid()); 

signal(SIGHUP, sigroutine); //* 下面設定三個訊號的處理方法 

signal(SIGINT, sigroutine); 

signal(SIGQU99v, sigroutine); 

for (;;) ; 

}

其中訊號SIGINT由按下Ctrl-C發出,訊號SIGQU99v由按下Ctrl-發出。該程式執行的結果如下:

     localhost:~$ ./sig_test 

process id is 463 

Get a signal -SIGINT //按下Ctrl-C得到的結果 

Get a signal -SIGQU99v //按下Ctrl-得到的結果 

//按下Ctrl-z將程序置於後臺 

[1]+ Stopped ./sig_test 

localhost:~$ bg 

[1]+ ./sig_test & 

localhost:~$ kill -HUP 463 //向程序傳送SIGHUP訊號 

localhost:~$ Get a signal – SIGHUP 

kill -9 463 //向程序傳送SIGKILL訊號,終止程序 

localhost:~$

2、kill 系統呼叫

系統呼叫kill用來向程序傳送一個訊號。該呼叫宣告的格式如下: 
  int kill(pid_t pid, int sig); 
  在使用該呼叫的程序中加入以下標頭檔案: 
  #include <sys/types.h> 
  #include <signal.h>

該系統呼叫可以用來向任何程序或程序組傳送任何訊號。如果引數pid是正數,那麼該呼叫將訊號sig傳送到程序號為pid的程序。如果pid等於0,那麼訊號sig將傳送給當前程序所屬程序組裡的所有程序。如果引數pid等於-1,訊號sig將傳送給除了程序1和自身以外的所有程序。如果引數pid小於-1,訊號sig將傳送給屬於程序組-pid的所有程序。如果引數sig為0,將不傳送訊號。該呼叫執行成功時,返回值為0;錯誤時,返回-1,並設定相應的錯誤程式碼errno。下面是一些可能返回的錯誤程式碼: 
   EINVAL:指定的訊號sig無效。 
   ESRCH:引數pid指定的程序或程序組不存在。注意,在程序表項中存在的程序,可能是一個還沒有被wait收回,但已經終止執行的僵死程序。 
   EPERM:程序沒有權力將這個訊號傳送到指定接收訊號的程序。因為,一個程序被允許將訊號傳送到程序pid時,必須擁有root權力,或者是發出呼叫的程序的UID或EUID與指定接收的程序的UID或儲存使用者ID(savedset-user-ID)相同。如果引數pid小於-1,即該訊號傳送給一個組,則該錯誤表示組中有成員程序不能接收該訊號。

3、pause系統呼叫

系統呼叫pause的作用是等待一個訊號。該呼叫的宣告格式如下: 
   int pause(void); 
在使用該呼叫的程序中加入以下標頭檔案: 
   #include <unistd.h>

該呼叫使得發出呼叫的程序進入睡眠,直到接收到一個訊號為止。該呼叫總是返回-1,並設定錯誤程式碼為EINTR(接收到一個訊號)。下面是一個簡單的範例:

#include <unistd.h> 
  #include <stdio.h> 
  #include <signal.h> 
  void sigroutine(int unused) 


     printf("Catch a signal SIGINT "); 
  }

int main() 


    signal(SIGINT, sigroutine); 
    pause(); 
    printf("receive a signal "); 
  }

在這個例子中,程式開始執行,就象進入了死迴圈一樣,這是因為程序正在等待訊號,當我們按下Ctrl-C時,訊號被捕捉,並且使得pause退出等待狀態。

4、alarm和 setitimer系統呼叫

系統呼叫alarm的功能是設定一個定時器,當定時器計時到達時,將發出一個訊號給程序。該呼叫的宣告格式如下: 
   unsigned int alarm(unsigned int seconds); 
在使用該呼叫的程序中加入以下標頭檔案: 
   #include <unistd.h>

系統呼叫alarm安排核心為呼叫程序在指定的seconds秒後發出一個SIGALRM的訊號。如果指定的引數seconds為0,則不再發送SIGALRM訊號。後一次設定將取消前一次的設定。該呼叫返回值為上次定時呼叫到傳送之間剩餘的時間,或者因為沒有前一次定時呼叫而返回0。

注意,在使用時,alarm只設定為傳送一次訊號,如果要多次傳送,就要多次使用alarm呼叫。

對於alarm,這裡不再舉例。現在的系統中很多程式不再使用alarm呼叫,而是使用setitimer呼叫來設定定時器,用getitimer來得到定時器的狀態,這兩個呼叫的宣告格式如下:

int getitimer(int which, struct itimerval *value); 
  int setitimer(int which, const struct itimerval *value, struct itimerval *ovalue); 

在使用這兩個呼叫的程序中加入以下標頭檔案: 
  #include <sys/time.h>

該系統呼叫給程序提供了三個定時器,它們各自有其獨有的計時域,當其中任何一個到達,就傳送一個相應的訊號給程序,並使得計時器重新開始。三個計時器由引數which指定,如下所示: 
TIMER_REAL:按實際時間計時,計時到達將給程序傳送SIGALRM訊號。 
99vIMER_VIRTUAL:僅當程序執行時才進行計時。計時到達將傳送SIGVTALRM訊號給程序。 
99vIMER_PROF:當程序執行時和系統為該程序執行動作時都計時。與99vIMER_VIR-TUAL是一對,該定時器經常用來統計程序在使用者態和核心態花費的時間。計時到達將傳送SIGPROF訊號給程序。

定時器中的引數value用來指明定時器的時間,其結構如下: 
struct itimerval { 
struct timeval it_interval; /* 下一次的取值 */ 
struct timeval it_value; /* 本次的設定值 */ 
};

該結構中timeval結構定義如下: 
struct timeval { 
long tv_sec; /* 秒 */ 
long tv_usec; /* 微秒,1秒 = 1000000 微秒*/ 
};

在setitimer呼叫中,引數ovalue如果不為空,則其中保留的是上次呼叫設定的值。定時器將it_value遞減到0時,產生一個訊號,並將it_value的值設定為it_interval的值,然後重新開始計時,如此往復。當it_value設定為0時,計時器停止,或者當它計時到期,而it_interval為0時停止。呼叫成功時,返回0;錯誤時,返回-1,並設定相應的錯誤程式碼errno: 
EFAULT:引數value或ovalue是無效的指標。 
EINVAL:引數which不是99vIMER_REAL、99vIMER_VIRT或99vIMER_PROF中的一個。

下面是關於setitimer呼叫的一個簡單示範,在該例子中,每隔一秒發出一個SIGALRM,每隔0.5秒發出一個SIGVTALRM訊號:

#include <signal.h> 
#include <unistd.h> 
#include <stdio.h> 
#include <sys/time.h> 
int sec; 
void sigroutine(int signo) { 
switch (signo) { 
case SIGALRM: 
printf("Catch a signal -- SIGALRM "); 
break; 
case SIGVTALRM: 
printf("Catch a signal -- SIGVTALRM "); 
break; 

return; 


int main() { 
struct itimerval value,ovalue,value2; 
sec = 5; 

printf("process id is %d ",getpid()); 
signal(SIGALRM, sigroutine); 
signal(SIGVTALRM, sigroutine); 

value.it_value.tv_sec = 1; 
value.it_value.tv_usec = 0; 
value.it_interval.tv_sec = 1; 
value.it_interval.tv_usec = 0; 
setitimer(99vIMER_REAL, &value, &ovalue);

value2.it_value.tv_sec = 0; 
value2.it_value.tv_usec = 500000; 
value2.it_interval.tv_sec = 0; 
value2.it_interval.tv_usec = 500000; 
setitimer(99vIMER_VIRTUAL, &value2, &ovalue);

for (;;) ; 
}

該例子的螢幕拷貝如下:

localhost:~$ ./timer_test 
process id is 579 
Catch a signal – SIGVTALRM 
Catch a signal – SIGALRM 
Catch a signal – SIGVTALRM 
Catch a signal – SIGVTALRM 
Catch a signal – SIGALRM 
Catch a signal –GVTALRM


相關推薦

C語言程式設計技巧——signal(訊號) 函式訊號機制中斷

Signal()函式使用簡介          signal()是一種系統呼叫,用於通知執行時系統,當某種特定的“軟體中斷”發生時呼叫特定的程式。它的真正的名字應該是“Call_that_routine_when_this_interrupt_Comes_in(當該中斷

C語言程式設計基礎講座之函式

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

海天醬油教你不知道的C語言程式設計技巧

在C語言程式設計中,我們經常會遇到這種情況,在某個函式中經過演算法處理以後得到一個字串型別的結果,可能需要將這個字串以指標的形式進行返回,那麼如何在函式中正確返回該字串的內容呢? 例如,定義一個函式,要求該函式能夠返回一個指向字串“I love C.”的指標並能在主程式中正確得到該字串的內容

C++語言程式設計基礎》之函式學習筆記

引用的開銷比較小,因為只是傳遞指標,相當於變數多個名字 int i,j; int &ri=i; //ri引用 賦值為i的 j=10; ri=j; //ri賦值為j, 由於ri是引用,所以i也賦值為j 相當於i=j,引用是可以作為形參的 函式一般情況是單向傳遞的

C語言系列(二)有符號數和無符號數

1、你自已決定是否需要有正負: 就像我們必須決定某個量使用整數還是實數,使用多大的範圍數一樣,我們必須自已決定某個量是否需要正負。如果這個量不會有負值,那麼我們可以定它為帶正負的型別。 在計算機中,可以區分正負的型別,稱為有符型別(signed),無正負的型

Linux字符集和系統語言設置-LANG,locale,LC_ALL,POSIX等命令參數

linux lang locale lc_all 博文說明【前言】: 本文將通過個人口吻介紹Linux字符集和系統語言設置,包括LANG,locale,LC_ALL,POSIX等命令及參數詳解的相關知識,在目前時間點【2017年6月21號】下,所掌握的技術水平有限,可能會存在不少知識理解

vi和vim文本編輯器原理參數快捷鍵

vi vim linux 運維 系統 vi屬於Linux內核內置命令(vi編輯器)1、 vi/vim工作原理在學習vi/vim編輯器參數與快捷鍵之前首先得了解vi/vim的工作原理。vi/vim命令有三大模式:編輯模式、視圖模式、命令模式;不同模式可以執行不同的命令。 2、vim基於

window.parent window.topwindow.self

clas str 是否 set tex scrip 語法 strong style 在應用有frameset或者iframe的頁面時,parent是父窗口,top是最頂級父窗口(有的窗口中套了好幾層frameset或者iframe),self是當前窗口。 1. windo

Linux C語言程式設計(十五)——程序執行緒與訊號

1、程序 1.1 基本概念 每個程序在核心中都有一個程序控制塊( PCB)來維護程序相關的資訊, Linux核心的程序控制塊是task_struct結構體。 程序ID:統中每個程序有唯一的id,在C語言中用pid_t型別表示,其實就是一個非負整數。 程序狀態:有執行、掛起、

1079: C語言程式設計教程(第三版)課後習題8.2---求方程 的根,用三個函式分別求當b^2-4ac大於0等於0和小於0時的根,並輸出結果。從主函式輸入abc的值。【兩種方法】

題目描述 求方程 的根,用三個函式分別求當b^2-4ac大於0、等於0、和小於0時的根,並輸出結果。從主函式輸入a、b、c的值。 輸入 a b c 輸出 x1=? x2=? 樣例輸入 4

清華大學《C++語言程式設計進階》線上課程筆記06---繼承派生多型性

類的繼承 保持已有類的特性而構造新類的過程稱為繼承; 實現設計與程式碼的重用. 在已有類的基礎上新增自己的特性而產生新類的過程稱為派生 當新的問題出現,原有程式無法解決(或不能完全解決)時,需要對原有程式進行改造 派生類的構成 吸收基類成員 預設情況下派生類包含了全部基

《6.C語言巨集定義與預處理函式函式庫》

《6.C語言巨集定義與預處理、函式和函式庫》 第一部分、章節目錄 4.6.1.C語言預處理理論 4.6.2.C語言預處理程式碼實戰 4.6.3.巨集定義1 4.6.4.巨集定義2 4.6.5.函式的本質 4.6.6.函式的基本使用 4.6.7.遞迴函式 4.6.8.函式庫 4.6.9.字

C語言程式設計 細節總結(第10-11章 檔案預處理命令)

第10章 檔案 1.C語言系統把檔案當成一個流,按位元組進行處理 2.檔案按編碼方式分為二進位制檔案和ASCII檔案(文字檔案) 3.用檔案指標標識檔案,開啟檔案可取得該指標 4.對檔案進行操作,開啟->讀寫->關閉 5.檔案開啟方式只有四種:只讀、

C語言程式設計 學習筆記 12.3 多個原始碼檔案標頭檔案宣告

我們經常在做“分而治之”的事情(多個.c檔案): 1.main()裡的程式碼太長了適合分成幾個函式 2.一個原始碼檔案太長了適合分成幾個檔案 3.兩個獨立的原始碼檔案不能編譯成可執行的程式 對於(1),我們可以舉以下例子: 有個主函式main.c,有另外一個函式

C語言靜態變數和靜態函式

static C語言        C語言程式可以看成由一系列外部物件構成,這些外部物件可能是變數或函式。而內部變數是指定義在函式內部的函式引數及變數。外部變數定義在函式之外,因此可以在許多函式中使用。由於C語言不允許在一個函式中定義其

浙大版《C語言程式設計(第3版)》題目集(函式題)練習5-1

練習5-1 求m到n之和(10 分) 本題要求實現一個計算m~n(m 函式介面定義: int sum( int m, int n ); 其中m和n是使用者傳入的引數,保證有m < n。

浙大版《C語言程式設計(第3版)》題目集(函式題)練習5-3

練習5-3 數字金字塔(15 分) 本題要求實現函式輸出n行數字金字塔。 函式介面定義: void pyramid( int n ); 其中n是使用者傳入的引數,為[1, 9]的正整數。

c語言如何操作記憶體(資料型別函式在記憶體中解析簡介)

1、用變數名來訪問記憶體 (c語言對記憶體地址的封裝:資料型別、函式名) ---【直接訪問記憶體(使用地址)】              資料型別:表示一個記憶體格子的長度和解析方法。(記憶體編址的單位是一個位元組)               (int *) 0; 

C語言-全域性變數與區域性變數全域性函式與內部函式

變數的宣告與定義: Int a;是定義型宣告,既是宣告又是定義,需要分配儲存空間Extern a;是引用型宣告,只是宣告,不需要分配儲存空間1、全域性變數與區域性變數 全域性變數: 編譯時分配記憶體,放在靜態儲存區裡,習慣:首字母大寫;。可以使用extern擴充套件全域性

C語言程式設計基礎-09函式與返回值形參

函式 返回值 形參實參 函式 在大規模的程式中需要對語句進行分組管理,把相互之間聯絡比較緊密的語句合併成一組; 分組可以在多個不同層次上進行,最低一級分組的結果叫程式碼塊,程式碼塊由{}大括號包括; 在大括號前面新增     型別名 函式名()的就是函式; 函式的形式如 v