您好,登錄后才能下訂單哦!
volatile:防止編譯器性能優(yōu)化,與移植性有關(guān)。
#include<stdio.h> #include<signal.h> int done=0; void handle(int sig) { printf("get sig %d\n",sig); done=1; } int main() { signal(SIGINT,handle); while(!done); }
Makefile:
my_volatile:my_volatile.c
gcc -o $@ $^ -O3
.PHONY:clean
clean:
rm -f my_volatile
在while循環(huán)中:沒(méi)有寫入修改done的值,編譯器會(huì)在讀取變量的時(shí)候,將其從內(nèi)存拿出存入寄存器并比較,在下次讀取時(shí),直接從寄存器中取該變量的值。
而在信號(hào)處理函數(shù)中,有修改done的值,需寫回內(nèi)存。
可指定編譯器優(yōu)化級(jí)別
編譯器優(yōu)化級(jí)別:
gcc -o my_volatile my_volatile.c -O0//不優(yōu)化
gcc -o my_volatile my_volatile.c -O1//默認(rèn)
gcc -o my_volatile my_volatile.c -O2
gcc -o my_volatile my_volatile.c -O3//優(yōu)化級(jí)別最高
此時(shí)會(huì)發(fā)生內(nèi)存級(jí)別的不一致:寄存器:0,內(nèi)存:1.
運(yùn)行結(jié)果:
要改變此程序顯示預(yù)期效果,只需定義done為:volatile int done=0;//這樣每次使用done時(shí)都會(huì)從內(nèi)存中取done的值。
sig_atomic_t:由C語(yǔ)言提供。
雖然C代碼只有一行,但是在32位機(jī)上對(duì)一個(gè)64位的long long變量賦值需要兩條指令完成,因此不是原子操作。同樣地,讀取這個(gè)變量到寄存器需要兩個(gè)32位寄存器才放得下,也需要兩條指令, 不是原子操作。
為了解決這些平臺(tái)相關(guān)的問(wèn)題,C標(biāo)準(zhǔn)定義了一個(gè)類型sig_atomic_t,在不同平臺(tái)的C語(yǔ)言庫(kù)中取不同的類型,例如在32位機(jī) 上定義sig_atomic_t為int類型。
竟態(tài)條件:
由于異步事件在任何時(shí)候都有可能發(fā)生(這里的異步事件指出現(xiàn)更優(yōu) 先級(jí)的進(jìn)程),如果我們寫程序時(shí)考慮不周密,就可能由于時(shí)序問(wèn)題而導(dǎo)致錯(cuò)誤,這叫做競(jìng)態(tài)條件 (Race Condition)。
#include<stdio.h> #include<signal.h> #include<string.h> void handler(int sig) { //do nothing } int my_sleep(int time) { struct sigaction act; act.sa_handler=handler; act.sa_flags=0; sigemptyset(&act.sa_mask); struct sigaction old; memset(&old,'\0',sizeof(old)); sigaction(SIGALRM,&act,&old);//注冊(cè)信號(hào)處理函數(shù) alarm(time);//time秒后讓系統(tǒng)發(fā)SIGALRM信號(hào) pause();//內(nèi)核切換到別的進(jìn)程運(yùn)行 int ret=alarm(0); sigaction(SIGALRM,&old,NULL);//恢復(fù)默認(rèn)信號(hào)處理動(dòng)作 return ret; } int main() { while(1) { printf("I am sleep...\n"); my_sleep(5); } return 0; }
雖然alarm(nsecs)緊接著的下一行就是pause(),但是無(wú)法保證pause()一定會(huì)在調(diào)用alarm(nsecs)之 后的nsecs秒之內(nèi)被調(diào)用。
在調(diào)用pause之前屏蔽SIGALRM信號(hào)使它不能提前遞達(dá)就可以了。
1. 屏蔽SIGALRM信號(hào);
2. alarm(nsecs);
3. 解除對(duì)SIGALRM信號(hào)的屏蔽;
4. pause();
從解除信號(hào)屏蔽到調(diào)用pause之間存在間隙,SIGALRM仍有可能在這個(gè)間隙遞達(dá)。
要是“解除信號(hào)屏蔽”和“掛起等待信號(hào)”這兩步能合并成一個(gè)原子操作就好了,這正是sigsuspend
函數(shù)的功 能。 sigsuspend包含了pause的掛起等待功能,同時(shí)解決了競(jìng)態(tài)條件的問(wèn)題,在對(duì)
時(shí)序要求嚴(yán)格的場(chǎng)合下都應(yīng)該調(diào)用sigsuspend不是pause。
注:調(diào)用sigsuspend時(shí),進(jìn)程的信號(hào)屏蔽字由sigmask參數(shù)指定,可以通過(guò)指定sigmask來(lái)臨時(shí)
解除對(duì)某 個(gè)信號(hào)的屏蔽,然后掛起等待,當(dāng)sigsuspend返回時(shí),進(jìn)程的信號(hào)屏蔽字恢復(fù)為原
來(lái)的值,如果原來(lái)對(duì)該信號(hào)是屏蔽的,從sigsuspend返回后仍然是屏蔽的。
#include<stdio.h> #include<signal.h> void handle(int sig) { //do nothing } int sleep(int time) { struct sigaction oldact,newact; sigset_t newmask,oldmask,suspmask; newact.sa_handler=handle; newact.sa_flags=0; sigemptyset(&newact.sa_mask); sigaction(SIGALRM,&newact,&oldact);//注冊(cè)SIGALRM的信號(hào)處理函數(shù) sigemptyset(&newmask); sigaddset(&newmask,SIGALRM); sigprocmask(SIG_BLOCK,&newmask,&oldmask);//屏蔽SIGALRM信號(hào); alarm(time); suspmask=oldmask; sigdelset(&suspmask,SIGALRM);//解除suspmask中SIGALRM信號(hào)的屏蔽; sigsuspend(&suspmask);//用suspmask去替換PCB中的block表,從而解除對(duì)SIGALRM信號(hào)的阻塞 int ret=alarm(0); sigaction(SIGALRM,&oldact,NULL); sigprocmask(SIG_SETMASK,&oldmask,NULL);//恢復(fù)之前的系統(tǒng)默認(rèn)處理信號(hào)方式 return ret; } int main() { while(1) { printf("I am sleep\n"); sleep(5); } return 0; }
子進(jìn)程在終止時(shí)會(huì)給父進(jìn)程發(fā)SIGCHLD信號(hào),該信號(hào)的默認(rèn)處理動(dòng)作是忽略,父進(jìn)程可以自定義SIGCHLD信號(hào)的處理函數(shù),這樣父進(jìn)程只需專心處理自己的工作,不必關(guān)心子子進(jìn)程了,子進(jìn)程終止時(shí)會(huì)通知父進(jìn)程,父進(jìn)程在信號(hào)處理函數(shù)中調(diào)用wait清理子進(jìn)程即可。
優(yōu)點(diǎn):沒(méi)花費(fèi)時(shí)間在等待上,直到收到信號(hào)(異步信號(hào))
#include<stdio.h> #include<stdlib.h> #include<signal.h> #include<unistd.h> void my_sigchld(int sig) { int status=0; pid_t ret=waitpid(-1,&status,0); if(ret>0) { printf("sig: %d,code: %d\n",status&0xff,(status>>8)&0xff); } } int main() { pid_t tid=fork(); if(tid<0) { perror("fork"); exit(1); } else if(tid==0) { sleep(10);//保證父進(jìn)程已注冊(cè)完信號(hào)處理函數(shù),父,子進(jìn)程誰(shuí)先運(yùn)行不確定 printf("child is quit!\n"); exit(1); } else { signal(SIGCHLD,my_sigchld); while(1); } return 0; }
但是,如果一個(gè)父進(jìn)程有100個(gè)子進(jìn)程,收到好多SIGCHLD信號(hào),只會(huì)保存一份,只能wait一份,故應(yīng)該修改代碼防止此情況發(fā)生
void my_sigchld(int sig) { int status=0; pid_t ret; while((ret=waitpid(-1,&status,0))>0) { printf("sig: %d,code: %d\n",status&0xff,(status>>8)&0xff); } }
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。