在Linux系统中,信号(signal)是一种异步事件的机制,可以用来通知进程发生了某种事件。Linux系统提供了Signal.h头文件来支持信号处理机制,其中包含了各种信号相关的常量、结构体和函数。
我们拥有10余年网页设计和网站建设经验,从网站策划到网站制作,我们的网页设计师为您提供的解决方案。为企业提供成都做网站、成都网站建设、微信开发、小程序设计、手机网站制作设计、HTML5、等业务。无论您有什么样的网站设计或者设计方案要求,我们都将富于创造性的提供专业设计服务并满足您的需求。
本文将从以下几个方面,包括信号的基本概念、信号的分类、信号的发送和接收、信号的处理方式以及信号的应用实例。
一、信号的基本概念
信号是由操作系统发送给进程的一种通知机制,用于向进程发送各种事件的通知。当某个事件发生时,操作系统会向特定的进程发送相应的信号,进程可以注册信号处理函数来处理这些信号。
Linux系统中有几十种不同的信号,每一种信号都用一个整数常量来表示。这些常量定义在Signal.h头文件中,并以”SIG”开头。例如,SIGINT表示当用户按下Ctrl+C时发送给前台进程的信号,SIGTERM表示进程终止信号,SIGKILL表示无法处理或终止进程的强制终止信号。
二、信号的分类
Linux系统中的信号可以根据不同的分类方式进行分类,其中较为常见的分类方式包括以下几种:
1.默认信号和自定义信号
默认信号是由系统定义的且不能被修改的信号,如SIGTERM、SIGKILL等。自定义信号可以由用户根据自己的需求来定义,如SIGUSR1、SIGUSR2等。
2.同步信号和异步信号
同步信号是进程在执行某些操作时自己产生的信号,如除零错误、访问非法地址等。异步信号是由进程外部的事件(如按键、定时器、网络套接字等)触发引起的信号。
3.可靠信号和不可靠信号
可靠信号是指能保证信号不会被丢失或重复发送的信号。不可靠信号可能会在信号处理函数还未执行完毕时再次被发送,或者在信号处理函数中多次发送。
三、信号的发送和接收
Linux系统中,进程可以使用kill函数向其他进程或自己发送信号。kill函数的原型如下:
“`c++
#include
int kill(pid_t pid, int sig);
“`
其中pid参数表示接收信号的进程ID,sig参数表示发送的信号。
接收信号的进程需要通过signal函数来注册信号处理函数,如:
“`c++
#include
void signal_handler(int sig) {
// 处理信号
}
int mn() {
signal(SIGINT, signal_handler); // 注册键盘中断信号处理函数
while (1) {
// 循环等待信号
}
return 0;
}
“`
在本例中,调用signal函数来注册SIGINT信号,并指定了signal_handler作为处理函数。当接收到SIGINT信号时,操作系统会调用signal_handler处理函数来处理信号。
四、信号的处理方式
接收到信号之后,操作系统会调用之前注册的信号处理函数来处理信号。关于信号处理函数的基本要求如下:
1.信号处理函数必须是一个C语言函数,返回值为void。
2.信号处理函数不能有任何参数。
3.信号处理函数必须是可重入的,因为它可能在任何时间被调用。
信号处理函数有以下三种处理方式:
1.默认处理方式
默认处理方式就是不做任何处理,直接忽略信号或终止进程。可以使用signal函数进行设置,如:
“`c++
#include
signal(SIGINT, SIG_DFL); // 默认操作为终止进程
“`
2.忽略信号
可以使用signal函数将信号处理函数设置为SIG_IGN来忽略某个信号,如:
“`c++
#include
signal(SIGINT, SIG_IGN); // 忽略键盘中断信号
“`
3.自定义处理方式
可以编写自定义的信号处理函数来处理特定的信号,如:
“`c++
#include
void signal_handler(int sig) {
// 处理信号
}
signal(SIGINT, signal_handler); // 注册键盘中断信号处理函数
“`
五、信号的应用实例
Linux系统中,信号机制广泛应用于进程间的通信、进程控制、时间管理等领域。下面列举几个信号的应用实例。
1.进程间通信
可以使用kill函数向其他进程发送自定义信号,如:
“`c++
kill(pid, SIGUSR1); // 向进程pid发送SIGUSR1信号
“`
收到信号的进程可以根据信号类型和来源,决定如何响应。例如,可以使用自定义信号来实现双方进程的心跳检测、资源分配等功能。
2.进程控制
可以使用信号机制来控制进程的行为,例如通过SIGSTOP和SIGCONT信号来停止和继续进程的执行,如:
“`c++
kill(pid, SIGSTOP); // 停止进程pid的执行
kill(pid, SIGCONT); // 继续进程pid的执行
“`
3.时间管理
可以使用定时器信号来进行时间管理,例如使用SIGALRM信号来实现闹钟功能,如:
“`c++
#include
#include
void alarm_handler(int sig) {
// 播放音乐或执行其他操作
}
signal(SIGALRM, alarm_handler); // 注册定时器信号处理函数
alarm(10); // 10秒后发送SIGALRM信号
pause(); // 等待定时器信号
“`
在本例中,使用alarm函数设置10秒钟的定时器,当定时器到期时,将发送SIGALRM信号通知进程。进程在收到SIGALRM信号时,调用alarm_handler函数进行处理。
成都网站建设公司-创新互联,建站经验丰富以策略为先导10多年以来专注数字化网站建设,提供企业网站建设,高端网站设计,响应式网站制作,设计师量身打造品牌风格,热线:028-86922220驱动程序运行在内核空间中,应用程序运行在用户空间中,两者是不能直接通信的。但在实际应用中,在设备已经准备好的时候,我们希望通知用户程序搏告坦设备已经ok,用户程序可以读取了,这样应用程序就不需要一直查询该设备的状态,从而节约了资源,这就是异步通知。好,那下一个问题就来了,这个过程如何实现呢?简单,两方面的工作。
一 驱动方面:
1. 在设备抽象的数据结构中增加一个struct fasync_struct的指针
2. 实现设备操作中的fasync函数,这个函数很简单,其主体就是调用内核的fasync_helper函数。
3. 在需要向用户空间通知的地方(例如中断中)调用内核的kill_fasync函数。
4. 在驱动的release方法中调用前面定义的fasync函数
呵呵,简单吧,就三点。其中fasync_helper和kill_fasync都是内核函数,我们只需要调用就可以了。在
1中定义的指针是一个重要参数,fasync_helper和kill_fasync会使用这个参数。
二 应用层方面
1. 利用signal或者sigaction设置SIGIO信号的处理函数
2. fcntl的F_SETOWN指令设置当前进程为设备文件owner
3. fcntl的F_SETFL指令设置FASYNC标志
完成了以上的工作的话,当内核执行到kill_fasync函数,用户空间SIGIO函数的处理函数就会被调用了。
呵呵,看起来不是很复杂把,让我们结合具体代码看看就更明白了。
先从应用层代码开始吧:
#include
#include
#include
#include
#include
#include
#define MAX_LEN 100
//处理函数,没什么好讲的,用户自己定义
void input_handler(int num)
{
char data;
int len;
//读取并输出STDIN_FILENO上的输入
len = read(STDIN_FILENO, &data, MAX_LEN);
data = 0;
printf(“input available:%s\n”, data);
}
void main()
{
int oflags;
//启动信号驱动机制,将SIGIO信号同input_handler函数关联起来,一旦产生SIGIO信号,就会执行input_handler
signal(SIGIO, input_handler);
//STDIN_FILENO是打开的设备文件描述符,F_SETOWN用来决定操作是干什么的,getpid()是个系统调用,
//功能是返回当前进程的进程号,整个函数基桐的功能是STDIN_FILENO设置这个设备文件的拥有者为当前进程。
fcntl(STDIN_FILENO, F_SETOWN, getpid());
//得到打开文件描述符的状态
oflags = fcntl(STDIN_FILENO, F_GETFL);
//设置文件描述符的状态为oflags | FASYNC属性,一旦文件描述符被设置成具有FASYNC属性的状态,
//也就是将设备文件切换到异步操作模式。这时系统就会自动调用驱动程序的fasync方法。
fcntl(STDIN_FILENO, F_SETFL, oflags | FASYNC);
//最后进入一个死循环,程序什么都不干了,只有信号能激发input_handler的运行
/友凯/如果程序中没有这个死循环,会立即执行完毕
while (1);
}
再看驱动层代码,驱动层其他部分代码不变,就是增加了一个fasync方法的实现以及一些改动
//首先是定义一个结构体,其实这个结构体存放的是一个列表,这个
//列表保存的是一系列设备文件,SIGIO信号就发送到这些设备上
static struct fasync_struct *fasync_queue;
//fasync方法的实现
static int my_fasync(int fd, struct file * filp, int on)
{
int retval;
//将该设备登记到fasync_queue队列中去
retval=fasync_helper(fd,filp,on,&fasync_queue);
if(retval
{
return retval;
}
return 0;
}
在驱动的release方法中我们再调用my_fasync方法
int my_release(struct inode *inode, struct file *filp)
{
//..processing..
drm_fasync(-1, filp, 0);
//..processing..
}
这样后我们在需要的地方(比如中断)调用下面的代码,就会向fasync_queue队列里的设备发送SIGIO信号
,应用程序收到信号,执行处理程序
if (fasync_queue)
kill_fasync(&fasync_queue, SIGIO, POLL_IN);
好了,这下大家知道该怎么用异步通知机制了吧?
以下是几点说明:
1 两个函数的原型
int fasync_helper(struct inode *inode, struct file *filp, int mode, struct fasync_struct **fa);
一个”帮忙者”, 来实现 fasync 设备方法. mode 参数是传递给方法的相同的值, 而 fa 指针指向一个设
备特定的 fasync_struct *
void kill_fasync(struct fasync_struct *fa, int sig, int band);
如果这个驱动支持异步通知, 这个函数可用来发送一个信号到登记在 fa 中的进程.
2.
fasync_helper 用来向等待异步信号的设备链表中添加或者删除设备文件, kill_fasync被用来通知拥有相关设备的进程. 它的参数是被传递的信号(常常是 SIGIO)和 band, 这几乎都是 POLL_IN(但是这可用来发送”紧急”或者带外数据, 在网络代码里).
这判州宴跟execvp函数的实现方式有关:
int execvp(const char *file ,char * const argv );
execvp()会从PATH 环境变量所指的目录中查找符掘银合参数file的文件名,找到后便执行该文件,然后将第二个参数argv传给该欲执行的文件。如果执行成功则函数不会返回,执行失败则直接返回-1,失败原因存于errno中。
之所以显示“fail to exec”,是因为在PATH环境变量所指的目录中没有名为“hello”的程序。建议进行如下操作:
1、运行“echo $PATH”,查看一迹乱下PATH环境变量指向那些目录
2、编写一个输出“hello world”的程序,并命名为hello,即执行命令:
gcc -o hello hello.c
3、把名为”hello“的程序拷贝到PATH变量所指的其中一个目录中
可以分三步来做:
做两个简单的守护进程,并能正常运行
监控进程是否在运行
启动进程
综合起来就可以了,代码如下:
被监控进程thisisatest.c(来自
):
#include
#include
#include
#include
#include
#include
#include
#include
void init_daemon()
{
int pid;
int i;
pid=fork();
if(pid0) //父进程退出
exit(0);
setsid(); //使子进程成为组长
pid=fork();
if(pid>0)
exit(0); //再次退出,使进程不是组长,这样进程就不会打开控制终端
else if(pid=0)
{
time(&t);
竖族 fprintf(fp,”current time is:%s\n”,asctime(localtime(&t))); //转换为本地时间输出
fclose(fp);
}
}
return;
}
监控进程monitor.c:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define BUFSZ 150
void init_daemon()
{
int pid;
int i;
pid=fork();
if(pid0) //父进程退出
exit(0);
setsid(); //使子进程成为组长
pid=fork();
if(pid>0)
exit(0); //再次退出,使进程不是组长,这样进程就不会打开控制终端
else if(pid=0)
{
count = does_service_work();
time(&t);
if(count>0)
fprintf(fp,”current time is:%s and the process exists, the count is %d\n”,asctime(localtime(&t)), count); //转换为本地时间输出
else
{
fprintf(fp,”current time is:%s and the process does not exist, restart it!\n”,asctime(localtime(&t))); //转换为本地时间输出
system(“/home/user/daemon/thisisatest”); //启动服务
}
fclose(fp);
}
}
return;
}
具体CMD命令:
cc thisisatest.c -o thisisatest
./thisisatest
cc monitor.c -o monitor
./monitor
tail -f testfork3.log — 查看日志
linux signal.h的介绍就聊到这里吧,感谢你花时间阅读本站内容,更多关于linux signal.h,深入探索Linux Signal.h信号处理机制,当linux应用程序中存在多个异步通知时怎样处理,写一个linux下写个关于c语言的双守护进程,就是监视一个进程,当其死掉,马上将其重启的信息别忘了在本站进行查找喔。
成都创新互联科技公司主营:网站设计、网站建设、小程序制作、成都软件开发、网页设计、微信开发、成都小程序开发、网站制作、网站开发等业务,是专业的成都做小程序公司、成都网站建设公司、成都做网站的公司。创新互联公司集小程序制作创意,网站制作策划,画册、网页、VI设计,网站、软件、微信、小程序开发于一体。
当前题目:深入探索LinuxSignal.h信号处理机制(linuxsignal.h)
转载来源:http://www.shufengxianlan.com/qtweb/news44/128944.html
网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联