網絡編程(13)—— 利用信號處理函數signal和sigaction銷燬殭屍進程

轉自:https://blog.csdn.net/hyman_c/article/details/52771885

一、引言
        上一文中介紹了利用wait函數和waitpid函數來銷燬殭屍進程,本文主要介紹利用Linux中的信號處理機制來銷燬殭屍進程。linux中的信號處理類似於windows中的消息處理,基本的編程步驟就是先在系統中註冊信號和對應的信號處理函數,我們用代碼或者 系統自動產生註冊的信號時會調用該信號處理函數。我們下面要介紹的signal函數和sigaction函數實際上就是我們用來向操作系統註冊信號和信號處理函數的api。

二、signal函數
       signal函數的原型如下:

#include <signal.h>
typedef void (*sighandler_t)(int);
void signal(int signum, sighandler_t handler);

signum,要處理的信號,一般用宏來表示,如: 
    SIGALRM,通過alarm函數觸發的鬧鐘信號; 
    SIGINT,輸入Ctrl+C時系統自動產生的信號;
    SIGCHIL,子進程終止 時產生的信號;
handler,信號處理器,就是我們自己定義的信號處理函數的函數指指針。 
 
    這裏簡單介紹下alarm函數:
#include <unistd.h>
 unsigned int alarm(unsigned int seconds);

       該函數接收一個int類型的參數,表示過了參數傳遞的秒之後,就會產生一個SIGALRM信號,當傳給其0時,表示之前預約的SIGALRM將取消。特別指出的是如果調用該函數時,未指定對應的SIGALRM信號處理器,該函數將會迫使進程強制退出,此時就類似於exit()的作用了。
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<signal.h>
void timeout(int sig)
{
    if(sig==SIGALRM)
            puts("Time out");
    alarm(2);
}
void keycontrol(int sig)
{
    if(sig==SIGINT)
            puts("CTRL + C pressed");
}
int main()
{
    int i;
    signal(SIGALRM,timeout);
    signal(SIGINT,keycontrol);
    alarm(2);
    for(i=0;i<3;i++)
    {
        puts("wait...");
        sleep(100);
    }
    return 0;
}
       上述代碼實現的功能是分別用signal註冊SIGALRM和SIGINT的信號處理函數timeout和keycontrol。用alarm函數註冊一個鬧鐘,2秒之後發送一個SIGALRM信號,然後到達2秒後,timeout被調用,打印出字符串“Time out”。如此反覆3次。如果用戶按下了Ctrl+C,會產生一個SIGINT信號,該信號的處理函數keycontrol會打印一個字符串“CTRL + C pressed”。我們先來看下結果:

[Hyman@Hyman-PC csdn]$ ./a.out 
wait...
Time out
wait...
^CCTRL + C pressed
wait...
Time out
另外需要注意的是: 
1、按下Ctrl + C時,信號會被signal捕獲,而不會再終止進程。 
2、調用信號處理器時將會喚醒正在sleep中的程序,也就是說上述程序不會再sleep 100秒 
 
二、sigaction函數
        sigaction函數完全可以替代signal函數,而且使用sigaction函數還有一個好處就是:signal函數會隨着Unix版本的不同而不同,但是每個版本的sigaction的都是一樣的。sigacton的原型如下:
#include <signal.h>
int sigaction(int signum, const struct sigaction *act,
                     struct sigaction *oldact);

signum,和signal的參數一樣,傳遞的都是信號信息。 
act,sigaction結構體,裏面包含信號處理函數等信息。 
oldact,通過此參數獲取之前註冊的sigaction結構體指針,如果不需要,直接設置0 
 
先看一下sigaction結構體:

struct sigaction
{
    void (*sa_handler)(int);
    sigset_t sa_mask;
    int sa_flags;
}
sa_handler就是我們需要處理信號的信號處理函數指針,其他兩個參數用來指定信號相關的選項和特性,一般情況下指定爲0即可,指定的方法將下面的代碼。

#include<stdio.h>
#include<unistd.h>
#include<signal.h>
 
void timeout(int sig)
{
    if(sig==SIGALRM)
            puts("Time out");
    alarm(2);
}
 
int main()
{
    int i=0;
    struct sigaction act;
    act.sa_handler=timeout;
    sigemptyset(&act.sa_mask);
    act.sa_flags=0;
    sigaction(SIGALRM,&act,0);
    alarm(2);
    for(i=0;i<3;i++)
    {
        puts("wait ...");
        sleep(100);
    }
    return 0;
}
上述代碼實現了和上面signal的代碼同樣的功能,只不過沒有處理SIGINT信號,結果我們不再展示。下面主要介紹下利用sigaction處理子進程的結束:

#include<stdio.h>
#include<unistd.h>
#include<signal.h>
#include<sys/wait.h>
 
void childproHandler(int sig)
{
    int status;
    pid_t pid;
    if(sig==SIGCHLD)
    {
        pid = waitpid(-1,&status,WNOHANG);
        if(WIFEXITED(status))
        {
            printf("removed prc id:%d \n",pid);
            printf("child send:%d \n",WEXITSTATUS(status));
        }
    }
}
 
int main()
{
    pid_t pid;
    struct sigaction act;
    act.sa_handler=childproHandler;
    sigemptyset(&act.sa_mask);
    act.sa_flags=0;
    sigaction(SIGCHLD,&act,0);
    pid=fork();
    if(pid==0)
    {
        puts("I'm child process");
        sleep(10);
        return 12;
    }
    else
    {
        printf("child process id %id\n",pid);
        pid=fork();
        if(pid==0)
        {
            puts("I'm child process");
            sleep(10);
            exit(24);
        }
        else
        {
            int i=0;
            printf("child process id:%d\n",pid);
            for(i=0;i<5;i++)
            {
                puts("wait ...");
                sleep(5);
            }
        }
    }
    return 0;
}
第6行我們定義了一個SIGCHIL的信號處理函數,在這個函數中我們用waitpid來回收子進程資源。請注意signal和sigaction只是信號處理函數的註冊api,其本身沒有銷燬殭屍進程的作用,我們還需要藉助wait或者waitpid來銷燬殭屍進程。

第24到28行,分別定義sigaction結構體變量以及利用sigaction註冊信號已經對應的信號處理函數。

第29和39行,分別fork一個子進程,等到這兩個子進程結束,操作系統就會產生一個SIGCHIL信號,就會調用我們定義的信號處理函數,輸出我們定義的打印信息。

        結果如下:

[Hyman@Hyman-PC csdn]$ ./a.out 
child process id 2472d
child process id:2473
wait ...
I'm child process
I'm child process
wait ...
wait ...
removed prc id:2472 
child send:12 
wait ...
wait ...
[Hyman@Hyman-PC csdn]$ 

 


--------------------- 
 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章