5)Linux程序设计入门--信号处理
- )Linux程序设计入门--信号处理
- Linux下的信号事件
- 前言:这一章我们讨论一下Linux下的信号处理函数.
- Linux下的信号处理函数:
- 信号的产生
- 信号的处理
- 其它信号函数
- 一个实例
- 。信号的产生
- Linux下的信号可以类比于DOS下的INT或者是Windows下的事件.在有一个信号发生时
- 候相信的信号就会发送给相应的进程.在Linux下的信号有以下几个. 我们使用 kill -l
- 命令可以得到以下的输出结果:
- ) SIGHUP ) SIGINT ) SIGQUIT ) SIGILL
- ) SIGTRAP ) SIGABRT ) SIGBUS ) SIGFPE
- ) SIGKILL ) SIGUSR1 ) SIGSEGV ) SIGUSR2
- ) SIGPIPE ) SIGALRM ) SIGTERM ) SIGCHLD
- ) SIGCONT ) SIGSTOP ) SIGTSTP ) SIGTTIN
- ) SIGTTOU ) SIGURG ) SIGXCPU ) SIGXFSZ
- ) SIGVTALRM ) SIGPROF ) SIGWINCH ) SIGIO
- ) SIGPWR
- 关于这些信号的详细解释请查看man signal的输出结果. 信号事件的发生有两个来源
- :一个是硬件的原因(比如我们按下了键盘),一个是软件的原因(比如我们使用系统函数或
- 者是命令发出信号). 最常用的四个发出信号的系统函数是kill, raise, alarm和setit
- imer函数. setitimer函数我们在计时器的使用 那一章再学习.
- #include <sys/types.h>;
- #include <signal.h>;
- #include <unistd.h>;
- int kill(pid_t pid,int sig);
- int raise(int sig);
- unisigned int alarm(unsigned int seconds);
- kill系统调用负责向进程发送信号sig.
- 如果pid是正数,那么向信号sig被发送到进程pid.
- 如果pid等于0,那么信号sig被发送到所以和pid进程在同一个进程组的进程
- 如果pid等于-,那么信号发给所有的进程表中的进程,除了最大的哪个进程号.
- 如果pid由于-,和0一样,只是发送进程组是-pid.
- 我们用最多的是第一个情况.还记得我们在守护进程那一节的例子吗?我们那个时候用这
- 个函数杀死了父进程守护进程的创建
- raise系统调用向自己发送一个sig信号.我们可以用上面那个函数来实现这个功能的.
- alarm函数和时间有点关系了,这个函数可以在seconds秒后向自己发送一个SIGALRM信号
- .. 下面这个函数会有什么结果呢?
- #include <unistd.h>;
- main()
- {
- unsigned int i;
- alarm();
- ;;i++)
- printf("I=%d",i);
- }
- SIGALRM的缺省操作是结束进程,所以程序在1秒之后结束,你可以看看你的最后I值为多少
- ,来比较一下大家的系统性能差异(我的是2232).
- 。信号操作 有时候我们希望进程正确的执行,而不想进程受到信号的影响,比如我
- 们希望上面那个程序在1秒钟之后不结束.这个时候我们就要进行信号的操作了.
- 信号操作最常用的方法是信号屏蔽.信号屏蔽要用到下面的几个函数.
- #include <signal.h>;
- int sigemptyset(sigset_t *set);
- int sigfillset(sigset_t *set);
- int sigaddset(sigset_t *set,int signo);
- int sigdelset(sigset_t *set,int signo);
- int sigismember(sigset_t *set,int signo);
- int sigprocmask(int how,const sigset_t *set,sigset_t *oset);
- sigemptyset函数初始化信号集合set,将set设置为空.sigfillset也初始化信号集合,只
- 是将信号集合设置为所有信号的集合.sigaddset将信号signo加入到信号集合之中,sigd
- elset将信号从信号集合中删除.sigismember查询信号是否在信号集合之中.
- sigprocmask是最为关键的一个函数.在使用之前要先设置好信号集合set.这个函数的作
- 用是将指定的信号集合set加入到进程的信号阻塞集合之中去,如果提供了oset那么当前
- 的进程信号阻塞集合将会保存在oset里面.参数how决定函数的操作方式.
- SIG_BLOCK:增加一个信号集合到当前进程的阻塞集合之中.
- SIG_UNBLOCK:从当前的阻塞集合之中删除一个信号集合.
- SIG_SETMASK:将当前的信号集合设置为信号阻塞集合.
- 以一个实例来解释使用这几个函数.
- #include <signal.h>;
- #include <stdio.h>;
- #include <math.h>;
- #include <stdlib.h>;
- int main(int argc,char **argv)
- {
- double y;
- sigset_t intmask;
- int i,repeat_factor;
- )
- {
- fprintf(stderr,]);
- exit();
- }
- ]))<)repeat_factor=;
- sigemptyset(&intmask); /* 将信号集合设置为空 */
- sigaddset(&intmask,SIGINT); /* 加入中断 Ctrl+C 信号*/
- )
- {
- /*阻塞信号,我们不希望保存原来的集合所以参数为NULL*/
- sigprocmask(SIG_BLOCK,&intmask,NULL);
- fprintf(stderr,"SIGINT signal blocked\n");
- ;i<repeat_factor;i++)y=sin((double)i);
- fprintf(stderr,"Blocked calculation is finished\n");
- /* 取消阻塞 */
- sigprocmask(SIG_UNBLOCK,&intmask,NULL);
- fprintf(stderr,"SIGINT signal unblocked\n");
- ;i<repeat_factor;i++)y=sin((double)i);
- fprintf(stderr,"Unblocked calculation is finished\n");
- }
- exit();
- }
- 程序在运行的时候我们要使用Ctrl+C来结束.如果我们在第一计算的时候发出SIGINT信号
- ,由于信号已经屏蔽了,所以程序没有反映.只有到信号被取消阻塞的时候程序才会结束.
- 注意我们只要发出一次SIGINT信号就可以了,因为信号屏蔽只是将信号加入到信号阻塞
- 集合之中,并没有丢弃这个信号.一旦信号屏蔽取消了,这个信号就会发生作用.
- 有时候我们希望对信号作出及时的反映的,比如当拥护按下Ctrl+C时,我们不想什么事情
- 也不做,我们想告诉用户你的这个操作不好,请不要重试,而不是什么反映也没有的. 这个
- 时候我们要用到sigaction函数.
- #include <signal.h>;
- int sigaction(int signo,const struct sigaction *act,
- struct sigaction *oact);
- struct sigaction {
- void (*sa_handler)(int signo);
- void (*sa_sigaction)(int siginfo_t *info,void *act);
- sigset_t sa_mask;
- int sa_flags;
- void (*sa_restore)(void);
- }
- 这个函数和结构看起来是不是有点恐怖呢.不要被这个吓着了,其实这个函数的使用相当
- 简单的.我们先解释一下各个参数的含义. signo很简单就是我们要处理的信号了,可以是
- 任何的合法的信号.有两个信号不能够使用(SIGKILL和SIGSTOP). act包含我们要对这个
- 信号进行如何处理的信息.oact更简单了就是以前对这个函数的处理信息了,主要用来保
- 存信息的,一般用NULL就OK了.
- 信号结构有点复杂.不要紧我们慢慢的学习.
- sa_handler是一个函数型指针,这个指针指向一个函数,这个函数有一个参数.这个函数就
- 是我们要进行的信号操作的函数. sa_sigaction,sa_restore和sa_handler差不多的,只
- 是参数不同罢了.这两个元素我们很少使用,就不管了.
- sa_flags用来设置信号操作的各个情况.一般设置为0好了.sa_mask我们已经学习过了
- 在使用的时候我们用sa_handler指向我们的一个信号操作函数,就可以了.sa_handler有
- 两个特殊的值:SIG_DEL和SIG_IGN.SIG_DEL是使用缺省的信号操作函数,而SIG_IGN是使用
- 忽略该信号的操作函数.
- 这个函数复杂,我们使用一个实例来说明.下面这个函数可以捕捉用户的CTRL+C信号.并输
- 出一个提示语句.
- #include <signal.h>;
- #include <stdio.h>;
- #include <string.h>;
- #include <errno.h>;
- #include <unistd.h>;
- #define PROMPT "你想终止程序吗?"
- char *prompt=PROMPT;
- void ctrl_c_op(int signo)
- {
- write(STDERR_FILENO,prompt,strlen(prompt));
- }
- int main()
- {
- struct sigaction act;
- act.sa_handler=ctrl_c_op;
- sigemptyset(&act.sa_mask);
- act.sa_flags=;
- )
- {
- fprintf(stderr,"Install Signal Action Error:%s\n\a",strerror(errno));
- exit();
- }
- );
- }
- 在上面程序的信号操作函数之中,我们使用了write函数而没有使用fprintf函数.是因为
- 我们要考虑到下面这种情况.如果我们在信号操作的时候又有一个信号发生,那么程序该
- 如何运行呢? 为了处理在信号处理函数运行的时候信号的发生,我们需要设置sa_mask成
- 员. 我们将我们要屏蔽的信号添加到sa_mask结构当中去,这样这些函数在信号处理的时
- 候就会被屏蔽掉的.
- 。其它信号函数 由于信号的操作和处理比较复杂,我们再介绍几个信号操作函数.
- #include <unistd.h>;
- #include <signal.h>;
- int pause(void);
- int sigsuspend(const sigset_t *sigmask);
- pause函数很简单,就是挂起进程直到一个信号发生了.而sigsuspend也是挂起进程只是在
- 调用的时候用sigmask取代当前的信号阻塞集合.
- #include <sigsetjmp>;
- int sigsetjmp(sigjmp_buf env,int val);
- void siglongjmp(sigjmp_buf env,int val);
- 还记得goto函数或者是setjmp和longjmp函数吗.这两个信号跳转函数也可以实现程序的
- 跳转让我们可以从函数之中跳转到我们需要的地方.
- 由于上面几个函数,我们很少遇到,所以只是说明了一下,详细情况请查看联机帮助.
- 。一个实例 还记得我们在守护进程创建的哪个程序吗?守护进程在这里我们把那个
- 程序加强一下. 下面这个程序会在也可以检查用户的邮件.不过提供了一个开关,如果用
- 户不想程序提示有新的邮件到来,可以向程序发送SIGUSR2信号,如果想程序提供提示可以
- 发送SIGUSR1信号.
- #include <unistd.h>;
- #include <stdio.h>;
- #include <errno.h>;
- #include <fcntl.h>;
- #include <signal.h>;
- #include <string.h>;
- #include <pwd.h>;
- #include <sys/types.h>;
- #include <sys/stat.h>;
- /* Linux 的默任个人的邮箱地址是 /var/spool/mail/ */
- #define MAIL_DIR "/var/spool/mail/"
- /* 睡眠10秒钟 */
- #define SLEEP_TIME 10
- #define MAX_FILENAME 255
- unsigned ;
- long get_file_size(const char *filename)
- {
- struct stat buf;
- )
- {
- ;
- ;
- }
- return (long)buf.st_size;
- }
- void send_mail_notify(void)
- {
- fprintf(stderr,"New mail has arrived\007\n");
- }
- void turn_on_notify(int signo)
- {
- notifyflag=;
- }
- void turn_off_notify(int signo)
- {
- notifyflag=;
- }
- int check_mail(const char *filename)
- {
- long old_mail_size,new_mail_size;
- sigset_t blockset,emptyset;
- sigemptyset(&;blockset);
- sigemptyset(&;emptyset);
- sigaddset(&;blockset,SIGUSR1);
- sigaddset(&;blockset,SIGUSR2);
- old_mail_size=get_file_size(filename);
- );
- ) send_mail_notify();
- sleep(SLEEP_TIME);
- )
- {
- ) ;
- )sigsuspend(&;emptyset);
- ) ;
- new_mail_size=get_file_size(filename);
- if(new_mail_size>;old_mail_size)send_mail_notify;
- old_mail_size=new_mail_size;
- sleep(SLEEP_TIME);
- }
- }
- int main(void)
- {
- char mailfile[MAX_FILENAME];
- struct sigaction newact;
- struct passwd *pw;
- if((pw=getpwuid(getuid()))==NULL)
- {
- fprintf(stderr,"Get Login Name Error:%s\n\a",strerror(errno));
- exit();
- }
- strcpy(mailfile,MAIL_DIR);
- strcat(mailfile,pw->;pw_name);
- newact.sa_handler=turn_on_notify;
- newact.sa_flags=;
- sigemptyset(&;newact.sa_mask);
- sigaddset(&;newact.sa_mask,SIGUSR1);
- sigaddset(&;newact.sa_mask,SIGUSR2);
- )
- fprintf(stderr,"Turn On Error:%s\n\a",strerror(errno));
- newact.sa_handler=turn_off_notify;
- )
- fprintf(stderr,"Turn Off Error:%s\n\a",strerror(errno));
- check_mail(mailfile);
- exit();
- }
- 信号操作是一件非常复杂的事情,比我们想象之中的复杂程度还要复杂,如果你想彻底的
- 弄清楚信号操作的各个问题,那么除了大量的练习以外还要多看联机手册.不过如果我们
- 只是一般的使用的话,有了上面的几个函数也就差不多了. 我们就介绍到这里了.
5)Linux程序设计入门--信号处理的更多相关文章
- 2)Linux程序设计入门--进程介绍
)Linux程序设计入门--进程介绍 Linux下进程的创建 前言: 这篇文章是用来介绍在Linux下和进程相关的各个概念.我们将会学到: 进程的概念 进程的身份 进程的创建 守护进程的创建 .进程的 ...
- 6)Linux程序设计入门--消息管理
)Linux程序设计入门--消息管理 前言:Linux下的进程通信(IPC) Linux下的进程通信(IPC) POSIX无名信号量 System V信号量 System V消息队列 System V ...
- 3)Linux程序设计入门--文件操作
)Linux程序设计入门--文件操作 Linux下文件的操作 前言: 我们在这一节将要讨论linux下文件操作的各个函数. 文件的创建和读写 文件的各个属性 目录文件的操作 管道文件 .文件的创建和读 ...
- 1)Linux程序设计入门--基础知识
)Linux程序设计入门--基础知识 Linux下C语言编程基础知识 前言: 这篇文章介绍在LINUX下进行C语言编程所需要的基础知识.在这篇文章当中,我们将 会学到以下内容: 源程序编译 Makef ...
- 8)Linux程序设计入门--线程操作
)Linux程序设计入门--线程操作 前言:Linux下线程的创建 介绍在Linux下线程的创建和基本的使用. Linux下的线程是一个非常复杂的问题,由 于我对线程的学习不时很好,我在这里只是简单的 ...
- 7)Linux程序设计入门--网络编程
)Linux程序设计入门--网络编程 Linux系统的一个主要特点是他的网络功能非常强大.随着网络的日益普及,基于网络的 应用也将越来越多. 在这个网络时代,掌握了Linux的网络编程技术,将令每一个 ...
- 4)linux程序设计入门--时间概念
)程序设计入门--时间概念 前言:Linux下的时间概念 这一章我们学习Linux的时间表示和计算函数 时间的表示 时间的测量 计时器的使用 .时间表示 在程序当中,我们经常要输出系统当前的时间,比如 ...
- Linux程序设计(一)入门
1. linux程序 Linux应用程序表现为两种特殊类型的文件:可执行文件和脚本文件. 可执行文件:是计算可以直接运行的程序,相当于windows的.exe文件. 脚本文件:一组指令的集合.这些指令 ...
- Linux基础入门
第一节,linux系统简介 一.实验内容 了解 Linux 的历史,Linux 与 Windows 的区别等入门知识. 二.实验要求 阅读linux简介与历史 三.实验步骤 (一).Linux 为何物 ...
随机推荐
- iOS客户端学习之AES加密
数据加密在解密在软件开发过程中举足轻重的作用,可能有的公司在加密的时候有自己公司内部一套设计的算法,而在这方面不想浪费太大精力就可以去考虑使用第三方提供的加密算法,如AES加密算法,本篇内容介绍开源中 ...
- yii2 DateTimePicker显示到天
扩展是 kartik\datetime\DateTimePicker; 关键是加入此配置 'minView'=> "month",示例如下: <?php echo D ...
- JAVA编程思想读书笔记(一)--面向对象
第二章 一切都是对象 No1: 程序运行时,我们最好对数据保存到什么地方做到心中有数.特别要注意的是内存的分配.有六个地方都可以保存数据: (1) 寄存器.这是最快的保存区域,因为它位于和其他所有保存 ...
- Wannafly挑战赛7 E - 珂朵莉与GCD
题目描述 给你一个长为n的序列a m次查询 每次查询一个区间的所有子区间的gcd的和mod1e9+7的结果 输入描述: 第一行两个数n,m之后一行n个数表示a之后m行每行两个数l,r表示查询的区间 输 ...
- STM32使用定时器实现输入捕获
输入捕获简介输入捕获模式可以用来测量脉冲宽度或者测量频率.STM32的定时器,除了TIM6和TIM7,其他定时器都有输入捕获功能. STM32的输入捕获,简单地说就是通过检测TIMx_CHx上的边沿信 ...
- Git 无法拉取,Unlink of file '.git/objects/pack/pack-***.pack' failed. Should I try again? (y/n)
现象 Git 无法拉取,提示: Unlink of file '.git/objects/pack/pack-***.pack' failed. Should I try again? (y/n) 原 ...
- 详细理解Java虚拟机的运行过程
基本概述: Java虚拟机简称JVM,是JRE中的一部分,也是Java程序运行的最关键的部分.完整的Java运行流程大致包括编译.java文件形成.class文件,然后根据.class文件的内容进行一 ...
- Zookeeper启动和集群选举
1. QuorumPeerMain运行 1)判断是采用单实例模式还是多实例模式启动QuorumPeerMain 2)在多实例模式下,加载启动参数中指定的配置文件 3)启动QuorumPeer publ ...
- PHP -- 简单表单提交
网上看博文,一步步入门~~ 简单表单,简单提交 @_@!! <?php //php代码部分开始 echo "<html>"; echo "<hea ...
- 2015 UESTC 数据结构专题A题 秋实大哥与小朋友 线段树 区间更新,单点查询,离散化
秋实大哥与小朋友 Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://acm.uestc.edu.cn/#/contest/show/59 Desc ...