信號的基本概念
信號就是一個軟件中斷,可以打斷進程的執行,讓進程處理信號的事件
信號種類:
1-31信號是不可靠信號:信號有可能會丟失(非實時信號)
1.向進程發送非實時信號(該信號沒有被掛起),信號響應會嵌套(即正在響應某個函數時,有其它信號發來,進程會先去響應其它信號,結束後再繼續原來的任務)。
2.當進程正在響應某個信號時(該信號沒有被掛起),即響應函數正在執行的過程中,有相同的n個信號相繼發來,進程不會嵌套;
當執行完響應函數後,進程只會執行n個信號中的一個。
3.對非實時信號,掛起的信號不會重複(即有n個相同的信號被掛起,進程只會執行一次)
34-64信號是可靠信號:信號不會丟失(實時信號)
1.向進程發送實時信號(該信號沒有被掛起),信號響應會嵌套。
2.當進程正在響應某個信號時(該信號沒有被掛起),即響應函數正在執行的過程中,有相同的n個信號相繼發來,進程不會嵌套;
當執行完響應函數後,進程會繼續執行函數n次。
3.對實時信號,掛起的信號可以重複(即有n個相同的信號被掛起,進程會執行n次)
如果進程的掛起信號中含有實時和非實時信號,那麼進程優先響應實時信號並會從大到小依次響應,而非實時信號沒有固定的次序,甚至某個非實時信號會被丟失(這可說明爲什麼非實時信號被叫做不可靠信號)。
信號產生方式:
1.硬件產生:ctrl+c(SIGINT),ctrl+z(SIGTSTP),ctrl+|(SEGOUIT)
2.軟件產生:kill -信號號碼+進程號
eg:kill -9 12345
kill函數:kill(信號號, SIGINT);//給給定信號號的進程發送SIGINT信號
abort函數:void abort();給當前進程發送SIGABRT信號
raise函數:int raise(int sig);給當前進程發送sig信號
alarm函數:unsigned int alarm(unsigned int seconds);
seconds秒之後,給調用進程發送SIGALRM信號
seconds若爲0,則表示取消上一個定時器
返回值:上一個定時器的剩餘時間
alarm(3);//定時3秒
sleep(1);//表示alarm會響應2秒
alarm(0);//取消3秒定時器
core dumped:核心轉儲文件命名格式:core.pid
ulimit -a 查看
ulimit -c 設置core文件最大大小,單位爲kb ulimit -c 1024
調試方法:gdb ./main->core.file core.pid ->bt
信號的生命週期
信號在進程當中的註冊
非可靠信號(1~31):
如果待註冊的信號,在pending位圖當中已經存在了,則不再去添加當前信號(意味着不增加sigqueue節點)
如果待註冊的信號,在pending位圖中不存在,將pending位圖當中的對應的bit位置位1,然後添加sigqueue節點
可靠信號(34~64):
如果待註冊的信號,在pending位圖當中存在(意味着對應的bit位爲1),另外增加sigqueue節點(意味着這個信號也會被處理)
如果待註冊的信號,在pending位圖當中不存在,則更改pending位圖中對應的bit位,並且增加sigqueue節點
信號在進程當中的註銷
1.非可靠信號
將pending位圖當中的對應的bit位置爲0,並且將sigqueue節點刪除
2.可靠信號
如果註銷的信號,存在的sigqueue節點只有1個,則將pending位圖當中的對應的bit位置爲0,並且將sigqueue節點刪除
如果註銷的信號,存在sigqueue節點有多個,不能將pending位圖當中對應的bit位置爲0,而是刪除一個sigqueue節點
信號的處理
默認處理方式---SIG_DFL
忽略處理方式---SIG_IGN
自定義處理方式
signal函數:可以重置當前操作系統對信號的處理方式
typedef void (* sighandler_t)(int);
sighandler_t signal(int signum,sighandler_t handler);
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
void sigbackfunc(int sig)
{
printf("sig : [%d]\n", sig);
signal(2, SIG_DFL);//遇到2號信號之後,忽略處理
}
int main()
{
signal(SIGINT, sigbackfunc);//收到2號信號之後,自定義處理(轉到sigbackfunc函數)
while(1)
{
printf("hello~\n");
sleep(1);
}
return 0;
}
sigaction函數
int sigaction(int signum,const struct sigaction act,struct sigaction oldact);
act:表示將當前信號修改成一種處理方式,讓操作系統接收到這個信號的時候,調用哪一個函數處理
oldact:表示之前操作系統對收到該信號的時候 的處理方式
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
void sigbackfunc(int sig)
{
printf("%d\n", sig);
}
int main()
{
struct sigaction newact;
struct sigaction oldact;
newact.sa_handler = sigbackfunc;
newact.sa_flags = 0;
sigemptyset(&newact.sa_mask);
sigaction(2, &newact, &oldact);
while(1)
{
printf("hello\n");
sleep(1);
}
return 0;
}
信號捕捉流程
信號阻塞
信號阻塞不是說信號不能被註冊,而是說,從操作系統在判斷pending位圖是發現接受某個信號了,去查找block位圖對應的bit位
如果block對應的位爲1,則不處理該信號,sigqueue節點還是在的
如果block對應的位置爲0,則處理該信號
int sigprocmask(int how,const sigset_t set,sigset_t oldset);
how:SIG_BLOCK-->設置某個信號爲阻塞狀態,block(new)=block(old) | set
SIG_UNBLOCK-->設置某個信號爲非阻塞狀態,block(new)=block & (~set)
SIG_SETMASK-->設置新位圖block=set
block:0000 0000
set : 1111 1111
(阻塞信號) block(new)=block(old) | set 1111 1111
(非阻塞信號)block(new)=block & (~set) 1111 1111 & (0000 0000)-->0000 0000
注意
9號,19號信號是不能設置爲阻塞的
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
void sigcallback(int sig)
{
printf("sig : [%d]\n", sig);
}
int main()
{
signal(2, sigcallback);
signal(40, sigcallback);
//block位圖設置一下,也就是阻塞某些信號
//int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
sigset_t set, oldset;
sigemptyset(&set);
sigemptyset(&oldset);
//給set位圖編程全1
//int sigfillset(sigset_t *set);
sigfillset(&set);
sigprocmask(SIG_BLOCK, &set, &oldset);
getchar();
sigprocmask(SIG_UNBLOCK, &set, NULL);
while(1)
{
;
}
return 0;
}
競態條件
程序的不同的執行流,執行順序的不同,會導致程序結果的不同,這種我們稱之爲競態條件
重入:不同的執行流可以訪問同樣的資源(代碼)
可重入:不同的執行流可以訪問同樣的資源,不會對程序的結果產生影響
不可重入:不同的執行流可以訪問同樣的資源,但是對程序的結果產生影響
不可重入場景:全局變量,malloc和free ,絕大多數的庫函數
SIGCHLD--17號信號
SIGCHLD信號的默認行爲是不處理,我們可以更改掉SIGCHLD信號的默認處理函數,讓收到該信號的時候調用自定義的函數去完成進程等待,從而父進程就可以完成自己的邏輯,而不用調用wait進行阻塞了,意味着了,我們可以在回調函數中調用wait
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>
void sigcallback(int sig)
{
printf("sig : %d\n", sig);
wait(NULL);
}
int main()
{
signal(SIGCHLD, sigcallback);
pid_t pid = fork();
if(pid < 0)
{
return pid;
}
else if(pid == 0)
{
//child
printf("i am child\n");
sleep(3);
exit(1);
}
else
{
//wait
while(1)
{
printf("我不聽~~~\n");
sleep(1);
}
}
return 0;
}