信号概述
1.對于Linux來說,實際信号是軟中斷,許多重要的程式都需要處理信号。信号為Linux提供了一種處理異步事件的方法。比如:終端使用者輸入了ctrl+c來中斷程式,會通過信号機制停止一個程式。
2.信号的名字和編号:
- 每個信号都有一個名字和編号,這些名字都以"SIG"開頭,例如:“SIGIO”,"SIGCHLD"等等
- 信号定義在signal.h頭檔案中,信号名都定義為正整數
- 具體的信号名稱可以用指令 kill -l 來檢視信号的名字以及序号,信号是從1開始編号的,不存在0号信号。kill對于信号0有特殊的應用
下列是一些信号的具體作用
3.信号的處理:
信号的處理有三種方法,分别是:忽略,捕捉和預設動作。
- 忽略信号,大多數信号可以使用這個方式來處理,但是有兩種信号不能被忽略(分别是SIGKILL和SIGSTOP)。因為它們向核心和超級使用者提供了程序終止和停止的可靠方法,如果忽略了,那麼這個程序就變成了沒人能管理的程序,顯然是核心設計者不希望看到的場景
- 捕捉信号,需要告訴核心,使用者希望如何處理某一種信号,說白了就是寫一個信号處理函數,然後将這個函數告訴核心,當該信号産生時,由核心來調用使用者自定義的函數,以此來實作某種信号的處理
- 系統預設動作,對于每個信号來說,系統都對應由預設的處理動作,當發生了該信号,系統會自動運作。具體的信号預設動作可以使用指令 man 7 signal 來檢視
4.對于常用的kill 指令就是一個發送信号的工具。例如:kill -9 + 程序ID來殺死程序或者kill -SIGKILL + 程序ID 也可以殺死程序
信号程式設計
1.信号處理函數的注冊:signal()函數
*typedef void (sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
參數說明:
- signum:要捕捉的信号(Linux下檢視信号:kill -l ,9号SIGKILL和19号SIGSTOP信号不能被捕捉)
- handler:我們要對信号進行的處理方式。信号的處理方式一般有三種:忽略此信号(SIG_IGN),恢複對信号的系統預設處理(SIG_DFL)
demo:
#include <signal.h>
#include <stdio.h>
void handler(int signum)
{
printf("get signum = %d\n",signum);
switch(signum){
case 2:
printf("SIGINT\n");
break;
case 9:
printf("SIGKILL\n");
break;
case 10:
printf("SIGUSR1\n");
break;
}
printf("never quit\n");
}
int main()
{
signal(SIGINT,handler);
signal(SIGKILL,handler);
signal(SIGUSR1,handler);
while(1);
return 0;
}
2.信号處理發送函數:kill()函數
int kill(pid_t pid, int sig);
功能:用于任何程序組或程序發送信号。
傳回值:成功傳回0,失敗傳回-1
參數說明:
- pid:有四種情況:1)大于0:pid是信号欲送往的程序的辨別;等于0:信号将送往所有與調用kill()的那個程序屬同一個使用組的程序;3)等于-1:信号将送往所有調用程序有權給其發送信号的程序,除了程序1(init程序);4)小于-1:信号将送往以 -pid 為組辨別的程序
- sig:準備發送的信号代碼,假如其值為0,則沒有任何信号送出,但是系統會執行錯誤檢查,通常會利用sig值為0來檢驗某個程序是否仍在執行
demo:
#include <signal.h>
#include <stdio.h>
#include <sys/types.h>
int main(int argc, char **argv)
{
int signum;
int pid;
char cmd[128] = {0};
signum = atoi(argv[1]); // 因為傳進來的參數是字元串,是以要轉化成整形數
pid = atoi(argv[2]);
printf("signum=%d,pid=%d\n",signum,pid);
sprintf(cmd,"kill -%d %d",signum,pid);
system(cmd);
printf("send signal ok\n");
return 0;
}
信号攜帶消息程式設計
**1.信号注冊函數:sigaction()函數
int sigaction(int signum, const struct sigaction act, struct sigaction oldact);
功能:檢查或修改與指定信号相關聯的處理動作(可同時兩種操作)
傳回值:成功傳回0,失敗傳回-1,錯誤原因置于errno中
參數說明:
- signum:要捕捉的信号名稱(除了SIGKILL和SIGSTOP)
- act:指定新的信号處理方式,它是一個結構體
struct sigaction
{
void (*sa_handler)(int); // 信号處理程式,不接受額外資料
void (*sa_sigaction)(int, siginfo_t *, void *); // 信号處理程式,能接受額外資料,和sigqueue配合使用
sigset_t sa_mask; // 用來設定在處理該信号時暫時将sa_mask指定的信号阻塞
int sa_flags; // 影響信号的行為,SA_SIGINFO表示能夠接受資料
void (*sa_restorer)(void);
};
SA_SIGINFO:
siginfo_t
{
int si_signo;
int si_errno;
int si_code;
int si_trapno;
pid_t si_pid;
uid_t si_uid;
int si_status;
clock_t si_utime;
clock_t si_stime;
sigval_t si_value;
int si_int;
void *si_ptr;
int si_overrun;
int si_timerid;
void *si_addr;
long si_band;
int si_fd;
short si_addr_lsb;
}
- oldact:原來對信号的處理方式。不關心的話寫NULL
2.信号發送函數:sigqueue()函數
int sigqueue(pid_t pid, int sig, const union sigval value);
傳回值:成功傳回0,失敗傳回-1
參數說明:
- pid:指定接收信号的程序ID
- sig:确定即将發送的信号
- 第三個參數是一個聯合體
union sigval
{
int sival_int;
void *sival_ptr;
};
說明:使用這兩個函數之前,必須要有幾個操作完成
- 使用sigaction函數安裝信号處理程式時,制定了SA_SIGINFO的标志
- sigaction結構體中的sa_sigaction成員提供了信号捕捉函數。如果實作的是sa_handler成員,那麼将無法擷取額外攜帶的資料
// 接收信号端
#include <stdio.h>
#include <signal.h>
#include <string.h>
void handler(int signum, siginfo_t *info, void *context)
{
printf("get signum %d\n",signum);
if(context != NULL){
printf("get data=%d\n",info->si_int);
printf("from:%d\n",info->si_pid);
}
}
int main()
{
struct sigaction act;
printf("pid=%d\n",getpid());
act.sa_sigaction = handler;
act.sa_flags = SA_SIGINFO;
sigaction(SIGUSR1,&act,NULL);
while(1);
return 0;
}
// 發送信号端
#include <signal.h>
#include <stdio.h>
int main(int argc,char **argv)
{
int signum;
int pid;
signum = atoi(argv[1]);
pid = atoi(argv[2]);
union sigval value;
value.sival_int = 100;
sigqueue(pid,signum,value);
printf("%d, done\n",getpid());
return 0;
}
運作結果: