信号处理(unix操作系统系)---5

来源:互联网 发布:js调用android摄像头 编辑:程序博客网 时间:2024/05/16 01:58


 

信号处理

一.实验目的

本实验要求利用可靠信号机制解决信号处理时可能出现的时间窗口,以及非局部转移等问题,将学习使用sigaction,alarm,sigpending,sigsetjmp和siglongjmp等函数解决在处理信号时遇到的问题。

二.实验设计

我们可以直接利用系统shell(在cs8是bash):execl(“/bin/sh”, “sh”, “-c”, buf, (char *) 0);

这样程序sigtest就具有系统shell的全部功能。

需要处理的信号:因为需要使用闹钟,所以实验需要处理两个信号:SIGALRM和

针对:SIGQUIT(要处理两种情况、用户输入、命令执行中)信号的可能情况。

第一种:如果当前程序正在执行用户命令,则信号处理函数必须“杀死”用户命令进程:kill(pid, SIGKILL);     // pid为用户命令进程的ID。

第二种:对于信号SIGQUIT还有一种可能:正在接收用户输入的命令串。此时需要放弃当前输入,重新开始接收输入。解决方法可能需要使用非局部转移机制。

三.源代码

 

#include "apue.h"#include <sys/wait.h>#include <setjmp.h>#include <unistd.h>#include <signal.h>static void    sig_int(int);     /* our signal-catching function */static void sig_alrm(int);/* 闹钟 */static volatile pid_t pid;/* 进程id */static sigjmp_buf jmpbuf;/* 非局部转移 */Sigfunc *signal(int signo , Sigfunc *func){    struct sigaction act, oact;/* act:原来的动作 新的动作:oact */    act.sa_handler = func;/* 处理函数设置为传进来的出来函数 */    sigemptyset (& act.sa_mask);/* 信号就要考虑屏蔽字 这是清空屏屏蔽字 */    act.sa_flags = 0;    if (signo == SIGALRM)        sigaddset (& act.sa_mask, SIGQUIT);/* 如果是因为alarm信号引去的系统调用中断,会重启该系统调用。    act.sa_flags |= SA_RESTART;   //在输入期间,发生中断,原来的系统调用将重启。        /* 设置信号值signo对应的处理函数为act.sahandle 旧的信号将保存在oact中。 */    if (sigaction(signo , &act,&oact)<0)return (SIG_ERR);    /* 调用传进来的函数 ,将执行结果返回给调用方 */    return (oact.sa_handler);}
int main(int argc,char  *argv[]){       char    buf[MAXLINE];    /* from apue.h */    int    status, time, flag = 0;    if  (argc >= 3)    {        flag=1;        time=atoi(argv[2]);    }    if (signal(SIGQUIT, sig_int) == SIG_ERR)        err_sys("signal error");    if (signal(SIGALRM, sig_alrm) == SIG_ERR)        err_sys("signal error");     /* 设置转移终点 */    sigsetjmp(jmpbuf,1);    /* 跳转打印标志 */     printf("%% ");     /* print prompt (printf requires %% to print %)  */    /* alarm 标志 time为睡眠某个时间后的唤醒 */     alarm(time);    if (flag)    /* 死循环 */    while (fgets(buf, MAXLINE, stdin) != NULL)     {    if (buf[strlen(buf) - 1] == '\n')/* replace newline with null 也就是'\0' */    buf[strlen(buf) - 1] = 0;/* 创建线程出错 */ /* 创建子进程 */if ((pid = fork()) < 0){    err_sys("fork error");} else if (pid == 0) {          /* child  子进程执行代码段 */    execl("/bin/sh","sh","-c",buf,(char *)0);    err_ret("couldn't execute: %s", buf);    exit(127);}/* parent 进程运行的代码段 *//* 等待子进程退出,并看子进程退出状态 */waitpid(pid, &status, 0);pid =0;alarm(0);printf("%% ");    }    exit(0);}/* SIGQUIT 信号处理函数 ctrl+\ 不是ctrl+c ctrl+z */voidsig_int(int signo){    sigset_t pendmask;    /* 子进程还没执行完, 杀死子进程 */    if (pid > 0)    {        /* waitpid (pid,NULL,0); */        kill(pid,SIGKILL);    }    /* 子进程执行完,正在执行父进程 跳到转移终点所在的位置。可以传参数代表哪个终点 */    else    {        printf ("\n");        printf ("interrupt\n");        siglongjmp (jmpbuf,1);        }     /* 函数返回在送往进程的时候被阻塞挂起的信号集合,如果返回出错则提示错误 */    if (sigpending(&pendmask) < 0)        {        err_sys("sigpending error");    }    /* sigismember()用来测试参数signum 代表的信号是否已加入至参数set信号集里。      *  如果信号集里已有该信号则返回1,否则返回0。如果有错误则返回-1。      */    if (sigismember(&pendmask,SIGALRM))    {signal(SIGALRM,SIG_IGN);signal(SIGALRM,sig_alrm);        }} /* 闹钟信号处理函数 */voidsig_alrm(int signo){    printf("TIME OUT!\n");    sigset_t pendmask;    if (pid >0)    {        kill(pid,SIGKILL);    }    if (sigpending(&pendmask)<0)        err_sys("sigpending error");    if (sigismember(&pendmask,SIGQUIT))    {        signal(SIGQUIT,SIG_IGN);        signal(SIGQUIT,sig_int);    }    siglongjmp (jmpbuf,1);    }

四.运行结果