linux的signal_pending及signal

signal_pending(3) — The event API

1. signal_pending( current )的作用

仅检查当前进程是否有信号处理(不会在这里处理信号),返回不为0表示有信号需要处理

1.1. 两个信号的概念

signal pending:指在信号被处理之前生成信号的状态,是信号生成和信号消除的中间状态
signal blocking:指阻塞正在处理的信号,这是一种信号处理方法

2. 常规使用场景

 if (signal_pending(current)) {
     ret = -ERESTARTSYS;
     return ret;
    }

signal_pending(current)检查到当前进程有信号需要处理时,退出当前的系统调用,并返回"-ERESTARTSYS";然后便会进入对应的Signal处理函数。
"-ERESTARTSYS"表示信号函数处理完毕后重新执行之前的那个系统调用(如果Signal处理函数之前有系统调用的话)。

3. 情景分析

当一个系统调用处于睡眠状态时(如:等待输入缓冲区不为空),如果此时产生信号,这个信号仅在thread_info结构中标识以下,即所谓"发信号";
"发信号"动作会唤醒系统调用,系调调用便会执行signal_pending(current)检测是否有信号,如果有信号则系统调用返回-ERESTARTSYS,在从系统调用的返回用户空间后,会根据thread_info中信号标识位调用相应的Signal处理函数,这里就是所谓的“接收信号”;
Signal信号函数处理完成后,上层库函数会根据系统调用的ERESTARTSYS返回值重启该系统调用,重启的系统调用会再次检查缓冲区:为空,说明刚才的信号不是缓冲区有数据了的信号,继续等待,重复刚才的过程;不为空,就可以直接处理数据,系统调用正常结束,最后返回用户空间的应用程序。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CKEWdp00-1607526956110)(https://www.programmersought.com/images/355/4c97b4155d2f61758a00b981fec7160b.JPEG)]
Processing process:
When the program is running in user mode -> the process enters the kernel due to a system call or interruption -> turns to the user mode to execute the signal processing function -> enters the kernel after the signal processing function is completed -> returns to the user mode to continue executing the program

First, the program is executed in user mode. On the eve of the process falling into the kernel and returning from the kernel, it will check whether the signal has not been processed, and if it has and is not blocked, it will call the corresponding signal handler to process it. First, the kernel creates a layer on the user stack, in which the return address is set to the address of the signal processing function, so that when the kernel returns to the user mode, the signal processing function will be executed. When the signal processing function is executed, it will enter the kernel again, mainly to detect whether there is a signal that has not been processed, and to restore the original program interrupt execution point, restore the kernel stack, etc., so that when returning from the kernel, it will return to the original program execution place Up

4. 检测进程p是否有待处理的信号的代码

static inline int signal_pending(struct task_struct *p) {
    return unlikely(test_tsk_thread_flag(p, TIF_SIGPENDING)); // p->thread_info->flags中TIF_SIGPENDING位是否置位
}
#define TIF_SIGPENDING          2   

static inline int test_tsk_thread_flag(struct task_struct *tsk, int flag) {
    return test_ti_thread_flag(task_thread_info(tsk), flag);
}

static inline int test_ti_thread_flag(struct thread_info *ti, int flag) {  
    return test_bit(flag, &ti->flags);
}

static inline int test_bit(int nr, const volatile void * addr) {  
    return (1UL &(((const int *) addr)[nr >> 5] >>(nr & 31) )) != 0UL; // 检测addr的第nr位是否为1(addr右起最低位为第0位)

5. Signal operation 信号操作实验

#include<stdio.h>
#include<signal.h>
#include<unistd.h>

/*
 * Add the signal SIGINT to the signal set, set the signal set to a blocked signal set, get the pending signal set, and print it out. When you enter Ctrl+C, you can find that the pending signal set has changed.
 *
 * in conclusion:
 *
 * If a signal is blocked by the process, it will not be passed to the process, but it will stay in the pending state.When the signal is blocked, The signal will be processed immediately.
 */
// Block a signal, print the pending signal set
void printsigset(sigset_t *set)
{
    int i = 0;
    for(;i<32;i++){
		if(sigismember(set,i)) //Determine whether the specified signal is in the target set
            putchar('1');
        else
            putchar('0');
    }
    puts("");
}

int main()
{
	sigset_t s,p; //Define the signal set object, and clear the initialization, sigset_t 128bytes
    sigemptyset(&s);
	sigaddset(&s,SIGINT); //Add signal to the signal set, SIGINT是 ctrl+c发出的信号
	sigprocmask(SIG_BLOCK,&s,NULL); //Set blocking signal set, SIGINT信号被阻挡后ctrl+c是无法停止程序的
    while(1) {
		sigpending(&p); //Get pending signal set
        printsigset(&p);
        sleep(1);
    }
    return 0;
}
int sigemptyset(sigset_t *set);
 //Initialize the signal set pointed to by set, and clear the corresponding bits of all signals, indicating that the signal set does not contain any valid signals
int sigfillset(sigset_t *set);
 //Initialize the signal set pointed to by set and set the corresponding bits of all signals, indicating that the effective signal of the signal machine includes all the signals supported by the system.
int sigaddset(sigset_t *set,int signo);
 //Add some kind of valid signal to the signal set.
int sigdelset(sigset_t *set,int signo);
 //Delete a valid signal in the signal set
int sigismemeber(sigset_t* set,int signo);
 //It is a Boolean function, used to determine whether a signal set contains a certain signal, if it contains a thief, it returns 1, if it does not, it returns 0, and it returns -1 on error
int sigprocmask(int how,const sigset_t*set,sigset_t *oset);
 //Read or change the signal shielding word (blocking signal set) of the process, if successful, return 0, fail, return -1
int sigpending(sigset_t *set);
 //Read the pending signal set of the current process and pass it through the set parameter. If the call is successful, it returns 0, and if it fails, it returns -1.

6. linux kernel对ERESTARTSYS等返回值的处理

If a process is blocked on a system call it is put on a waitqueue in the TASK_INTERRUPTIBLE or TASK_UNINTERRUPTIBLE state. In the former case, upon receiving a signal, the kernel puts it back in the TASK_RUNNING state, adds the process to the runqueue and adds the signal to the list of pending signals.

When the process is scheduled, it continues the execution of the system call. Since the syscall was not completed, the possible return codes are:

EINTR
ERESTARTNOHAND
ERESTART_RESTARTBLOCK
ERESTARTSYS
ERESTARTNOINTR
On the exit path from the system call, the pending signals are checked. In this case, your SIG_ALRM handler is called and immediately afterwards the kernel checks the return code of the system call. In general, the following cases may occur:

Return code is EINTR: the user mode process is resumed and the return value of read will be exactly -EINTR
Return code is ERESTARTNOINTR: the system call is reexecuted
Return code is ERESTARTSYS: system call is reexecuted if SA_RESTART flag is set, otherwise -EINTR is returned to userspace.
Return code is ERESTARTNOHAND or ERESTART_RESTARTBLOCK: usermode process is resumed and -EINTR returned.
Reexecuting a system call means that the process EIP is decremented by 2 and it is therefore forced to execute again int 0x80.

参考资料

Flaw in calling read with a timeout
Analysis of Linux signal mechanism
wiki Signal
signal(7)

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值