linux信号机制分析

概念

信号递达:实际执行信号的处理动作就是信号递达

信号未决:信号从产生到递达之间的状态就是信号未决(未决就是没有解决)

收到某信号后,把未决信号集中的此信号置为1(1表示未解决的信号),然后去阻塞信号集中查看此信号有没有被阻塞,如果没有被阻塞的话,那就去信号捕捉函数数组中查看该如何处理该信号

在阻塞信号集中,阻塞和未阻塞用一个bit表示就可以,在未决信号集中也是一样,因此阻塞和未决可以用相同的数据类型sigset_t来存储

发送信号

不管内核驱动调用kill_fasync发送信号,还是用户层调用kill系统调用,最终都会调通过__send_signal_locked,调用内核函数sigaddset来将信号加入信号集

kill_fasync__send_signal_locked
sys_kill__send_signal_locked
static int __send_signal_locked(int sig, struct kernel_siginfo *info,struct task_struct *t, enum pid_type type, bool force)
{struct sigpending *pending;struct sigqueue *q;int override_rlimit;int ret = 0, result;lockdep_assert_held(&t->sighand->siglock);result = TRACE_SIGNAL_IGNORED;if (!prepare_signal(sig, t, force))goto ret;pending = (type != PIDTYPE_PID) ? &t->signal->shared_pending : &t->pending;/** Short-circuit ignored signals and support queuing* exactly one non-rt signal, so that we can get more* detailed information about the cause of the signal.*/result = TRACE_SIGNAL_ALREADY_PENDING;if (legacy_queue(pending, sig))goto ret;result = TRACE_SIGNAL_DELIVERED;/** Skip useless siginfo allocation for SIGKILL and kernel threads.*/if ((sig == SIGKILL) || (t->flags & PF_KTHREAD))goto out_set;/** Real-time signals must be queued if sent by sigqueue, or* some other real-time mechanism.  It is implementation* defined whether kill() does so.  We attempt to do so, on* the principle of least surprise, but since kill is not* allowed to fail with EAGAIN when low on memory we just* make sure at least one signal gets delivered and don't* pass on the info struct.*/if (sig < SIGRTMIN)override_rlimit = (is_si_special(info) || info->si_code >= 0);elseoverride_rlimit = 0;//新分配一个sigqueue,将其加入pending队列q = __sigqueue_alloc(sig, t, GFP_ATOMIC, override_rlimit, 0);if (q) {list_add_tail(&q->list, &pending->list);switch ((unsigned long) info) {case (unsigned long) SEND_SIG_NOINFO:clear_siginfo(&q->info);q->info.si_signo = sig;q->info.si_errno = 0;q->info.si_code = SI_USER;q->info.si_pid = task_tgid_nr_ns(current,task_active_pid_ns(t));rcu_read_lock();q->info.si_uid =from_kuid_munged(task_cred_xxx(t, user_ns),current_uid());rcu_read_unlock();break;case (unsigned long) SEND_SIG_PRIV:clear_siginfo(&q->info);q->info.si_signo = sig;q->info.si_errno = 0;q->info.si_code = SI_KERNEL;q->info.si_pid = 0;q->info.si_uid = 0;break;default:copy_siginfo(&q->info, info);break;}} else if (!is_si_special(info) &&sig >= SIGRTMIN && info->si_code != SI_USER) {/** Queue overflow, abort.  We may abort if the* signal was rt and sent by user using something* other than kill().*/result = TRACE_SIGNAL_OVERFLOW_FAIL;ret = -EAGAIN;goto ret;} else {/** This is a silent loss of information.  We still* send the signal, but the *info bits are lost.*/result = TRACE_SIGNAL_LOSE_INFO;}out_set:signalfd_notify(t, sig);sigaddset(&pending->signal, sig);/* Let multiprocess signals appear after on-going forks */if (type > PIDTYPE_TGID) {struct multiprocess_signals *delayed;hlist_for_each_entry(delayed, &t->signal->multiprocess, node) {sigset_t *signal = &delayed->signal;/* Can't queue both a stop and a continue signal */if (sig == SIGCONT)sigdelsetmask(signal, SIG_KERNEL_STOP_MASK);else if (sig_kernel_stop(sig))sigdelset(signal, SIGCONT);sigaddset(signal, sig);}}//唤醒进程complete_signal(sig, t, type);
ret:trace_signal_generate(sig, info, t, type != PIDTYPE_PID, result);return ret;
}

应用层接口

1.int sigemptyset(sigset_t* set);

功能:初始化信号集(把set所指向的信号集中的所有信号的对应bit清零,表示该信号集不包含任何有效信号)

2.int sigfillset(sigset_t* set);

功能:初始化信号集(把set所指向的信号集中的所有信号的对应bit置为1,表示该信号集包含所有的有效信号)

3.int sigaddset(sigset_t* set,int signo);

功能:添加有效信号signo(把编号为signo的信号在位图中的bit从0变为1)

4.int sigdelset(sigset_t* set,int signo);

功能:删除有效信号signor(把编号为signo的信号在位图中的bit从1变为0)

5.int sigismember(const sigset_t* set,int signo);

功能:用于判断信号集中的有效信号是否包含编号为signor的信号

6.int sigprocmask(int how,const sigset_t* set,sigset_t* oset);

功能:可以读取或更改进程的阻塞信号集(信号屏蔽字);SIGKILL和SIGSTOP不能被阻塞

7.int sigpending(sigset_t* set);

功能:读取当前进程的未决信号集,通过参数set传出

8.sighandler_t signal(int signum, sighandler_t handler);

功能:handler需要用户自定义处理信号的方式;也可以是SIG_IGN:忽略该信号;SIG_DFL:采用系统默认方式处理信号

SIGKILL和SIGSTOP不能阻塞原因

见sigprocmask内核代码

void set_current_blocked(sigset_t *newset)
{sigdelsetmask(newset, sigmask(SIGKILL) | sigmask(SIGSTOP));__set_current_blocked(newset);
}void __set_current_blocked(const sigset_t *newset)
{struct task_struct *tsk = current;/** In case the signal mask hasn't changed, there is nothing we need* to do. The current->blocked shouldn't be modified by other task.*/if (sigequalsets(&tsk->blocked, newset))return;spin_lock_irq(&tsk->sighand->siglock);__set_task_blocked(tsk, newset);spin_unlock_irq(&tsk->sighand->siglock);
}/** This is also useful for kernel threads that want to temporarily* (or permanently) block certain signals.** NOTE! Unlike the user-mode sys_sigprocmask(), the kernel* interface happily blocks "unblockable" signals like SIGKILL* and friends.*/
int sigprocmask(int how, sigset_t *set, sigset_t *oldset)
{struct task_struct *tsk = current;sigset_t newset;/* Lockless, only current can change ->blocked, never from irq */if (oldset)*oldset = tsk->blocked;switch (how) {case SIG_BLOCK:sigorsets(&newset, &tsk->blocked, set);break;case SIG_UNBLOCK:sigandnsets(&newset, &tsk->blocked, set);break;case SIG_SETMASK:newset = *set;break;default:return -EINVAL;}__set_current_blocked(&newset);return 0;
}
EXPORT_SYMBOL(sigprocmask);

执行流程

目标进程在从内核态返回用户态的过程中检测是否有挂起的信号,发现有挂起的信号则从链表中每次拿出一个信号事件进行处理直到链表为空

自定义处理函数

对于有通过 signal、sigaction 注册信号处理函数的信号,设定堆栈后跳转到用户态的信号处理函数开始执行,此函数返回后触发一个 sigreturn 系统调用后再次回到内核,然后恢复旧的堆栈继续运行

默认处理

对于 SIGKILL、SIGSTOP 这两种不可被用户程序捕获的信号,以及设定了 SIG_IGN、SIG_DFL 行为的信号而言,这些信号的处理过程均在内核态完成。

对于SIG_DFL,默认行为是dump 的信号处理可能会进程工作目录下创建一个core 文件. 这个文件列出了进程的地址空间和cpu 寄存器的值.

do_signal 创建这个文件后, 就会杀死整个线程组. 剩下18 个信号的默认处理是terminate, 这仅仅是简单地杀死整个线程组. 为此,do_signal 调用了do_group_exit

处理流程

el0_svc执行系统调用后,会通过ret_fast_syscall返回用户空间,会执行do_work_pending

ret_fast_syscall:
__ret_fast_syscall:UNWIND(.fnstart	)UNWIND(.cantunwind	)str	r0, [sp, #S_R0 + S_OFF]!	@ save returned r0
#if IS_ENABLED(CONFIG_DEBUG_RSEQ)/* do_rseq_syscall needs interrupts enabled. */mov	r0, sp				@ 'regs'bl	do_rseq_syscall
#endifdisable_irq_notrace			@ disable interruptsldr	r2, [tsk, #TI_ADDR_LIMIT]cmp	r2, #TASK_SIZEblne	addr_limit_check_failedldr	r1, [tsk, #TI_FLAGS]		@ re-check for syscall tracingtst	r1, #_TIF_SYSCALL_WORK | _TIF_WORK_MASKbeq	no_work_pendingUNWIND(.fnend		)
ENDPROC(ret_fast_syscall)/* Slower path - fall through to work_pending */
#endiftst	r1, #_TIF_SYSCALL_WORKbne	__sys_trace_return_nosave
slow_work_pending:mov	r0, sp				@ 'regs'mov	r2, why				@ 'syscall'bl	do_work_pendingcmp	r0, #0beq	no_work_pendingmovlt	scno, #(__NR_restart_syscall - __NR_SYSCALL_BASE)ldmia	sp, {r0 - r6}			@ have to reload r0 - r6b	local_restart			@ ... and off we go
ENDPROC(ret_fast_syscall)

do_work_pending调用do_signal对信号做处理

asmlinkage int
do_work_pending(struct pt_regs *regs, unsigned int thread_flags, int syscall)
{/** The assembly code enters us with IRQs off, but it hasn't* informed the tracing code of that for efficiency reasons.* Update the trace code with the current status.*/trace_hardirqs_off();do {if (likely(thread_flags & _TIF_NEED_RESCHED)) {schedule();} else {if (unlikely(!user_mode(regs)))return 0;local_irq_enable();if (thread_flags & _TIF_SIGPENDING) {int restart = do_signal(regs, syscall);if (unlikely(restart)) {/** Restart without handlers.* Deal with it without leaving* the kernel space.*/return restart;}syscall = 0;} else if (thread_flags & _TIF_UPROBE) {uprobe_notify_resume(regs);} else {clear_thread_flag(TIF_NOTIFY_RESUME);tracehook_notify_resume(regs);rseq_handle_notify_resume(NULL, regs);}}local_irq_disable();thread_flags = current_thread_info()->flags;} while (thread_flags & _TIF_WORK_MASK);return 0;
}

简化框图如下

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

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

相关文章

cesium 指定点旋转rectangle Primitive方式 矩阵篇

cesium中rectangle是水平垂直于正北方向的&#xff0c;rectangle的属性中有rotation&#xff0c;但是rotation是以矩形的中心点进行旋转的&#xff0c;旋转过程中矩形的形状可能会变形&#xff0c;如果需要以矩形的顶点为原点进行旋转&#xff0c;可以采用primitive的方式添加p…

Postman调用OpenAI接口

首先你需要科学上网。。。。。 请求方式&#xff1a;post 请求地址&#xff1a;https://api.openai.com/v1/chat/completions 请求头&#xff1a; Authorization : Bearer key Content-Type : application/json Body : { "messages": [{ "role": &quo…

全网人气排行第一的免费开源ERP:Odoo电商功能应用亮点介绍

Odoo E-Commerce是一款创新型电子商务管理系统&#xff0c;旨在帮助企业建立以客户为中心的B2B与B2C电子商务平台&#xff0c;提高电商业务敏捷性&#xff0c;保障利润&#xff0c;并确保客户体验战略与时俱进。 —— 开源智造Odoo老杨 什么是Odoo免费开源电商管理系统&#xf…

html网页在展示时,监听网络是否断网,如果断网页面暂停点击响应

序言&#xff1a; 集合百家之所长&#xff0c;方著此篇文章&#xff0c;废话少说&#xff0c;直接上代码&#xff0c;找好你的测试网页&#xff0c;进行配置&#xff0c;然后复制粘贴代码&#xff0c;就可以了。 1.css文件内容 #newbody{display: none;width: 100%;height: 9…

CSS动画(css、js动画库:各种动画效果)

第一种方法&#xff1a;文字从上到下显示动画&#xff1b; <div class"text-container"><div class"text">文字从上到下显示</div></div><style scoped> /*确保 keyframes 规则在引用它的任何选择器之前定义&#xff0c;以避…

Coze初体验

一、Coze 是什么 Coze 是新一代 AI Bot 的应用编辑开发平台&#xff0c;能够快速创建、调试和优化 Bot 的应用程序&#xff0c;并可以将其发布到各类社交平台和通讯软件上。 国内版&#xff1a;Coze 国外版&#xff1a;Coze 二、Coze 有哪些主要功能 1. 插件 插件是一个工…

为硬刚小米SU7,华为智界S7整出了「梅开二度」操作

如今国产中大型新能源轿车市场&#xff0c;在小米 SU7 加入后&#xff0c;可算彻底活了过来。 过去几年&#xff0c;咱们自主新能源品牌在 20-30 万元级轿车上发力明显不足&#xff0c;老牌车厂比亚迪汉几乎以一己之力扛起销量担当。 随着新能源汽车消费升级、竞争加剧&#x…

nginx服务访问页面白色

问题描述 访问一个域名服务返回页面空白&#xff0c;非响应404。报错如下图。 排查问题 域名解析正常&#xff0c;网络通讯正常&#xff0c;绕过解析地址访问源站IP地址端口访问正常&#xff0c;nginx无异常报错。 在打开文件时&#xff0c;发现无法打开配置文件&#xff0c…

AI交互数字人对教育领域有何优势?

AI交互数字人不仅能够跨越物理距离的限制&#xff0c;以数字人形象为学生提供“面对面”教学互动体验&#xff0c;还能根据学生的具体需求提供个性化的知识解答。如天津大学推出了数字人老师&#xff0c;以刘艳丽教授形象1&#xff1a;1仿真打造的2.5D数字人&#xff0c;能够应…

springcloud第4季 springcloud-alibaba之nacos篇-配置中心

一 nacos的配置中心 1.1 配置中心 namespace&#xff0c;group&#xff0c;dataid 之间的关系 1.2 配置样例

autodesk系列软件安装错误1603,手动安装Autodesk Desktop Licensing Service之后,启动服务提示错误1067

一般Autodesk Desktop Licensing Service这个服务没安装或者不正常会导致autodesk系列软件安装错误1603或者其他报错。 手动安装Autodesk Desktop Licensing Service之后&#xff0c;启动服务提示错误1067&#xff0c; 解决方法如下 打开autoremove点击扩展功能&#xff0c;输…

W801学习笔记十一:掌机进阶V3版本之硬件改造

经由前面的笔记&#xff0c;我们打造出了一款游戏掌机。 W801学习笔记十&#xff1a;HLK-W801制作学习机/NES游戏机(总结) 然而&#xff0c;考虑到后续的游戏开发&#xff0c;总是忧心容量不足。故而&#xff0c;在正式展开软件开发工作以前&#xff0c;最终进行一下升级改造…