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

文章目录

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

c 复制代码
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.函数定义

c 复制代码
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结构

c 复制代码
#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)

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

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

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

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

c 复制代码
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: 通用处理

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

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

二、信号状态重计算recalc_sigpending

c 复制代码
#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);
        else
                clear_tsk_thread_flag(t, TIF_SIGPENDING);
}
void recalc_sigpending(void)
{
        recalc_sigpending_tsk(current);
}

1.宏定义解析

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

2.核心判断逻辑

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

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

c 复制代码
t->signal->group_stop_count > 0

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

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

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

c 复制代码
PENDING(&t->pending, &t->blocked)

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

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

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

c 复制代码
PENDING(&t->signal->shared_pending, &t->blocked)

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

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

3.标志设置函数

设置信号挂起标志

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

清除信号挂起标志

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

三、实现进程的挂起refrigerator

c 复制代码
void refrigerator(unsigned long flag)
{
        /* Hmm, should we be allowed to suspend when there are realtime
           processes 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.保存当前状态

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

2.设置为不可中断状态

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

3.调试信息输出

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

4.清除冻结标志

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

5.信号清理

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

6.设置冻结完成标志

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

7.等待唤醒循环

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

8.恢复状态

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

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

c 复制代码
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.信号掩码测试

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

1.2.信号掩码删除

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

1.3.信号掩码生成

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

1.4.信号队列释放

c 复制代码
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: 快速检查

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

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

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

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

c 复制代码
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.循环宏
c 复制代码
list_for_each_entry_safe(q, n, &s->list, list)
  • 安全遍历:允许在遍历时删除节点
  • q:当前信号队列条目
  • n:下一个条目(用于安全删除)
2.3.2.条件判断
c 复制代码
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.删除操作
c 复制代码
list_del_init(&q->list);   // 从链表中移除
__sigqueue_free(q);        // 释放信号结构
相关推荐
天码-行空1 天前
【大数据环境安装指南】HBase集群环境搭建教程
大数据·linux·运维·hbase
天空之外1361 天前
生成一个带 IP 的自签证书、制作Http证书
linux·运维·服务器
释怀不想释怀1 天前
linux常见安装(JDK,mysql,nginx)
linux·运维·服务器
杰克崔1 天前
do_exit的hungtask问题及coredump的实验及原理分析一
linux·运维·服务器·车载系统
pengdott1 天前
Linux进程数据结构与组织方式深度解析
linux·运维·服务器
Java 码农1 天前
gitlab gitrunner springboot 多环境多分支部署 (非容器方式,使用原生linux 环境)
linux·spring boot·gitlab
LongQ30ZZ1 天前
Linux的常见指令
linux·服务器
走向IT1 天前
vdbench在Centos系统上联机测试环境搭建
linux·运维·centos
阳宗德1 天前
基于CentOS Linux release 7.1实现了Oracle Database 11g R2 企业版容器化运行
linux·数据库·docker·oracle·centos
liulilittle1 天前
libxdp: No bpffs found at /sys/fs/bpf
linux·运维·服务器·开发语言·c++