sigsuspend/sigaction

1.一段代码存在的问题

因为操作是不原子的

void Handler(int n)
{
        write(1, "!", 1);
}


int main()
{
        int i;
        sigset_t set;

        sigemptyset(&set);
        sigaddset(&set, SIGINT);


        signal(SIGINT, Handler);

        while(1)
        {
           ///阻塞信号
           sigprocmask(SIG_BLOCK, &set, NULL);
           for(i = 0; i < 5; i++)
           {
                write(1, "*", 1);
                sleep(1);
           }
           write(1, "\n", 1);
           //恢复信号
           sigprocmask(SIG_UNBLOCK, &set, NULL);
           //等待信号
           pause();
        }

        return 0;
}

我们要实现的功能是当一行的*打印完之后,我们按下ctrl+c,才能继续打印
在这里插入图片描述

上面图片中出现的问题,在信号阻塞的时候,如果产生了信号,该信号被推迟直到解除了阻塞,所以该信号就好像发生在恢复信号和等待信号之间。如果在解除阻塞时候和pause之间确实发生了信号,那么可能该信号永远不可见,使得pause永远阻塞。

2.sigsuspend

为了纠正此问题,需要在一个原子操作中实现 先恢复信号屏蔽字,然后进程休眠,如果捕捉到一个信号,而且从该信号处理程序返回,则该函数返回

void Handler(int n)
{
        write(1, "!", 1);
}


int main()
{
        int i;
        sigset_t set, unblock_set;

        sigemptyset(&set);
        sigaddset(&set, SIGINT);


        signal(SIGINT, Handler);
        //先让不阻塞信号
        sigprocmask(SIG_UNBLOCK, &set, NULL);
        //把当前的状态保存到unblock_set中
        sigprocmask(SIG_BLOCK, &set, &unblock_set);

        while(1)
        {
           
           sigprocmask(SIG_BLOCK, &set, NULL);
           for(i = 0; i < 5; i++)
           {
                write(1, "*", 1);
                sleep(1);
           }
           write(1, "\n", 1);
           //sigprocmask(SIG_UNBLOCK, &set, NULL);
           //pause();
           //原子化操作 
           sigsuspend(&unblock_set);
        }

        return 0;
}

在这里插入图片描述

3.sigaction

signal在多个信号,共用一个信号处理函数的时候会产生问题
signal也不能区分信号的来源

NAME
       sigaction - examine and change a signal action

SYNOPSIS
       #include <signal.h>

       int sigaction(int signum, const struct sigaction *act,
                     struct sigaction *oldact);
 The sigaction structure is defined as something like:

struct sigaction {
    void     (*sa_handler)(int);//可以替换signal那个函数
    void     (*sa_sigaction)(int, siginfo_t *, void *);
    sigset_t   sa_mask;//信号集类型 可以把某些信号block住
    int        sa_flags;
    void     (*sa_restorer)(void);
};
sa_mask specifies a mask of signals which should be blocked

这里可以获得信号的属性

siginfo_t {
 int      si_signo;    /* Signal number */
 int      si_errno;    /* An errno value */
 int      si_code;     /* Signal code */
 int      si_trapno;   /* Trap number that caused
                          hardware-generated signal
                          (unused on most architectures) */
 pid_t    si_pid;      /* Sending process ID */
 uid_t    si_uid;      /* Real user ID of sending process */
 int      si_status;   /* Exit value or signal */
 clock_t  si_utime;    /* User time consumed */
 clock_t  si_stime;    /* System time consumed */
 sigval_t si_value;    /* Signal value */
 int      si_int;      /* POSIX.1b signal */
 void    *si_ptr;      /* POSIX.1b signal */
 int      si_overrun;  /* Timer overrun count; POSIX.1b timers */
 int      si_timerid;  /* Timer ID; POSIX.1b timers */
 void    *si_addr;     /* Memory location which caused fault */
 long     si_band;     /* Band event (was int in
                          glibc 2.3.2 and earlier) */
 int      si_fd;       /* File descriptor */
 short    si_addr_lsb; /* Least significant bit of address
                          (since Linux 2.6.32) */
}

4.一个简单的demo

在这里插入图片描述
在这里插入图片描述
以前写的一个令牌桶,来实行流量控制,现在用户态的一个ALRM信号,会干扰流量控制
解决


void AlarmAction(int nNum, siginfo_t *info, void *unuse)
{
        int i;

        //alarm(1);

        //printf("**********************\n");
		
		//只有内核态 才可以
        if(SI_KERNEL == info->si_code)
        for(i = 0; i < MYTBF_MAX; i++)
        {
                if(NULL != job[i])
                {
                   job[i]->token += job[i]->cps ;

                   if(job[i]->burst < job[i]->token)
                      job[i]->token = job[i]->burst;
           
                }
        }


}



void ModuleUnload(void)
{
        int i;
        struct itimerval itv;
        //signal(SIGALRM, SignalRes);
        //alarm(0);
		
		//对信号处理进行恢复
        sigaction(SIGALRM, &oldsa, NULL);

		//关闭alarm
        itv.it_interval.tv_sec = 0;
        itv.it_interval.tv_usec = 0;
        itv.it_value.tv_sec = 0;
        itv.it_value.tv_usec = 0;

        setitimer(ITIMER_REAL, &itv, NULL);


        for(i = 0; i < MYTBF_MAX; i++)
        {
                free(job[i]);
        }

}

void ModuleLoad(void)
{
        //SignalRes = signal(SIGALRM, AlarmHandler);
        //alarm(1);
        //
        struct sigaction sa;
        struct itimerval itv;
		//设置信号处理函数
        sa.sa_sigaction = AlarmAction;
        //设置flag 使用那个三参的函数指针
        sa.sa_flags = SA_SIGINFO;
        //不需要屏蔽任何mask
        sigemptyset(&sa.sa_mask);
		//指定信号
        sigaction(SIGALRM, &sa, &oldsa);

		//这个就是设置时间 自动形成一个alarm链条
        itv.it_interval.tv_sec = 1;
        itv.it_interval.tv_usec = 0;
        itv.it_value.tv_sec = 1;
        itv.it_value.tv_usec = 0;

        setitimer(ITIMER_REAL, &itv, NULL);
        Inited = 1;

		//钩子函数
        atexit(ModuleUnload);
}

代码的其余部分不变

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C语言入门 C语言本质 Linux系统编程 28. 文件与I/O 1. 汇编程序的Hello world 2. C标准I/O库函数与Unbuffered I/O函数 3. open/close 4. read/write 5. lseek 6. fcntl 7. ioctl 8. mmap 29. 文件系统 1. 引言 2. ext2文件系统 2.1. 总体存储布局 2.2. 实例剖析 2.3. 数据块寻址 2.4. 文件和目录操作的系统函数 3. VFS 3.1. 内核数据结构 3.2. dup和dup2函数 30. 进程 1. 引言 2. 环境变量 3. 进程控制 3.1. fork函数 3.2. exec函数 3.3. wait和waitpid函数 4. 进程间通信 4.1. 管道 4.2. 其它IPC机制 5. 练习:实现简单的Shell 31. Shell脚本 1. Shell的历史 2. Shell如何执行命令 2.1. 执行交互式命令 2.2. 执行脚本 3. Shell的基本语法 3.1. 变量 3.2. 文件名代换(Globbing):* ? [] 3.3. 命令代换:`或 $() 3.4. 算术代换:$(()) 3.5. 转义字符\ 3.6. 单引号 3.7. 双引号 4. bash启动脚本 4.1. 作为交互登录Shell启动,或者使用--login参数启动 4.2. 以交互非登录Shell启动 4.3. 非交互启动 4.4. 以sh命令启动 5. Shell脚本语法 5.1. 条件测试:test [ 5.2. if/then/elif/else/fi 5.3. case/esac 5.4. for/do/done 5.5. while/do/done 5.6. 位置参数和特殊变量 5.7. 函数 6. Shell脚本的调试方法 32. 正则表达式 1. 引言 2. 基本语法 3. sed 4. awk 5. 练习:在C语言中使用正则表达式 33. 信号 1. 信号的基本概念 2. 产生信号 2.1. 通过终端按键产生信号 2.2. 调用系统函数向进程发信号 2.3. 由软件条件产生信号 3. 阻塞信号 3.1. 信号在内核中的表示 3.2. 信号集操作函数 3.3. sigprocmask 3.4. sigpending 4. 捕捉信号 4.1. 内核如何实现信号的捕捉 4.2. sigaction 4.3. pause 4.4. 可重入函数 4.5. sig_atomic_t类型与volatile限定符 4.6. 竞态条件与sigsuspend函数 4.7. 关于SIGCHLD信号 34. 终端、作业控制与守护进程 1. 终端 1.1. 终端的基本概念 1.2. 终端登录过程 1.3. 网络登录过程 2. 作业控制 2.1. Session与进程组 2.2. 与作业控制有关的信号 3. 守护进程 35. 线程 1. 线程的概念 2. 线程控制 2.1. 创建线程 2.2. 终止线程 3. 线程间同步 3.1. mutex 3.2. Condition Variable 3.3. Semaphore 3.4. 其它线程间同步机制 4. 编程练习 36. TCP/IP协议基础 1. TCP/IP协议栈与数据包封装 2. 以太网(RFC 894)帧格式 3. ARP数据报格式 4. IP数据报格式 5. IP地址与路由 6. UDP段格式 7. TCP协议 7.1. 段格式 7.2. 通讯时序 7.3. 流量控制 37. socket编程 1. 预备知识 1.1. 网络字节序 1.2. socket地址的数据类型及相关函数 2. 基于TCP协议的网络程序 2.1. 最简单的TCP网络程序 2.2. 错误处理与读写控制 2.3. 把client改为交互式输入 2.4. 使用fork并发处理多个client的请求 2.5. setsockopt 2.6. 使用select 3. 基于UDP协议的网络程序 4. UNIX Domain Socket IPC 5. 练习:实现简单的Web服务器 5.1. 基本HTTP协议 5.2. 执行CGI程序

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值