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 为何物 ...
随机推荐
- 洛谷P1319压缩技术 题解
题目传送门 这道题是入门难度的题.特别水...QWQ...... #include<bits/stdc++.h> using namespace std; *],top; int main ...
- python开发学习-day03(set集合、collection系列 、深浅拷贝、函数)
s12-20160116-day03 *:first-child { margin-top: 0 !important; } body>*:last-child { margin-bottom: ...
- 原生js将数组分割成固定个数一组的小数组
reSetData(dataList,num) { let arr = []; let len = dataList.length; for (let i = 0; i < len; i += ...
- css写无缝滚动
html结构: <div class="authority"> <ul> <li> <img src="./images/rep ...
- Asp.net gzip压缩的启用
gzip压缩使用一种压缩算法,对网页内容进行压缩,从而减小了网页体积.使用gizp压缩后减小了服务器的带宽.提高了网页的打开速度.下边看看我找到的一个asp.net中启用gzip压缩方案. 首先,我们 ...
- SQL 如何查找一个表里,每个班级各个学科的最高分?
SQL 如何查找一个表里,每个班级各个学科的最高分? 学生表:STUDENT(S#,SNAME,SAGE,SSEX,CLASSNO) 班级表:CLASS(CLASSNO,CLASSNAME) 课程表: ...
- axios请求数据
1.安装axios模块 import axios from 'axios'; //安装方法 npm install axios //或 bower install axios 2.引入模块 直接引用: ...
- XV6操作系统代码阅读心得(三):锁
锁是操作系统中实现进程同步的重要机制. 基本概念 临界区(Critical Section)是指对共享数据进行访问与操作的代码区域.所谓共享数据,就是可能有多个代码执行流并发地执行,并在执行中可能会同 ...
- iOS 9音频应用播放音频之音量设置与声道设置
iOS 9音频应用播放音频之音量设置与声道设置 iOS 9音频应用音量设置 音量又称响度.音强,是指人耳对所听到的声音大小强弱的主观感受,其客观评价尺度是声音的振幅大小.在iOS 9音频应用的应用中, ...
- 2017/11/6 Leetcode 日记
2017/11/6 Leetcode 日记 344. Reverse String Write a function that takes a string as input and returns ...