2017-02-26

1. 发送

以tkill为例来说明. linux-3.10.86/kernel/signal.c 假定不是发送给组的, 即__send_signal()的@group为0

do_tkill -> do_send_specific
|--find_task_by_vpid
|--check_kill_permission
|--do_send_sig_info -> send_signal
|   |--__send_signal


__send_signal
|--q=__sigqueue_alloc //new sigqueue instance
|--list_add_tail(&q->list, &pending->list);
|--设置q->info的各域
|   |--q->info.si_signo =
|   |--q->info.si_pid =
|--complete_signal
|   |--signal_wake_up -> signal_wake_up_state
|   |   |--set_tsk_thread_flag(t, TIF_SIGPENDING);
|   |   |--wake_up_state(, |TASK_INTERRUPTIBLE) -> try_to_wake_up

发送信号是将信号的信息放入进程的某链表中, 并在thread info中置位TIF_SIGPENDING.

我们仅唤醒处于TASK_INTERRUPTIBLE状态的进程, 不去唤醒TASK_UNINTERRUPTIBLE状态的进程.

2. 检测

[Professional Linux Kernel Architecture] p388
Signal queue processing is initiated by the kernel each time a switch is made from kernel mode to user mode.

各体系实现上不一样, 但最后都是调用到 do_signal.

linux-3.10.86/arch/arm/kernel/signal.c

do_work_pending 
{
    if (thread_flags & _TIF_SIGPENDING) {
        do_signal
    }
}

问题:do_work_pending是否会被 __irq_usr 调用?
答: 会.
linux-3.10.86/arch/arm/kernel/entry-armv.S

__irq_usr:
...
b   ret_to_user_from_irq

ENTRY(ret_to_user_from_irq)
...
bne work_pending

work_pending:
    ...
    bl  do_work_pending  @定义在signal.c
    ...
    movlt   scno, #(__NR_restart_syscall - __NR_SYSCALL_BASE)
    ldmia   sp, {r0 - r6}           @ have to reload r0 - r6
    b   local_restart

进程运行时, 被硬件中断, 中断执行后, 从内核返回到用户态, 会检查是否有信号需要处理. 这样, 进程在用户态的代码就被信号handler打断了. 所以, [Understanding the Linux Kernel, 3rd Edition]中有 this means that the current process must first execute the signal handler in User Mode before being allowed to resume its “normal” execution. 这里的its “normal” execution 就是没有信号时, 程序所在执行的流程.

2.1 内核线程对信号的检测

由于是内核线程, 所以, 并不存在返回用户态的事情, 所以, 不会理会kill发送的信号. 如果内核线程需要响应信号,可以添加代码:

if (signal_pending(current))
{
    // 自定义信号处理函数
}
flush_signals(current);

3. 执行

信号的handler是在用户态执行的, 需要manipulates the user mode stack of the process 的那些麻烦事.
这部分内容本文未完成, 仅写了一部分, 以下内容就是了.

sandbox (以下内容待整理)

3.1 汇编

linux-3.10.86/arch/arm/kernel/entry-common.S

work_pending:
    mov r0, sp              @ 'regs'
    mov r2, why             @ 'syscall'
    @r1是? 答:see ENTRY(ret_to_user_from_irq)
    @r1是struct thread_info的域unsigned long     flags
    bl  do_work_pending  @定义在signal.c

linux-3.10.86/arch/arm/kernel/entry-header.S

tbl .req    r8      @ syscall table pointer
/*
如果是系统调用, 则r8非0, 是这个意思吗?
因为如果是系统调用, 则会给出syscall table pointer (就是上面的tbl  .req    r8), 
所以, 两者是共用r8的.
*/
/*
问题:在那个地方给r8赋值的? 
答:linux-3.10.86/arch/arm/kernel/entry-common.S
    ENTRY(vector_swi)
    ...
    adr tbl, sys_call_table     @ load syscall table pointer
*/
why .req    r8      @ Linux syscall (!= 0)

linux-3.10.86/arch/arm/kernel/signal.c

asmlinkage int
do_work_pending(struct pt_regs *regs, unsigned int thread_flags, int syscall)

3个参数跟上面的注释吻合. 这里@syscall表示是否是因为系统调用而陷入内核的.

3.2 stack

do_work_pending的相关参数传递给了 do_signal:

static int do_signal(struct pt_regs *regs, int syscall)
{
    ...
    if (get_signal(&ksig)) { //填充ksig
        handle_signal(&ksig, regs);
    }
    ...
}
  1. 修改pt_regs的pc, 这样后续让信号handler运行, 而不是 the normal program code. 在此之前, 要备份pt_regs.

setup_rt_framesetup_frame的区别??? 先不管, 看setup_frame.

handle_signal
|--setup_frame
|   |--struct sigframe __user *frame = get_sigframe(ksig, regs, sizeof(*frame));
|   |   |--可简化理解为 return regs->ARM_sp
|   |--setup_sigframe(struct sigframe __user *sf, struct pt_regs *regs, sigset_t *set)
|   |   |--//以下 备份pt_regs到 __user的某处
|   |   |--__put_user_error(regs->ARM_r0, &sf->uc.uc_mcontext.arm_r0, ...);
|   |   |--__put_user_error(regs->ARM_r1, &sf->uc.uc_mcontext.arm_r1, err);
|   |   |--....
|   |   |--__put_user_error(regs->ARM_pc, &sf->uc.uc_mcontext.arm_pc, err);
|   |   |--__put_user_error(regs->ARM_cpsr, &sf->uc.uc_mcontext.arm_cpsr, err);
|   |--setup_return
|   |   |--unsigned long handler = (unsigned long)ksig->ka.sa.sa_handler;
|   |   |--regs->ARM_sp = (unsigned long)frame;
|   |   |--regs->ARM_pc = handler;

算了, 先不看了.

本文地址: https://awakening-fong.github.io/posts/other/signal

转载请注明出处: https://awakening-fong.github.io


若无法评论, 请打开JavaScript, 并通过proxy.


blog comments powered by Disqus