信号处理
一.实验目的
本实验要求利用可靠信号机制解决信号处理时可能出现的时间窗口,以及非局部转移等问题,将学习使用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 */
void
sig_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);
}
}
/* 闹钟信号处理函数 */
void
sig_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);
}
四.运行结果