1. 程式人生 > >linux程式設計---訊號中斷處理

linux程式設計---訊號中斷處理

訊號

linux提供的訊號機制是一種程序間非同步的通訊機制,在實現上是一種軟中斷。訊號可以導致一個正在執行的程序被另一個非同步程序中斷。

訊號的處理流程

產生訊號:產生訊號有多種說法。一個程序建立一個訊號用於傳送給另一個程序叫傳送一個訊號;核心建立一個訊號叫生成一個訊號;

一個程序向自己傳送一個訊號叫喚起一個訊號

為使某個訊號到來時程序能夠執行相應的中斷服務程式,即設定某訊號到來時執行的程式碼,稱為安裝中斷

如果一個訊號被正確傳送到一個程序稱為訊號被遞給。

如果一個訊號的遞送導致一段處理程式被執行,稱為該訊號被捕捉。

如果一個訊號被髮送並且還沒有引起任何動作(一般是對應程序阻塞了此訊號),稱為訊號處於等待狀態。

產生訊號

1 kill產生一個訊號

kill(getpid(), sig);

2 raise自舉一個訊號

int raise(int sig);

3  alarm定時

 #include <unistd.h>
 unsigned int alarm(unsigned int seconds);

4 ualarm定時

useconds_t ualarm(useconds_t usecs, useconds_t interval);

訊號處理與signal安裝訊號

1.訊號處理辦法

(1)忽略此訊號

大多數訊號都可以這樣處理。除了sigkill和sigstop。

(2)捕捉訊號

通知核心在某種訊號發生時呼叫一個使用者函式。在使用者函式中,如果需要按使用者希望執行這種事件進行的處理,即需要安裝此訊號。

(3)執行系統預設操作。

2.signal安裝訊號

#include <signal.h>

typedef void (*sighandler_t)(int);

sighandler_t signal(int signum, sighandler_t handler);

2 sigaction安裝訊號


   #include <signal.h>


    int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);

使用例子

#include<stdio.h>
#include<stdlib.h>
#include<signal.h>

void myhandler(int sig);

int main()
{
        struct sigaction act,oact;
        act.sa_handler = myhandler;
        sigemptyset(&act.sa_mask);
        act.sa_flags = 0;
        sigaction(SIGUSR1,&act,&oact);

        while(1)
        {
                printf("hello world\n");
                pause();
        }
}

void myhandler(int sig)
{
        printf("i get signal %d\n",sig);
}

訊號集與遮蔽訊號

中斷可以被遮蔽(阻塞)(部分硬體中斷則必須立即處理,例如復位中斷),因此,linux的訊號是可以遮蔽,即阻塞訊號。

(1)清空訊號集

 int sigemptyset(sigset_t *set);

(2)完全填空訊號集

int sigfillset(sigset_t *set);

(3)新增訊號到訊號集中

int sigaddset(sigset_t *set, int signum);

(4)從訊號集中刪除某個訊號

int sigdelset(sigset_t *set, int signum);

(5)檢測訊號是否在訊號集中

int sigismember(const sigset_t *set, int signum);

//將阻塞的訊號新增到set中

 int sigpending(sigset_t *set);

//設定或獲取當前程序阻塞的訊號集

int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);

第1個引數

#define SIG_BLOCK 0

#define SIG_UNBLOCK 1

#define SIG_SETMASK 2

SIG_BLOCK 將第2個引數所描述的集合新增到當前程序阻塞的訊號集中。

SIG_UNBLOCK 將第2個引數所描述的集合從當前程序阻塞的訊號集中刪除。

SIG_SETMASK 無論之前的阻塞訊號,設定當前程序阻塞的集合為第2個引數描述的物件。

如果set是空指標,則引數how的值沒有意義,且不會更改程序的阻塞訊號集,因此該呼叫可用於查詢當前受阻塞的訊號 。

函式使用示例

#include<stdio.h>
#include<signal.h>
#include<stdlib.h>

static void sig_quit(int);

int main(int argc,char* argv[])
{
        sigset_t newmask,oldmask,pendmask;

        if(signal(SIGQUIT,sig_quit) == SIG_ERR)
        {
                perror("signal");
                exit(-1);
        }

        printf("install sig_quit\n");

        sleep(10);
        sigemptyset(&newmask);
        sigaddset(&newmask,SIGQUIT);

        if(sigprocmask(SIG_BLOCK,&newmask,&oldmask) < 0)
        {
                perror("signalmask ");
                exit(-1);
        }

        printf("block sigquit,wait 15 second...\n");

        sleep(15);

        if(sigpending(&pendmask) < 0)
        {
                perror("sigpending ");
                exit(-1);
        }

        if(sigprocmask(SIG_SETMASK,&oldmask,NULL) < 0)
        {
                perror("sigprocmask ");
                exit(-1);
        }
        printf("sigquit unblocked\n");
        sleep(10);

        return 0;
}

static void sig_quit(int signo)
{
        printf("caught sigquit,the process will quit\n");
        if(signal(SIGQUIT,SIG_DFL) == SIG_ERR) //重新設定
        {
                perror("signal ");
                exit(-1);
        }
}

//等待訊號 使當前程序處於等待狀態,直到當前程序阻塞訊號外任意一個訊號出現。

int pause(void);

//將呼叫程序阻塞的訊號集替換為其引數值,然後掛起該執行緒,直到傳遞一個非指定集合中訊號為止。

int sigsuspend(const sigset_t *mask);

針對sa_mask成員測試應用程式碼

#include<stdio.h>
#include<signal.h>
#include<stdlib.h>

int output(sigset_t set)
{
    printf("set.val[0]=%x\n",set.__val[0]);
}

void handler(int sig)
{
    int i;
    sigset_t sysset;
    printf("\nin handler sig=%d\n",sig);
    sigprocmask(SIG_SETMASK,NULL,&sysset);
    output(sysset);
    printf("return\n");
}

int main()
{
    struct sigaction act;
    sigset_t set,sysset,newset;
    sigemptyset(&set);
    sigemptyset(&newset);

    //分別加入不同值
    sigaddset(&set,SIGUSR1);
    sigaddset(&newset,SIGUSR2);
    
    printf("\nadd SIGUSR1,the value of set:");
    output(set);
    
    printf("\nadd SIGUSR2,the value of set:");
    output(newset);

    printf("\nafter set proc block set,and then read to sysset\n");
    //設定阻塞值
    sigprocmask(SIG_SETMASK,&set,NULL);
    //讀配置
    sigprocmask(SIG_SETMASK,NULL,&sysset);
    output(sysset);
    
    printf("install SIGALRM,and the act.sa_mask is newset(SIGUSR2)\n");
    act.sa_handler = handler;
    act.sa_flags = 0;
    act.sa_mask = newset;
    //安裝訊號
    sigaction(SIGALRM,&act,NULL);
    //等待訊號
    pause();
    
    printf("after exec ISR\n");
    sigemptyset(&sysset);
    //讀配置
    sigprocmask(SIG_SETMASK,NULL,&sysset);
    output(sysset);
}

訊號應用示例

建立兩個程序,父程序執行復制,子程序傳送訊號給父程序,查詢複製進度。

#include<stdio.h>
#include<fcntl.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#include<signal.h>


int count;
int file_size;
//處理alarm訊號
void sig_alarm(int arg);
//處理普通訊號sigusr1
void sig_usr(int arg);

int main(int argc,char* argv[])
{
    pid_t pid;
    int i;
    int fd_src,fd_des;
    char buf[128];
    
    if(argc != 3)
    {
        printf("check the format:comm src_file des_file\n");
        return -1;
    }
    //只讀方式開啟原始檔
    if((fd_src = open(argv[1],O_RDONLY)) == -1)
    {
        perror("open file src");
        exit(-1);
    }
    //獲取檔案長度
    file_size = lseek(fd_src,0,SEEK_END);
    //重新獲取寫位置
    lseek(fd_src,0,SEEK_SET);
    //以讀方式開啟,若沒有則建立
    if((fd_des = open(argv[2],O_RDWR|O_CREAT,0644)) == -1)
    {
        perror("open fd_des");
        exit(-1);
    }
    //建立子程序
    if((pid=fork()) == -1)
    {
        perror("fork");
        exit(-1);
    }
    //父程序
    else if(pid > 0)
    {
        //安裝訊號
        signal(SIGUSR1,sig_usr);
        do
        {
            memset(buf,'\0',128);
            if((i=read(fd_src,buf,1)) == -1)
            {
                perror("read");
                exit(-1);
            }
            else if(i == 0)
            {
                kill(pid,SIGINT);
                break;
            }
            else
            {
                if(write(fd_des,buf,i) == -1)
                {
                    perror("write");
                    exit(-1);                    
                }
                count += i;
            }
        }
        while(i != 0);
        //等待子程序退出,防止僵死程序
        wait(pid,NULL,0);
        exit(0);
    }
    //子程序
    else if(pid == 0)
    {
        usleep(2);
        //安裝訊號
        signal(SIGALRM,sig_alarm);
        ualarm(1,1);
        //一直執行
        while(1){;}
        exit(0);
    }    
}

void sig_alarm(int arg)
{
    //向父程序傳送sigalarm
    kill(getppid(),SIGUSR1);
}

void sig_usr(int arg)
{
    float i;
    i = (float)count / (float) file_size;
    printf("cureent over:%0.0f%%\n",i*100);
}