当前位置: 首页 > news >正文

Linux信号处理的相关数据结构和操作函数

文章目录

  • 一、是否有未阻塞的待处理信号`has_pending_signals`
    • 1.函数定义
    • 2.核心算法原理
    • 3.信号集数据结构
    • 4.逐case详细分析
      • 4.1.Case 1: 单字信号集 (_NSIG_WORDS = 1)
      • 4.2.Case 2: 双字信号集 (_NSIG_WORDS = 2)
      • 4.3.Case 4: 四字信号集(_NSIG_WORDS = 4)
      • 4.4.Default Case: 通用处理
  • 二、信号状态重计算`recalc_sigpending`
    • 1.宏定义解析
    • 2.核心判断逻辑
      • 2.1.条件1: 进程组停止计数
      • 2.2.条件2: 私有待处理信号
      • 2.3.条件3: 共享待处理信号
    • 3.标志设置函数
  • 三、实现进程的挂起refrigerator
    • 1.保存当前状态
    • 2.设置为不可中断状态
    • 3.调试信息输出
    • 4.清除冻结标志
    • 5.信号清理
    • 6.设置冻结完成标志
    • 7.等待唤醒循环
    • 8.恢复状态
  • 四、从信号队列中移除指定信号`rm_from_queue`
    • 1.辅助函数解析
      • 1.1.信号掩码测试
      • 1.2.信号掩码删除
      • 1.3.信号掩码生成
      • 1.4.信号队列释放
    • 2.主函数详细分析
      • 2.1.步骤1: 快速检查
      • 2.2.步骤2: 清除信号掩码
      • 2.3.步骤3: 遍历信号队列
        • 2.3.1.循环宏
        • 2.3.2.条件判断
        • 2.3.3.删除操作

一、是否有未阻塞的待处理信号has_pending_signals

typedef struct {unsigned long sig[_NSIG_WORDS];
} sigset_t;
#define _NSIG		64
#define _NSIG_BPW	32
#define _NSIG_WORDS	(_NSIG / _NSIG_BPW)
static inline int has_pending_signals(sigset_t *signal, sigset_t *blocked)
{unsigned long ready;long i;switch (_NSIG_WORDS) {default:for (i = _NSIG_WORDS, ready = 0; --i >= 0 ;)ready |= signal->sig[i] &~ blocked->sig[i];break;case 4: ready  = signal->sig[3] &~ blocked->sig[3];ready |= signal->sig[2] &~ blocked->sig[2];ready |= signal->sig[1] &~ blocked->sig[1];ready |= signal->sig[0] &~ blocked->sig[0];break;case 2: ready  = signal->sig[1] &~ blocked->sig[1];ready |= signal->sig[0] &~ blocked->sig[0];break;case 1: ready  = signal->sig[0] &~ blocked->sig[0];}return ready != 0;
}

1.函数定义

static inline int has_pending_signals(sigset_t *signal, sigset_t *blocked)
  • signal: 待处理信号集(pending signals)
  • blocked: 被阻塞的信号集(blocked signals)
  • 返回值: 1表示有未阻塞的待处理信号,0表示没有

2.核心算法原理

基本逻辑

  • 对于每个信号位:
  • 如果信号待处理 且 没有被阻塞 → 说明有需要处理的信号
  • ready = (signal & ~blocked)

位运算解释

  • signal: 0110 (信号2和3待处理)
  • blocked: 0010 (信号2被阻塞)
  • ~blocked: 1101 (阻塞信号的取反)
  • ready: 0100 (只有信号3需要处理)

3.信号集数据结构

sigset_t结构

#define _NSIG		64
#define _NSIG_BPW	32
#define _NSIG_WORDS	(_NSIG / _NSIG_BPW)typedef struct {unsigned long sig[_NSIG_WORDS];
} sigset_t;

4.逐case详细分析

4.1.Case 1: 单字信号集 (_NSIG_WORDS = 1)

case 1: ready = signal->sig[0] &~ blocked->sig[0];
  • 获取未阻塞的待处理信号63-0

4.2.Case 2: 双字信号集 (_NSIG_WORDS = 2)

case 2: ready  = signal->sig[1] &~ blocked->sig[1];ready |= signal->sig[0] &~ blocked->sig[0];
  • 两个字的按位或结果

4.3.Case 4: 四字信号集(_NSIG_WORDS = 4)

case 4: ready  = signal->sig[3] &~ blocked->sig[3];ready |= signal->sig[2] &~ blocked->sig[2];ready |= signal->sig[1] &~ blocked->sig[1];ready |= signal->sig[0] &~ blocked->sig[0];

适用场景:支持更多信号的系统

4.4.Default Case: 通用处理

default:for (i = _NSIG_WORDS, ready = 0; --i >= 0 ;)ready |= signal->sig[i] &~ blocked->sig[i];

适用场景:支持任意数量信号字的系统

二、信号状态重计算recalc_sigpending

#define PENDING(p,b) has_pending_signals(&(p)->signal, (b))
fastcall void recalc_sigpending_tsk(struct task_struct *t)
{if (t->signal->group_stop_count > 0 ||PENDING(&t->pending, &t->blocked) ||PENDING(&t->signal->shared_pending, &t->blocked))set_tsk_thread_flag(t, TIF_SIGPENDING);elseclear_tsk_thread_flag(t, TIF_SIGPENDING);
}
void recalc_sigpending(void)
{recalc_sigpending_tsk(current);
}

1.宏定义解析

#define PENDING(p,b) has_pending_signals(&(p)->signal, (b))
  • 简化调用:封装 has_pending_signals 函数
  • 参数
    • p: 待处理信号集结构指针
    • b: 阻塞信号集指针
  • 作用:检查指定信号集中是否有未阻塞的待处理信号

2.核心判断逻辑

函数检查三种情况,只要满足任一条件就设置 TIF_SIGPENDING 标志:

2.1.条件1: 进程组停止计数

t->signal->group_stop_count > 0

含义:进程组有停止的进程

  • 当进程组收到 SIGSTOPSIGTSTP 等停止信号时递增
  • 表示有进程需要被停止或恢复

2.2.条件2: 私有待处理信号

PENDING(&t->pending, &t->blocked)

含义:进程有未阻塞的私有待处理信号

  • t->pending: 进程特有的待处理信号
  • t->blocked: 进程阻塞的信号掩码

2.3.条件3: 共享待处理信号

PENDING(&t->signal->shared_pending, &t->blocked)

含义:进程组有未阻塞的共享待处理信号

  • t->signal->shared_pending: 进程组共享的待处理信号
  • 线程组中所有线程共享的信号

3.标志设置函数

设置信号挂起标志

set_tsk_thread_flag(t, TIF_SIGPENDING)
  • TIF_SIGPENDING: 线程信息标志,表示有信号需要处理
  • 设置后,在从内核返回用户空间时会检查并处理信号

清除信号挂起标志

clear_tsk_thread_flag(t, TIF_SIGPENDING)
  • 当没有需要处理的信号时清除标志

三、实现进程的挂起refrigerator

void refrigerator(unsigned long flag)
{/* Hmm, should we be allowed to suspend when there are realtimeprocesses around? */long save;save = current->state;current->state = TASK_UNINTERRUPTIBLE;pr_debug("%s entered refrigerator\n", current->comm);printk("=");current->flags &= ~PF_FREEZE;spin_lock_irq(&current->sighand->siglock);recalc_sigpending(); /* We sent fake signal, clean it up */spin_unlock_irq(&current->sighand->siglock);current->flags |= PF_FROZEN;while (current->flags & PF_FROZEN)schedule();pr_debug("%s left refrigerator\n", current->comm);current->state = save;
}

1.保存当前状态

long save;
save = current->state;
  • 保存进程原状态,用于恢复时还原进程状态

2.设置为不可中断状态

current->state = TASK_UNINTERRUPTIBLE;
  • TASK_UNINTERRUPTIBLE:表示进程处于不可中断状态
  • 调度时不会被信号唤醒

3.调试信息输出

pr_debug("%s entered refrigerator\n", current->comm);
printk("=");
  • 调试日志:记录进程开始挂起

4.清除冻结标志

current->flags &= ~PF_FREEZE;
  • PF_FREEZE:表示进程应该被冻结
  • 清除此标志,冻结过程已经开始

5.信号清理

spin_lock_irq(&current->sighand->siglock);
recalc_sigpending(); /* We sent fake signal, clean it up */
spin_unlock_irq(&current->sighand->siglock);
  • 获取信号锁
  • 重新计算信号状态:清理可能用于唤醒进程的伪信号
  • 释放信号锁

6.设置冻结完成标志

current->flags |= PF_FROZEN;
  • PF_FROZEN:表示进程已完全冻结
  • 通知冻结机制此进程已准备就绪

7.等待唤醒循环

while (current->flags & PF_FROZEN)schedule();
  • 循环检查:持续检查 PF_FROZEN 标志
  • 主动调度:调用 schedule() 让出CPU
  • 退出条件:当其他代码清除 PF_FROZEN 标志时退出循环

8.恢复状态

pr_debug("%s left refrigerator\n", current->comm);
current->state = save;
  • 调试日志:记录进程解冻
  • 恢复原状态:将进程状态恢复为进入前的值

四、从信号队列中移除指定信号rm_from_queue

static inline int sigtestsetmask(sigset_t *set, unsigned long mask)
{return (set->sig[0] & mask) != 0;
}
static inline void sigdelsetmask(sigset_t *set, unsigned long mask)
{set->sig[0] &= ~mask;
}
#define sigmask(sig)	(1UL << ((sig) - 1))
static inline void __sigqueue_free(struct sigqueue *q)
{if (q->flags & SIGQUEUE_PREALLOC)return;atomic_dec(&q->user->sigpending);free_uid(q->user);kmem_cache_free(sigqueue_cachep, q);
}
static int rm_from_queue(unsigned long mask, struct sigpending *s)
{struct sigqueue *q, *n;if (!sigtestsetmask(&s->signal, mask))return 0;sigdelsetmask(&s->signal, mask);list_for_each_entry_safe(q, n, &s->list, list) {if (q->info.si_signo < SIGRTMIN &&(mask & sigmask(q->info.si_signo))) {list_del_init(&q->list);__sigqueue_free(q);}}return 1;
}

1.辅助函数解析

1.1.信号掩码测试

static inline int sigtestsetmask(sigset_t *set, unsigned long mask)
{return (set->sig[0] & mask) != 0;
}
  • 功能:检查信号集中是否有指定的信号
  • 返回:有信号返回1,没有返回0

1.2.信号掩码删除

static inline void sigdelsetmask(sigset_t *set, unsigned long mask)
{set->sig[0] &= ~mask;
}
  • 功能:从信号集中清除指定的信号位
  • 操作:按位与掩码的反码

1.3.信号掩码生成

#define sigmask(sig) (1UL << ((sig) - 1))
  • 功能:将信号编号转换为位掩码
  • 计算:信号1 → 位0,信号2 → 位1,依此类推

1.4.信号队列释放

static inline void __sigqueue_free(struct sigqueue *q)
{if (q->flags & SIGQUEUE_PREALLOC)return;atomic_dec(&q->user->sigpending);free_uid(q->user);kmem_cache_free(sigqueue_cachep, q);
}
  • 预分配检查:预分配的信号结构不释放
  • 引用计数:减少用户的待处理信号计数
  • 用户释放:释放用户引用
  • 内存释放:返回信号队列缓存

2.主函数详细分析

2.1.步骤1: 快速检查

if (!sigtestsetmask(&s->signal, mask))return 0;
  • 早期返回:如果没有目标信号,立即返回0
  • 性能优化:避免不必要的队列遍历

2.2.步骤2: 清除信号掩码

sigdelsetmask(&s->signal, mask);
  • 更新信号集:从位图中移除指定的信号

2.3.步骤3: 遍历信号队列

list_for_each_entry_safe(q, n, &s->list, list) {if (q->info.si_signo < SIGRTMIN &&(mask & sigmask(q->info.si_signo))) {list_del_init(&q->list);__sigqueue_free(q);}
}
2.3.1.循环宏
list_for_each_entry_safe(q, n, &s->list, list)
  • 安全遍历:允许在遍历时删除节点
  • q:当前信号队列条目
  • n:下一个条目(用于安全删除)
2.3.2.条件判断
if (q->info.si_signo < SIGRTMIN &&(mask & sigmask(q->info.si_signo)))

两个条件

  1. 只处理标准信号q->info.si_signo < SIGRTMIN
  2. 信号在删除掩码中mask & sigmask(q->info.si_signo)
2.3.3.删除操作
list_del_init(&q->list);   // 从链表中移除
__sigqueue_free(q);        // 释放信号结构
http://www.dtcms.com/a/438313.html

相关文章:

  • 分类信息网站手机企业网站开发
  • 做杂志的网站有哪些织梦网站系统
  • 我的网站百度怎么搜索不到了文山网站建设代理
  • 小程序推广网站免费wordpress模板下载地址
  • 第66篇:AI+交通:智能驾驶、交通流优化与智慧物流
  • 苏州自学网站建设平台做外国美食的视频网站
  • 黄冈app下载推广平台优化视频
  • 学习日记20:GraphGPT
  • 做网站加班多吗蛋糕店网站建设方案
  • 从餐馆迎客看 accept4:更灵活的“接客“高手
  • Metasploit基础(MSF)
  • 浅析物理层过程
  • 总结 IP 协议的相关特性
  • 网球馆自动预约系统的反调试
  • PyQt5 QLineEdit组件详解:单行文本输入控件的完整指南
  • 网站建设的毕业报告公司名称变更流程及需材料
  • OSPF 多区域实验 概念及题目
  • 网站建设要经历哪些步骤丝芭传媒有限公司
  • 东莞微信网站建设怎样ceo是什么职位什么工作
  • model.fit(train_X, train_y)
  • 数据结构之队列:初始化、入队、出队与源码全解析
  • 国内外优秀网站网站建设江苏百拓
  • hive、spark任务报错或者异常怎么排查以及定位哪段sql
  • 南昌商城网站设计洛阳青峰网络做网站
  • 算法 - FOC闭环位置控制
  • 探索高效安全的去中心化应用——Solana区块链
  • 大模型openai服务网关,认证,限流,接口输入输出的修正,监控等功能
  • 贵州百度seo整站优化做网站收入怎样
  • AI驱动的视频生成革命:MoneyPrinterTurbo技术架构深度解析
  • 东莞p2p网站开发价钱店铺logo在线制作免费