linux之信号

信号:在生活中,我们遇到过不同种类的信号,比如:(交通信号,乃至某个人的表情,动作等带给你不同的信号)然而,在我们的linux下,我们最熟悉的就是,当遇到一个死循环的程序时,我们第一想到的就是按ctrl+c,此时这个进程立马终止,这是一种通过键盘产生的信号。而说起ctrl+c,就引出了前台进程和后台进程。当ctrl+c产生的信号只能发给前台进程。

用kill -l命令就可以查看信号了;


产生信号的另一种方式是:信号异常触发系统使该进程终止:

例子:

运行结果:



还有一种方式,通过指令来使该进程终止


然后直接运行./test:



还有一种方式,通过alarm使进程终止

说起alarm给大家举个例子吧,alarm意思是闹钟,在这里也同样代表着当你执行某个进程时,突然用alarm定时的时间到了,这时闹钟发挥了作用,使得你的进程被迫停止,比如:


这是一个统计1秒钟计数的程序,当一秒钟到时,就会被SIGALRM信号终止。

运行结果:


下面我们来说一下处理信号的几种方式吧!

忽略信号;执行默认;执行自定义(信号的捕捉)三种方式。(那么问题来了,什么时候处理呢?答案是适当的时候处理大笑


执行信号的处理动作称为信号递达,信号从产生到递达之间的状态,称为信号未决。进程可以选择阻塞某个信号,被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作。

阻塞和忽略是不同,只要信号被阻塞就不会递达,而忽略则是在递达之后可选的一种处理动作。

信号在内核中是这样表示的:


block:代表屏蔽状态字(1表示阻塞,0表示不阻塞)

pending:代表未决(1表示未决,0表示可以递达)

handler:代表信号的处理方式(默认,忽略,自定义)

每个信号都有两个标志位分别表示阻塞(block)和未决(pending),还有一个函数指针表示处理动作。信号产生时,内核在进程控制块中设置该信号的未决标志,直到信号递达才清除该标志。

上面那张表说明:SIGHUP是没有阻塞也没有产生,所以处理动作为默认处理动作

SIGINT的block为1,pending也为1,表示正在阻塞,无法递达。它的处理动作为忽略,但是在没有解除阻塞之前不能忽略该信号,很可能在解除阻塞前改变为其他的处理动作。

对于上述的三张表,操作系统中的每个进程运行时都会存在。

SIGQUIT的block为1,pending为0,说明正在被阻塞,解除阻塞后就可以递达。处理动作为用户自定义的处理动作。

还有一种现象是,对于解除阻塞之前可能会发送多次信号,这时操作系统该作何处理呢。这里主要是分为普通信号和实时信号,普通信号出现发送多次的情况,会当做是一次信号进行处理。而实时信号发送多次的情况,会将这多个信号存在一个队列中,分别处理各个信号。我们一般讨论的是普通信号,因此只记录一次,我们将未决和阻塞状态用同一个数据类型来存储sigset_t,信号集为sigset_t。还需要注意的一点是,阻塞信号集也叫做当前进程的信号屏蔽字。

说了信号集,我们之前说过这些普通信号是以位图的形式存放的。每一个bit位表示一种信号是否存在。

对于信号的操作,我们有一组信号集操作函数,如下:


函数sigemptyset初始化set所指向的信号集,使其中所有信号的对应bit清零,表示该信号集不包含任何有效信号。

函数sigfillset初始化set所指向的信号集,使其中所有信号的对应bit置位,表⽰示 该信号集的有效信号包括系统支

持的所有信号。

在使⽤用sigset_t类型的变量之前,⼀一定要调 ⽤用sigemptyset或sigfillset做初始化,使信号集处于确定的状态。

做完初始化之后调用sigaddset和sigdelset来添加或者删除信号。

这四个函数成功返回0,失败返回-1;

sigismember用来表示某种信号是否出现在有效信号集中。出现返回1,不出现返回0.

*还有函数sigprocmask:(读取或更改进程的信号屏蔽字)

#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oset);

成功返回0,出错返回-1

这里的oset为输出型参数,如果oset为非空,则输出当前进程的信号屏蔽字,通过oset输出。如果set为非空,则更改信号屏蔽字,how指示如何更改。如果set和oset都为非空,则将当前进程的信号屏蔽字备份在oset中,然后再通过how参数更改信号屏蔽字。

how主要有三种表示:SIG_BLOCK(包含了我们希望添加的信号屏蔽字),SIG_UNBLOCK(包含了我们希望从信号屏蔽字中阻塞的信号),SIG_SETMASK(设置了当前的信号屏蔽字的值);

*函数sigpending:(读取当前进程的未决信号集)

#include <signal.h>
int sigpending(sigset_t *set);

set为输出型参数,将信号通过set输出

 小栗子:

#include<stdio.h>
#include<signal.h>void PrintSigset(sigset_t *sig)
{int i = 0;for(i = 1; i < 32; i++){if(sigismember(sig,i)){printf("1 ");}else{printf("0 ");}}printf("\n");
}int main()
{sigset_t sigset,osigset;sigemptyset(&sigset);sigemptyset(&osigset);sigaddset(&sigset,SIGINT);sigprocmask(SIG_BLOCK,&sigset,&osigset);while(1){sigpending(&sigset);PrintSigset(&sigset);sleep(1);}return 0;
}
这个程序实现了将2号信号设置为阻塞信号。所以一直无法递达。

程序运行时,每秒钟把各信号的未决状态打印一遍,由于我们阻塞了SIGINT信号,按Ctrl-C将会使SIGINT信号处于未决

状态,按Ctrl-\和ctrl+z仍然可以终止程序,因为SIGQUIT信号没有阻塞。

运行结果:


还有一个小栗子,我们将2号信号阻塞之后,5秒后解除阻塞。并打印原来信号的状态。

#include<stdio.h>
#include<signal.h>void PrintSigset(sigset_t *sig)
{int i = 0;for(i = 1; i < 32; i++){if(sigismember(sig,i)){printf("1 ");}else{printf("0 ");}}printf("\n");
}void handler(int sig)
{printf("pid: %d sig:%d \n",getpid(),sig);
}
int main()
{sigset_t sigset,osigset;sigemptyset(&sigset);sigemptyset(&osigset);sigaddset(&sigset,SIGINT);sigprocmask(SIG_BLOCK,&sigset,&osigset);int count = 0;signal(2,handler);//signal(2,SIG_DFL);while(1){sigpending(&sigset);PrintSigset(&sigset);sleep(1);if(count++ > 5){sigprocmask(SIG_SETMASK,&osigset,NULL);count = 0;}}return 0;
}

运行结果:




本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/382709.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[Linux]继续探究mysleep函数(竞态条件)

之前我们探究过mysleep的简单用法&#xff0c;我们实现的代码是这样的&#xff1a; #include<stdio.h> #include<signal.h>void myhandler(int sig) {}unsigned int mysleep(unsigned int timeout) {struct sigaction act,oact;act.sa_handler myhandler;sigempt…

[Linux]死锁

死锁是指多个进程在运行过程中因争夺资源而造成的一种僵局&#xff0c;当进程处于这种僵持状态时&#xff0c;若无外力作用&#xff0c;它们都将无法再向前推进。之前信号量的时候我们知道&#xff0c;如果多个进程等待&#xff0c;主要体现在占有锁的问题上。死锁也可以被定义…

[Linux]线程安全和可重入函数

线程安全&#xff1a;一个函数被称为线程安全的&#xff0c;当且仅当被多个并发进程反复调用时&#xff0c;它会一直产生正确的结果。如果一个函数不是线程安全的&#xff0c;我们就说它是线程不安全的。 重入&#xff1a;函数被不同的控制流程调用,有可能在第一次调用还没返回…

[Linux]信号量

信号量是一个计数器&#xff0c;用于为多个进程提供对共享数据对象的访问。 在信号量上只有三种操作可以进行&#xff0c;初始化、递增和增加&#xff0c;这三种操作都是原子操作。递减操作可以用于阻塞一个进程&#xff0c;增加操作用于解除阻塞一个进程。 为了获得共享资源…

[Linux]关于SIGCHLD

之前我们就学过&#xff0c;关于wait和waitpid来处理僵尸进程&#xff0c;父进程等待子进程结束后自己才退出&#xff0c;这样的方法有俩种方式&#xff0c;一种是父进程死死的等子进程退出&#xff0c;也就是使用阻塞的方式等待子进程退出&#xff0c;另一种方式是通过非阻塞的…

C语言思维导图

本人能力有限&#xff0c;知识点难免概括不全&#xff0c;如有错误欢迎指正

pthread和互斥量条件变量函数意义速查表

数据类型 pthread_t 线程 互斥量和条件变量

[Linux]共享内存

共享内存是UNIX提供的进程间通信手段中速度最快的一种&#xff0c;也是最快的IPC形式。为什么是最快的呢&#xff0c;因为数据不需要在客户进程和服务器进程之间复制&#xff0c;所以是最快的一种IPC。这是虚存中由多个进程共享的一个公共内存块。 两个不同进程A、B共享内存的…

[Linux]gdb调试多进程多线程例程

gdb相信学linux的同学已经比较熟悉了吧&#xff0c;它是linux下代码调试工具。我们在写c语言&#xff0c;c的代码时经常会用到&#xff0c;它有一些常用的调试命令: run&#xff08;r&#xff09;&#xff1a;运行程序&#xff0c;如果有断点在下一个断点处停止 start&#xf…

[Linux]守护进程(精灵进程)

一、守护进程是什么 守护进程是生存期很长的一种进程&#xff0c;可以说它是7*24小时工作的。&#xff08;什么是7*24&#xff0c;一周7天&#xff0c;每天24小时&#xff0c;这不就是一年365天一直在工作嘛&#xff0c;还搞的这么诙谐&#xff0c;哈哈&#xff09;。它们常常…

浅谈shell中的clear命令实现

NAME(名称) clear - 清除终端屏幕 SYNOPSIS(总览) clear DESCRIPTION(描述) clear可以在允许的情况下清屏. 它会在环境变量中查找终端的类型, 然后到terminfo数据库中找出清屏的方法. 《man手册》 #include <stdio.h>int clear_main(int argc, char **argv) {/* Th…

[Linux]ARP协议

概念&#xff1a; 1. ARP协议(地址解析协议):由IP地址转换为MAC地址的协议。IP地址&#xff1a;网络号主机号。MAC地址&#xff1a;数据链路层的物理地址&#xff08;硬件地址&#xff09;。IP协议使用了ARP协议&#xff0c;因此被划归为网络层&#xff0c;但其用途是从网络层…

Makefile使用及多文件gdb 调试

文件内容 [koulocalhost makefile]$ cat 1.c #include "3.h" int main() {key_t key ftok(".",1);printf("%d\n",add(1,2));return 0; }[koulocalhost makefile]$ cat 2.c #include "3.h" int add(int a, int b) {return a b; } [k…

[Linux]CRC校验

CRC(Cyclic Redundancy Check),循环冗余校验码&#xff0c;是数据通信领域中最常用的一种差错校验码&#xff0c;其特征是信息字段和校验字段的长度可以任意选定。 CRC校验步骤&#xff1a; CRC分为两部分&#xff0c;前部分为信息码&#xff0c;后部分为校验码&#xff1b;设…

python字符串系列

1.find方法用于在长串中查找子串&#xff0c;返回子串中最左位置的下标&#xff0c;如果没找到&#xff0c;则返回-1 2.join方法用于在队列中添加元素 3.lower返回字符串的小写字母版 4.replace返回字符串中所有匹配项均被替换之后得到字符串 5.split将字符串分割成序列 6.stri…

linux网络编程Internet Socket地址,套接字,和函数

文章内容节选《linux/UNIX 系统网络编程》 Internet domain socket地址有两种&#xff1a;IPv4 IPv6 IPv4被存储在结构体中&#xff0c; 该结构体在 netinet/in.h 中进行定义 cd usr/include/netinet/in.h struct in_addr {in_addr_t s_addr; //32位IPv4地址 }struct so…

浅谈socket网络编程函数参数(一)

socket函数解析 概念: 每个进程的进程空间里都有一个socket描述符表。套接字描述符表属于一个进程&#xff0c;而socket地址结构位于操作系统的内核缓冲。 函数原型 #include <sys/socket.h>int socket(int domain, int type, int protocol);函数参数 family参数 默…

为什么计算机起始时间是1970年1月1日

1969年8月&#xff0c;贝尔实验室的程序员肯汤普逊利用妻儿离开一个月的机会&#xff0c;开始着手创造一个全新的革命性的操作系统&#xff0c;他使用B编译语言在老旧的PDP-7机器上开发出了Unix的一个版本。随后&#xff0c;汤普逊和同事丹尼斯里奇改进了B语言&#xff0c;开发…

TCP三次挥手四次握手(面试总结)

1、 为什么建立连接协议是三次握手&#xff0c;而关闭连接却是四次握手呢&#xff1f; 全双工通信。 这是因为服务端的LISTEN状态下的SOCKET当收到SYN报文的建连请求后&#xff0c;它可以把ACK和SYN&#xff08;ACK起应答作用&#xff0c;而SYN起同步作用&#xff09;放在一个…