linux 信号和信号量编程
對于 Linux來說,實(shí)際信號是軟中斷,許多重要的程序都需要處理信號。信號,為 Linux 提供了一種處理異步事件的方法。比如,終端用戶輸入了 ctrl+c 來中斷程序,會通過信號機(jī)制停止一個(gè)程序。
信號概述
信號的名字和編號:
每個(gè)信號都有一個(gè)名字和編號,這些名字都以“SIG”開頭,例如“SIGIO ”、“SIGCHLD”等等。
信號定義在signal.h頭文件中,信號名都定義為正整數(shù)。
具體的信號名稱可以使用kill -l來查看信號的名字以及序號,信號是從1開始編號的,不存在0號信號。kill對于信號0又特殊的應(yīng)用。
信號的處理:
??????信號的處理有三種方法,分別是:忽略、捕捉和默認(rèn)動作
??????忽略信號:大多數(shù)信號可以使用這個(gè)方式來處理,但是有兩種信號不能被忽略(分別是 SIGKILL和SIGSTOP)。因?yàn)樗麄兿騼?nèi)核和超級用戶提供了進(jìn)程終止和停止的可靠方法,如果忽略了,那么這個(gè)進(jìn)程就變成了沒人能管理的的進(jìn)程,顯然是內(nèi)核設(shè)計(jì)者不希望看到的場景
??????捕捉信號:需要告訴內(nèi)核,用戶希望如何處理某一種信號,說白了就是寫一個(gè)信號處理函數(shù),然后將這個(gè)函數(shù)告訴內(nèi)核。當(dāng)該信號產(chǎn)生時(shí),由內(nèi)核來調(diào)用用戶自定義的函數(shù),以此來實(shí)現(xiàn)某種信號的處理。
??????系統(tǒng)默認(rèn)動作:對于每個(gè)信號來說,系統(tǒng)都對應(yīng)由默認(rèn)的處理動作,當(dāng)發(fā)生了該信號,系統(tǒng)會自動執(zhí)行。不過,對系統(tǒng)來說,大部分的處理方式都比較粗暴,就是直接殺死該進(jìn)程。(kill -9 pid 或者 kill -SIGKILL pid)殺死進(jìn)程
具體的信號默認(rèn)動作可以使用man 7 signal來查看系統(tǒng)的具體定義。
信號處理函數(shù)的注冊
信號處理函數(shù)的注冊不只一種方法,分為入門版和高級版
(1)入門版:函數(shù)signal
(2)高級版:sigqueue
信號發(fā)送函數(shù)也不止一個(gè),同樣分為入門版和高級版
(1)入門版:kill
(2)高級版:sigqueue
signal
代碼演示
#include <signal.h> #include<stdio.h> // typedef void (*sighandler_t)(int);// sighandler_t signal(int signum, sighandler_t handler);void handler(int signum)//信號處理函數(shù),捕捉信號 {printf("get signal=%d\n",signum);switch(signum){case 2:printf("SIGINT\n");break;case 9:printf("SIGKILL\n");case 10:printf("SIGUSER1\n");}printf("never quite\n"); } int main() {signal(SIGINT,handler);//如果將handler改為SIG_ICN即可忽略函數(shù)signal(SIGKILL,handler);signal(SIGUSR1,handler);while(1); }kill發(fā)送消息——低級版
#include <sys/types.h>#include <signal.h>int kill(pid_t pid, int sig);代碼實(shí)戰(zhàn)
#include <signal.h> #include<stdio.h> #include <stdlib.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]);//atoi將阿斯科碼轉(zhuǎn)化為整數(shù)printf("num=%d,pid=%d\n",signum,pid); // kill(pid,signum);//用來發(fā)信號sprintf(cmd,"kill -%d %d",signum,pid);//cmd是目標(biāo)字符串,第二個(gè)參數(shù)是想要的目標(biāo)字符串的長相system(cmd);//用system調(diào)用腳本發(fā)信號printf("send signal ok\n");return 0; }sigaction原型(安裝信號處理程序)
#include <signal.h> int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact); //第一個(gè)參數(shù)是信號,第二個(gè)是sigaction結(jié)構(gòu)體用來綁定某些參數(shù),第三個(gè)參數(shù)也是sigaction結(jié)構(gòu)體,用來備份原有的信號操作,如不需要則設(shè)為NULL struct sigaction {void (*sa_handler)(int); //信號處理程序,不接受額外數(shù)據(jù),SIG_IGN 為忽略,SIG_DFL 為默認(rèn)動作void (*sa_sigaction)(int, siginfo_t *, void *); //信號處理程序,能夠接受額外數(shù)據(jù)和sigqueue配合使用,第一個(gè)參數(shù)是信號處理函數(shù),第三個(gè)是指針空無數(shù)據(jù),非空有數(shù)據(jù)sigset_t sa_mask;//阻塞關(guān)鍵字的信號集,可以再調(diào)用捕捉函數(shù)之前,把信號添加到信號阻塞字,信號捕捉函數(shù)返回之前恢復(fù)為原先的值。int sa_flags;//影響信號的行為SA_SIGINFO表示能夠接受數(shù)據(jù)}; //回調(diào)函數(shù)句柄sa_handler、sa_sigaction只能任選其一關(guān)于void (*sa_sigaction)(int, siginfo_t *, void );處理函數(shù)來說還需要有一些說明。void 是接收到信號所攜帶的額外數(shù)據(jù);而struct siginfo這個(gè)結(jié)構(gòu)體主要適用于記錄接收信號的一些相關(guān)信息。
siginfo_t {int si_signo; /* Signal number */int si_errno; /* An errno value */int si_code; /* Signal code */int si_trapno; /* Trap number that causedhardware-generated signal(unused on most architectures) */pid_t si_pid; /* Sending process ID */uid_t si_uid; /* Real user ID of sending process */int si_status; /* Exit value or signal */clock_t si_utime; /* User time consumed */clock_t si_stime; /* System time consumed */sigval_t si_value; /* Signal value 是結(jié)構(gòu)體*/int si_int; /* POSIX.1b signal */void *si_ptr; /* POSIX.1b signal */int si_overrun; /* Timer overrun count; POSIX.1b timers */int si_timerid; /* Timer ID; POSIX.1b timers */void *si_addr; /* Memory location which caused fault */int si_band; /* Band event */int si_fd; /* File descriptor */ }其中的成員很多,si_signo 和 si_code 是必須實(shí)現(xiàn)的兩個(gè)成員。可以通過這個(gè)結(jié)構(gòu)體獲取到信號的相關(guān)信息。
信號發(fā)送函數(shù)——高級版
接收端代碼實(shí)戰(zhàn)
#include <signal.h> #include<stdio.h>// int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact); 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("get data=%d\n",info->si_value.sival_int);printf("from: %d\n",info->si_pid);}} int main() {struct sigaction act;printf("ps=%d\n",getpid());act.sa_sigaction=handler;//收到信號后調(diào)用handle處理信號act.sa_flags=SA_SIGINFO;//SA_SIGINFO表示能接收數(shù)據(jù)sigaction(SIGUSR1,&act,NULL);while(1);return 0; }發(fā)送端代碼實(shí)戰(zhàn)
#include<stdio.h> #include <signal.h> //int sigqueue(pid_t pid, int sig, const union sigval value); int main(int argc,char**argv) {int signum;int pid;signum=atoi(argv[1]);pid=atoi(argv[2]);union sigval value;value.sival_int=100sigqueue(pid,signum,value);printf("pid =%d\n",getpid());printf("over\n"); return 0; }臨界資源:
多道程序系統(tǒng)中存在許多進(jìn)程,它們共享各種資源,然而有很多資源一次只能供一個(gè)進(jìn)程使用。一次僅允許一個(gè)進(jìn)程使用的資源稱為臨界資源。許多物理設(shè)備都屬于臨界資源,如輸入機(jī)、打印機(jī)、磁帶機(jī)等。
信號量
信號量(semaphore)與已經(jīng)介紹過的 IPC 結(jié)構(gòu)不同,它是一個(gè)計(jì)數(shù)器。信號量用于實(shí)現(xiàn)進(jìn)程間的互斥與同步,而不是用于存儲進(jìn)程間通信數(shù)據(jù)。
特點(diǎn)
1、 信號量用于進(jìn)程間同步,若要在進(jìn)程間傳遞數(shù)據(jù)需要結(jié)合共享內(nèi)存。
2、信號量基于操作系統(tǒng)的 PV 操作,程序?qū)π盘柫康牟僮鞫际窃硬僮鳌?/p>
3、 每次對信號量的 PV 操作不僅限于對信號量值加 1 或減 1,而且可以加減任意正整數(shù)。
4、 支持信號量組。
原型:
??????最簡單的信號量是只能取 0 和 1 的變量,這也是信號量最常見的一種形式,叫做二值信號量(Binary Semaphore)。而可以取多個(gè)正整數(shù)的信號量被稱為通用信號量。
??????Linux 下的信號量函數(shù)都是在通用的信號量數(shù)組上進(jìn)行操作,而不是在一個(gè)單一的二值信號量上進(jìn)行操作。
當(dāng)semget創(chuàng)建新的信號量集合時(shí),必須指定集合中信號量的個(gè)數(shù)(即num_sems),通常為1; 如果是引用一個(gè)現(xiàn)有的集合,則將num_sems指定為 0 。
代碼實(shí)戰(zhàn)
#include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> #include<stdio.h> // int semget(key_t key, int nsems, int semflg); //int semctl(int semid, int semnum, int cmd, ...); //int semop(int semid, struct sembuf *sops, unsigned nsops);//取鑰匙的函數(shù),第一個(gè)參數(shù)是信號量ID,第二個(gè)是配置信號量的個(gè)數(shù),第三個(gè)是第二項(xiàng)的個(gè)數(shù) union semun {int val; /* Value for SETVAL */struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */unsigned short *array; /* Array for GETALL, SETALL */struct seminfo *__buf; /* Buffer for IPC_INFO(Linux-specific) */}; void vPutBackKey(int id) {struct sembuf set;set.sem_num=0;set.sem_op=+1;set.sem_flg=SEM_UNDO;semop(id,&set,1);printf("put back key\n"); } void pGetKey(int id) {struct sembuf set;set.sem_num=0;//信號量的編號set.sem_op=-1;//拿鑰匙后原來的鑰匙減一set.sem_flg=SEM_UNDO;//SEM_UNDO表示等待,IPC_NOWAIT表示不等待semop(id,&set,1);printf("get key\n"); } int main (int argc,char**argv) {int semid;key_t key=ftok(".",2);semid=semget(key,1,IPC_CREAT|0666);//第二個(gè)參數(shù)代表信號量集所含信號量個(gè)數(shù),flag表示如果沒有信號怎么做。IPC_CREAT表示若信號量不存在則創(chuàng)建它,0666表示信號量的權(quán)限union semun initsem;initsem.val=0;//剛開始里面是沒有鑰匙的狀態(tài)semctl(sempid,0,SETVAL,initsem);//將信號量初始化,第一個(gè)參數(shù)是信號量集的ID,第二個(gè)參數(shù)是代表要操作第幾個(gè)信號量 ,0代表第0個(gè)。第三個(gè)參數(shù)是要對信號量如何操作,SETVAL表示設(shè)置信號量的值設(shè)置為initsem,initsem.val=0表示有一把鑰匙。pid_t pid=fork();if(pid>0){pGetKey(semid);printf("this is father\n");vPutBackKey(semid);semctl(semid,0,IPC_RMID);//銷毀信號量}else if(pid==0){//pGetKey(semid);printf("this is child\n");vPutBackKey(semid);}else{printf("fork error\n");}return 0; }本文參考以下兩篇博文編寫:
信號量
信號
總結(jié)
以上是生活随笔為你收集整理的linux 信号和信号量编程的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C++跨平台开发——SOCKET网络编程
- 下一篇: Linux上线程开发API概要(线程)