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
含义:进程组有停止的进程
- 当进程组收到
SIGSTOP
、SIGTSTP
等停止信号时递增 - 表示有进程需要被停止或恢复
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(¤t->sighand->siglock);recalc_sigpending(); /* We sent fake signal, clean it up */spin_unlock_irq(¤t->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(¤t->sighand->siglock);
recalc_sigpending(); /* We sent fake signal, clean it up */
spin_unlock_irq(¤t->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)))
两个条件:
- 只处理标准信号:
q->info.si_signo < SIGRTMIN
- 信号在删除掩码中:
mask & sigmask(q->info.si_signo)
2.3.3.删除操作
list_del_init(&q->list); // 从链表中移除
__sigqueue_free(q); // 释放信号结构