Linux下的信号(signal)

本文详细介绍了Linux系统下的信号机制,包括信号的产生、处理、注册、注销等过程。讲解了信号如何打断进程的阻塞操作,核心转储的设置与使用,以及信号的阻塞与未决状态。此外,还探讨了信号处理中的竞态条件、可重入概念,以及SIGCHLD信号在处理子进程退出时的角色。通过对信号机制的深入理解,有助于提升Linux系统的管理和调试能力。
摘要由CSDN通过智能技术生成

1.信号:是一个软中断--通知进程事件的发生

    信号的生命周期:产生->注册->注销->处理

    产生:(硬件/软件)

    处理:(默认/忽略/自定义)

2.信号的产生

    62种信号---使用kill -l命令查看

    1-31     (非可靠信号/非实时信号)

    34 -64  (可靠信号/实时信号)

    硬件产生:ctrl+c   ctrl+l   ctlr+z

    软件产生:kill命令    kill()   raise()    abort()   alarm()

    信号的到来会打断当前进程的阻塞操作

    core dumped:核心转储---异常退出时保存程序运行信息

        默认是关闭状态

        ulimit -a 查看核心转储文件大小

        ulimit -c 设置核心转储文件大小   ulimit -c 1024

        core文件命令方式:core.pid

        core文件的使用:

            gdb ./loop -> core-file core.pid

            先加载运行程序->加载程序运行信息->开始调试:bt查看调用栈信息

3.信息号注册

    pcb->struct sigpending->_sigset_t(信号集合)

    操作系统给一个进程发送信号,实际就是向这个进程pcb的信号pending集合中添加信号(修改位图)

    因为位图只能标记信号是否存在,不能标记到来信号的个数

    pcb有一个sigqueue链表,信号到来就会组织一个节点添加到这个链表中

    可靠信号到来:修改位图(不管位图是否为1),每个信号都组织节点添加到链表中

    非可靠信号到来:修改位图,若位图已经为1,则什么都不做;否则添加一个节点,修改位图

4.信号的注销

    可靠信号:因为节点有可能有多个,删除节点后判断是否还有相同节点,判断是否修改位图

    非可靠信号:因为节点只有一个,因此删除节点后,位图直接修改为0

5.信号的处理

    默认:因为每个信号都对应一个事件,这些事件在操作系统中都有既定义完毕的处理方式

    忽略:什么都不干

    自定义:用户自己定义处理方式(函数)---修改内核中信号所对应的处理方式

    信号的捕捉流程:自定义信号

    

    信号的捕捉处理是在从程序运行内核态到用户态切换之前完成

       1. 信号的处理方式选择则一个合适的时机去处理---从内核态运行切换到用户态运行之前看有没有信号需要被处理;

       2. 如果有信号待处理,并且是自定义处理方式,调用do_signal处理信号,返回用户态执行我们自定的信号处理函数;

       3. 用户自定义信号的处理函数执行完毕后,调用sig_return返回内核态;

        4.再次查看是否有信号待处理,如果没有了,则返回用户态回到程序主控流程,执行程序。

    一个进程如何从用户态切换到内核态(系统调用接口、程序异常、中断)

/*************************************************************************
	> File Name: loop.c
	> Author: ssh
	> Mail: sunshihao163@163.com 
	> Created Time: Mon 15 Apr 2019 04:56:57 PM CST
 ************************************************************************/

#include<stdio.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>

void sigcb(int signo){
	printf("recv signo:%d\n", signo);
}

int main(){
	//sighandler_t signal(int signum, sighandler_t handler);
	//	修改信号在内核中的处理方式
	//	signum:	信号编号
	//	handler:	处理方式
	//		SIG_DFL	默认处理方式
	//		SIG_IGN	忽略处理方式
	//		typedef void(*sighandler_t)(int);
	//signal(SIGQUIT, SIG_IGN);
	signal(SIGQUIT, sigcb);

	//int kill(pid_t pid, int sig);
	//	pid:指定信号要发送给那个进程的进程id
	//	sig:信号编号/usr/include/bits/signum.h
	//kill(getpid(), SIGSEGV);
	
	//int raise(int sig);
	//	给调用进程发送SIGABRT信号
	raise(SIGQUIT);
	
	//void abort(void);
	//	给调用进程发送SIGABRT信号
	//abort();

	//unsigned int alarm(unsigned int seconds);
	//	定时器:在seconds秒之后向调用进程发送SIGALRM信号
	//	返回值:上一个定时器剩余的时间或者0
	//alarm(3);
	while(1){
		printf("hello bit!\n");
		sleep(10);
	}
	return 0;
}

6.信号的阻塞:暂时不处理这个信号---阻止信号递达

    信号的递达:动作--信号处理

    信号的未决:状态--信号从产生到递达之间的状态

    未决信号:还没有被处理的信号

    sigset_t      信号集合(数组-位图)

    信号进行阻塞:实际上就是在blocked位图中标记那些信号到来之后暂时不被处理

    有两个信号是不会被阻塞,也不会被自定义:SIGKILL    SIGSTOP

    如何阻塞信号/解除阻塞

        sigemptyset  清空信号集合      sigfillset  向集合中填充所有信号 

        sigaddset    向集合中添加指定信号     sigprocmask    从集合中删除指定信号

    如何获取未决信号

        sigpending   判断指定信号是否在集合中 mask & 1 << num

        sigismemer   获取进程当前未决信号

     先阻塞所有信号->getchar()->解除阻塞-----信号的阻塞;可靠/非可靠信号处理

     mysleep实现:信号会打断当前进程的阻塞操作

        alarm+pause----pause使进程阻塞,alarm保证n秒中之后发送信号打断阻塞

        sigsuspend(mask)--临时使用mask替换阻塞集合,阻塞mask中的信号---陷入休眠---唤醒后还原阻塞信号

        先阻塞SIGALRM防止定时器与休眠之间SIGALRM被处理,使用sigsuspend对SIGALRM解除阻塞,并且陷入休眠(原子操作)

​/*************************************************************************
	> File Name: mask.c
	> Author: ssh
	> Mail: sunshihao163@163.com 
	> Created Time: Mon 15 Apr 2019 05:09:50 PM CST
 ************************************************************************/

/*	信号的阻塞
 *	int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
 *		how:
 *			SIG_BLOCK	阻塞set集合中的信号,将原有阻塞放到old中
 *			SIG_UNBLOCK	对set集合中的信号解除阻塞
 *			SIG_SETMASK	将set集合中的信号添加到阻塞集合中
 * */

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>

void sigcb(int signo){
	printf("recv signo:%d\n", signo);
}

int main(){
	struct sigaction act;
	sigemptyset(&act.sa_mask);
	act.sa_handler = sigcb;
	act.sa_flags  = 0;
	//int sigaction(int signum, struct sigaction *act, strcut sigactiong *oldset);
	//	signum:	信号编号
	//	act:	自定义处理动作
	//	oldset:	保存信号原有处理动作
	sigaction(SIGINT, &act, NULL);
	sigaction(SIGRTMIN+5, &act, NULL);
	/*	定义一个集合,
	 *	向集合中添加要阻塞的信号
	 *	阻塞这个集合的所有信号
	 *	getchar()
	 *	对集合中的信号解除阻塞
	 * */
	sigset_t newset, oldset;
	//int sigemptyset(sigset_t *set); 清空信号集合
	sigemptyset(&newset);
	//int sigfillset(sigset_t *set);   将所有信号添加到集合中
	//int sigaddset(sigset_t *set, int signum);  将指定信号添加到集合中
	sigfillset(&newset);
	//int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
	sigprocmask(SIG_BLOCK, &newset, &oldset);
	printf("all signal block, enter for unblocked\n");
	getchar();

	sigset_t set;
	//int sigpending(sigset_t *set);
	//获取未决信号
	sigpending(&set);
	int i;
	for(i = 1; i <= 62; i++){
		//int sigismember(const sigset_t *set, int signum);
		//判断指定信号是否在集合中
		if(sigismember(&set, i)){
			printf("1 ");
		}else{
			printf("0 ");
		}
	}
	printf("\n");
	sigprocmask(SIG_UNBLOCK, &newset, NULL);
	//sigprocmask(SIG_UNBLOCK, &oldset, NULL);
	
	return 0;
}
​

7.竞态条件:多个执行时间之间出现的资源竞争问题

    可重入/不可重入:能否在多个运行时序重复调用,而不造成数据二义/程序异常

        可不可重入的关键点:是否对非原子操作的全局数据进行了操作

        原子性操作:操作不可别打断

        当用户进行函数接口设计及调用的时候,就要考虑接口是否可重入

        不可重入情况:malloc和free---不可重入---内部有不受保护的全局链表操作;调用了标准I/O库函数,标准I/O库的实现都以不可重入的方式使用全局数据结构

    关键字:volatile---保持内存可见性--防止编译器对代码进行过度优化

        若要进行代码优化,就需要用户对进程有更多的可控---需要明白那些变量需要修饰

    SIGCHLD信号:17号信号

        一个进程退出后,操作系统通知父进程子进程已经退出(操作系统通过SIGCHLD信号通知父进程)

        问题:SIGCHLD信号的默认处理方式就是忽略处理

        因为父进程不知道SIGCHLD信号什么时候到来(不知道子进程什么时候退出),因此创建子进程之后就要进行等待;但是这样造成父进程的浪费(什么都没做,就是死等子进程);

        如果父进程直接对SIGCHLD信号进行了自定义处理方式,并且自定义的处理方式中包含了进程等待;这时候就不需要空等待了;

        问题:等待进程退出以while循环等待

        因为SIGCHLD信号是一个非可靠信号,只注册一次,意味着sigcb只会被回调一次,然而waitpid调用一次,只能处理一个进程退出;因此就尽可能的在回调中将退出的进程全部处理掉while(waitpid(-1, NULL, WNOHANG) > 0);只要返回值大于0就表示有子进程退出,那就一直处理,直到没有子进程退出(返回0)或者出错(返回-1),退出while循环

    

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值