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


 

信号处理

一.实验目的

本实验要求利用可靠信号机制解决信号处理时可能出现的时间窗口,以及非局部转移等问题,将学习使用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);    
}

四.运行结果

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值