進程信號

信號的基本概念
信號就是一個軟件中斷,可以打斷進程的執行,讓進程處理信號的事件
信號種類:
進程信號
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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章