Linux C 信号处理机制

    xiaoxiao2021-03-25  82

    一 . 信号

    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://ju.6miu.com/read-35749.html

    最新回复(0)