Linux C 信号处理机制

一 . 信号

1. 信号:是内核发送给某一进程的一种消息 。

2. 信号机制:是Linux系统中用于进程之间相互通信或操作的一种机制。

3. 信号的来源:信号来源于内核

4. 产生原因: (1)用户通过终端输入 (2)进程执行(3)一个进程调用kill向另一个进程发送信号

5. 信号分类:1)同步信号    异步信号

                        2)可靠信号 : 信号被递送多次  不可靠信号: 只被递送一次的信号

6. 信号处理方式

      A.  按系统默认方式处理 ——每个信号都有一个默认动作,典型的默认动作是终止进程。 man signal 7 列出系统对各信号的默认处理 。

      B.  忽略信号——接收信号,但是不做任何操作 。

      C.  捕捉信号——程序提前告诉内核,当信号到来时应该调用哪个函数。

二. 信号相关函数

   1.  简单的信号处理

    函数原型: sighandler_t signal(int signum, sighandler_t handler);

    参    数:signum ——要响应的信号,handler  ——信号发生时要执行的操作。

    返回值:非-1    执行成功,返回以前的信号处理函数指针

                      -1        遇到错误

//捕捉信号
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
void capture(int signum){
    printf("SIGINT is capture!\n");
}
int main(int argc, char *argv[]){
    int i=10;
    if(signal(SIGINT,capture) == SIG_ERR){
        printf("Can't catch SIGINT");
        exit(1);
    }
    printf("waiting for signal...\n");
    while(i > 0){
        printf("Now i=%d \n",i);
        sleep(1);
        i--;
    }
    return 0;
}

//忽略信号
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
int main(int argc,char *argv[]){
    int i=10;
    signal(SIGINT,SIG_IGN);
    printf("waiting for signal ...\n");
    while(i > 0){
        sleep(1);
        i--;
        printf("Now i = %d,but you can't stop this program by Ctrl + C\n",i);
    }
    return 0;
}
//恢复信号的默认处理
#include <stdio.h>
#include <signal.h>
void capthensfl(int signum){
    printf("SIGINT is capture !\n");
    signal(SIGINT,SIG_DFL);
    printf("SIGINT now is defaulted !\n");
}
int main(int argc,char *argv[]){
    int i=10;
    signal(SIGINT,capthensfl);
    printf("waiting for signal...\n");
    while(i > 0){
        sleep(1);
        printf("Now i = %d\n",i);
        i--;
    }
    return 0;
}

2. 信号处理函数:指定一个信号的处理函数

    函数原型:int sigaction(int signum, struct sigaction *action, struct sigaction *oldaction);

    参    数:signum  ,要处理的信号

                    action        要安装的信号处理操作的结构

                    oldaction   之前的信号处理操作的结构

    返回值:0    成功     -1,失败

    说    明:oldaction不为NULL,则用其来存储以前设置的与此信号关联的操作。

                    action不为NULL: 则指定信号关联的操作为此参数指向的结构 否则,信号处理保持不变。(此方式用来查询当前对指定信号的处理方式)

sigaction的结构:

        struct sigaction{     

                    union{         __sighandler_t _sa_handler;         

                                         void(*_sa_sigaction)(int , struct siginfo *, void *);   

                     } __u;     

                    sigset_t sa_mask;     

                    unsigned long sa_flags;

};

sa_sigaction的说明: void (*sa_sigaction)(int, struct siginfo *,void *);

                                        第一个参数:要响应的信号 。

                                        第二个参数:记录导致产生信号的原因、类型等 。

                                        第三个参数:信号发生时被中断的上下文环境 信号能传递简单的信息。

//使用sa_sigaction类型函数
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
void fun(int signo, siginfo_t *info, void *context){
    printf("Test for sa_sigaction !\n");
}
int main(int argc,char *argv[]){
    struct sigaction action,oldaction;
    sigemptyset(&action.sa_mask);
    action.sa_flags=SA_SIGINFO;
    action.sa_sigaction=fun;
    sigaction(SIGINT,&action,&oldaction);
    printf("waiting for signal...\n");
    while(1){    
        pause();
    }
    return 0;    
}

3. 初始化信号集

    函数原型:int sigemptyset(sigset_t  *set);

                        int sigfillset(sigset_t  *set);

    参    数:set  待初始化的信号集

    返回值:0   成功     -1,失败

4. 添加、删除信号

    函数原型:int sigaddset(sigset_t *set, int signo);

                       int sigdelset(sigset_t *set, int signo);

    参    数:set  待初始化的信号集 .  signo  待添加/删除的信号编号

    返回值:0  成功     -1,失败

5. 修改屏蔽信号集

    函数原型:int sigprocmask(int how,sigset_t *set, sigset_t * oldset);

    参    数:how   如何修改信号掩码

                    set 要使用的信号集

                    oldset   之前的信号集

    返回值:0  成功     -1,失败

    对参数how的说明:    SIG_BLOCK:信号集中增加set中的信号

                                        SIG_UNBLOCK:信号集中删除set中的信号

                                        SIG_SETMASK:信号集被设置为set信号集

三.  系统调用函数

1. kill  —— 向进程发送一个信号 

    函数原型:int kill(pid_t pid, int sig);

    参    数:pid 目标进程id, sig  要发送的信号

    返回值:0  成功     -1,失败

    说    明:     pid>0      目标进程的进程号为pid

                         pid=0      将信号发送给和当前进程在同一进程租的所有进程

                         pid=-1     将信号发送给系统内的所有进程

                         pid<0      将信号发送给进程组号PGID为pid绝对值的所有进程

    一个进程可使用kill函数将信号发送给另一进程或进程组。

    要求:发送信号的进程的用户ID和目标进程的用户ID相同,或发送信号的进程的owner是一个超级用户。

//kill 使用示例
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
void fun(int signo){
    printf("Process capture SIGINT");
    signal(SIGINT,SIG_DFL);
}
int main(int argc,char *argv[]){
    int pid;
    if((pid=fork()) == -1){
        perror("fork");
        exit(EXIT_FAILURE);
    }
    else if( pid == 0){
        signal(SIGINT,fun);
        printf("Child %d waiting for parent %d send signal\n",getpid(),getppid());
        pause();
        pause();
    }
    else{
        sleep(1);
        printf("Parent %d will send signal to child %d \n",getppid(),getpid());
        kill(pid,SIGINT);
        wait(NULL);
    }
    return 0;
}

2. raise —— 自举一个信号

    函数原型:int raise(int signo);

    参    数:signo      待发送的信号

    返回值:0  成功     -1,失败

    说   明:raise(signo)=kill(getpid(),signo)

3. alarm —— 设置计时器

    函数原型:int alarm(int seconds);

    参    数:seconds  计时的秒数

    返回值:      0    之前未调用过alarm

                        >0   之前调用alarm设置的闹钟时间的余留秒数

4. pause —— 挂起调用进程

    函数原型:int pause(void);

    返回值:-1       并将errno设置为EINTR

四. 信号集合操作应用示例

/*  信号的应用示例:
     父进程执行文件复制操作,如果收到SIGUSR1信号,就打印出当前的复制进度
     子进程每隔一个固定时间向父进程发送SIGUSR1信号
*/
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>

int count;  //当前复制大小
int file_size; //文件的大小

//父进程对SIGUSR1信号的处理函数
void sig_usr(int signum){
    float i;
    //求出复制进程
    i = (float)count/(float)file_size;
    printf("current over : %0.0f%%\n",i*100);
}

//子进程对SIGUSR1信号的处理,即向父进程发送SIGUSR1信号
void sig_alarm(int signo){
    kill(getppid(),SIGUSR1);
}

//主函数
int main(int argc ,char *argv[]){
    pid_t pid;
    int i;
    int fd_src,fd_des;
    char buf[128];
    if(argc != 3){
        printf("the format must be : command src_file des_file \n");
        return -1;
    }
    //以只读方式打开源文件
    if((fd_src = open(argv[1],O_RDONLY)) == -1){
        perror("open fd_src");
        exit(EXIT_FAILURE);
    }
    //获取源文件大小
    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(EXIT_FAILURE);
    }
    //进程创建失败
    if((pid = fork() == -1)){
        perror("fork");
        exit(EXIT_FAILURE);
    }
    //父进程
    else if(pid > 0){
        //安装信号
        signal(SIGUSR1,sig_usr);
        do{
            memset(buf,'\0',128);
            //复制数据
            if((i = read(fd_src,buf,1)) == -1){
                perror("read");
                exit(EXIT_FAILURE);
            }
            //如果复制完成,向子进程发送SIGINT信号,终止
            else if(i == 0){
                kill(pid,SIGINT);
                break;
            }
            else{
                //执行复制操作
                if(write(fd_des,buf,i) == -1){
                    perror("write");
                    exit(EXIT_FAILURE);
                }
                count += i; //更新已经复制的文件的大小
            }
        }while(i != 0);
        //等待子进程退出
        wait(pid,NULL,0);
        exit(EXIT_SUCCESS);
    }
    //子进程
    else if(pid == 0){
        usleep(1);
        signal(SIGALRM,sig_alarm);
        ualarm(1,1);
        while(i){
            ;
        }
        exit(EXIT_SUCCESS);
    }
    return 0;
}

转载于:https://my.oschina.net/xieganying/blog/787610

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值