1. 程式人生 > >linux學習-訊號相關函式

linux學習-訊號相關函式

未決:在訊號產生和遞送之間的時間間隔內,我們稱訊號是未決的。

阻塞訊號遞送:如果為程序產生了一個阻塞訊號,而且對該訊號的動作是預設動作或捕捉該訊號,則為該程序將此訊號保持為未決的,直到程序對此訊號解除阻塞,或者對此訊號的動作更改為忽略。

訊號遮蔽字:每個程序都有一個訊號遮蔽字,它規定了當前要阻塞遞送到該程序的訊號集。

函式kill和raise

#include<signal.h>
int kill(pid_t pid,int signo);
int raise(int signo);
                            返回值: 成功返回0,失敗返回-1

kill的pid引數 pid>0

將該訊號傳送程序ID為pid的程序 pid==0 將訊號傳送給與傳送程序同一程序組的所有程序,但不包 括系統程序集,既核心程序和init。 pid<0 將該程序傳送給其他程序組ID為pid絕對值的所有程序,不包括系統程序集 pid==-1將該訊號傳送給程序有許可權向它們傳送訊號的所有程序,不包括系統程序集 如上所述,程序傳送訊號是要許可權的,超級使用者可以傳送給所有的程序。非超級的,傳送者的實際使用者ID或者有效使用者ID必須等於接收者的實際使用者ID或者有效使用者ID。

1.函式alarm和pause alarm函式可以設定一個鬧鐘時間,超時產生SIGALRM訊號。如忽略或者不捕捉該訊號,系統預設動作是終止呼叫alarm函式的程序。

#include<unistd.h>
unsigned int alarm(unsigned int seconds);
                          返回值:0,或者是以前設定的鬧鐘時間的剩餘時間

每個程序只能有一個鬧鐘時間。如果在呼叫alarm之前已經為該程序註冊的鬧鐘時間還未超時,則該鬧鐘的剩餘時間作為本次alarm函式呼叫的值返回。以前註冊的鬧鐘時間則被新值代替。如果本次呼叫的seconds值是0,則取消以前的鬧鐘,其剩餘值,作為alarm函式的返回值。 pause函式呼叫程序掛起,直到捕捉到一個訊號

#include<ubistd.h>
int paush(void);     
                                 返回值:-1;errno設定為EINTR

給出一個用alarm和pause實現sleep的例項

#include<stdio.h>
#include<signal.h>
#include<setjmp.h>
#include<ubistd.h>

static jmp_buf env_alrm;
static void myalrm(int signo)         //訊號處理函式
{
	longjmp(env_alrm,1);              //區域性跳轉
	
}


int mysleep(int sec)
{
	int minsec,ret,tmp=0;
	if(signal(SIGALRM,myalrm)==SIG_EER)
		return sec;
	minsec=alrm(0);                  //得到上一次未超時定時器所剩的時間
	ret=sec>minsec?(sec-minsec):0;
	
	if(setjmp(env_alrm)==0)          //區域性跳轉
	{
		if(minsec)                   //考慮之前設定了定時器
			alarm(minsec<sec?minsec:sec);        //用時間短的做定時 
		else
			alarm(sec);
		    sleep(9);
		    pause();
		
	}
	tmp=alarm(0);                    //睡眠時,被其他訊號打斷而剩下的未睡眠時間
	if(minsec>sec){                  //若前面登記的定時器比睡眠時間長,退出後需要復位之前定時器剩下的時間
		alarm(minsec-sec);
	}
	
	if(tmp)
		ret+=tmp;
	return ret;
	
	
	
}


int main()
{
	int othersec;
	alarm(6);
	othersec=mysleep(3);
	printf("return %d\n",othersec);
	return 0;
}

2.函式sigprocmask 呼叫函式sigprocmask可以檢測和更改當前阻塞而不能遞送給程序的訊號集。

#include<signal.h>
int sigprocmask(int how,const sigset_t *restrict set,sigset_t *restrict oset);
                            返回值:成功返回0,失敗返回-1;

若oset為非空指標,程序當前的訊號遮蔽子通過oset返回。 若set為非空指標,引數how指示進行何種操作。若為空,則不改變當前訊號遮蔽字,how引數也無用。

how 說明
SIG_BLOCK 新set中的訊號加到訊號遮蔽字中
SIG_UNBLOCK 將set中訊號從當前訊號遮蔽子中取出
SIG_SETMASK 該程序新的訊號遮蔽是set指向的值

3.函式sigismember 函式sigismember用來測試引數signum 代表的訊號是否已加入至引數set訊號集裡。

int sigismember(const sigset_t *set,int signum)
返回值:有該訊號,返回1,無則返回0;出錯返回-1

4.函式sigpending sigpending函式返回在送往程序的時候被阻塞掛起的訊號集合。這個訊號集合通過引數set返回。

#include<signal.h>
int sigpengding(sigset_t *set);
                                             返回值:成功返回0,出錯返回-1

下面這個例程,用到了上面幾個函式

static void sig_quit(int signo)
{
	printf("caught sigquit\n");
}

int main()
{
 sigset_t newmak,oldmask,pendmsk;
 if(signal(SIGQUIT,sig_quit)==SIG_ERR)
 		printf("can not catch quit\n");

sigemptyset(&newmask);                                 //訊號集清空函式
sigaddmask(&newmask,SIGQUIT);                 //將訊號新增到訊號集中
if(sigprocmask(SIG_BLOCK,&newmask,&oldmask)<0)      //將訊號SIGQUIT新增到訊號遮蔽字中
        printf("err\n");
 sleep(5);
 if(sigpending(&pendmask)<0)                        //檢測當前程序中阻塞的訊號,通過pendmask返回
 		printf("sigpending err\n");
 if(sigismember(&pendmask,SIGQUIT))         //檢測SIGQUIT是否在pendmask訊號集中
 		printf("sigquit pending\n");
 
 if(sigprocmask(SIG_SETMASK,&oldmask,NULL)<0)   //將SIGQUIT訊號取消遮蔽
 		printf("err\n");
 sleep(5);
 exit(0);
}

5.函式sigaction sigaction函式的功能是檢查或者修改與指定訊號相關聯的動作。此函式取代了早期使用的signal函式。

#include<signal.h>
int sigaction(int signo,conststruct sigaction*restrict act,
struct sigaction*restrict oact);
返回值:成功返回0,失敗返回-1

給訊號signo設定新的訊號處理函式act, 同時保留該訊號原有的訊號處理函式oldact。 此函式用到了一個數據結構

struct sigaction{
  void (*sa_handler)(int);
   sigset_t sa_mask;
  int sa_flag;
  void (*sa_sigaction)(int,siginfo_t*,void*);
};

sa_handler欄位:包含一個訊號捕捉函式的地址。

sa_mask欄位:說明了一個訊號集,在呼叫該訊號捕捉函式之前,這一訊號集要加進程序的訊號遮蔽字中。僅當從訊號捕捉函式返回時再將程序的訊號遮蔽字復位為原先值。

sa_flags欄位:指定對訊號進行處理的 各個選項。有張表格,我就不列了,自己百度吧。簡單說明下面程式用到的兩個標誌 SA_NODEFER: 當訊號處理函式正在進行時,不堵塞對於訊號處理函式自身訊號功能。 SA_RESETHAND:當用戶註冊的訊號處理函式被執行過一次後,該訊號的處理函式被設為系統預設的處理函式。

sa_sigaction欄位:是一個替代的訊號處理程式,當sa_flags 為SA_SIGINFO時,使用該訊號處理程式。sa_sigaction和sa_handler,應用只能一次呼叫其中一個。

static void sig_quit(int signo)
{
 printf("catch quit\n");
}


int main()  
{  
        struct sigaction act,oldact;  
        act.sa_handler  = sig_quit;  
	    act.sa_flags = SA_NODEFER | SA_RESETHAND;     
        sigaction(SIGINT,&act,&oldact);                                  
       
        sleep(5);  
        printf("hello \n");
  
        return 0;  
}

6.函式sigsuspend sigsuspend函式和pause函式一樣,可以使程序掛起(進入睡眠狀態),直至有訊號發生。 sigsuspend函式的引數是一個訊號集,這個訊號集是用來遮蔽訊號的,訊號集中存放了要遮蔽的訊號。 如果該訊號集為空的話,sigsuspend就不遮蔽任何訊號,任何訊號都可以使程序從掛起狀態喚醒,這就與pause函式一樣了。

#include<signal.h>
int sigsuspend(const sigset_t *sigmask);
                                            返回值:-1,並將errno設定為EINTR

可以用於保護程式碼臨界區,使其不被特定的訊號中斷

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<stddef.h>
#include<unistd.h>
#include<signal.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<sys/termios.h>
#include<sys/ioctl.h>
#include<pwd.h>
#include<setjmp.h>
#include<time.h>

static void sig_init(int);

int main()
{
	sigset_t newmask,oldmask,waitmask;
	printf("hello");
	
	if(signal(SIGINT,sig_init)==SIG_ERR)
		perror("signal");
	sigemptyset(&waitmask);
	sigaddset(&waitmask,SIGUSR1);
	sigemptyset(&newmask);
	sigaddset(&newmask,SIGINT);
	
	if(sigprocmask(SIG_BLOCK,&newmask,&oldmask)<0)            //將SIGINT加入到訊號遮蔽字中,以oldmask返回
		perror("sigprocmask");
		
	printf("ok");
	if(sigsuspend(&waitmask)!=-1)                             //將sigusr1遮蔽
		perror("sigsuspend");
	
	if(sigprocmask(SIG_SETMASK,&oldmask,NULL)<0)              //將阻塞訊號SIGINT恢復
		perror("sigprocmask");
	exit(0);
	
	
}

static void sig_init(int signo)
{
	printf("how are you\n");
}