linux进程延时,Linux延时实现

原标题:Linux延时实现

本文转载自 Linux内核那些事

链接:https://mp.weixin.qq.com/s/Y_oJamxHXeOxFOCpVybN5Q

很多时候我们需要在程序中等待一定的时间再运行,例如我们需要每隔10秒去执行某个任务,这就需要系统提供接口来等待指定的时间再运行.在Linux系统中提供了sleep(nsec)系统调用来等待nsec秒.

严格来说sleep()并不是Linux提供的系统调用,而是Glibc库实现的.在Glibc的sysdeps/posix/sleep.c文件中可以找到具体的实现:

unsigned int

DEFUN(sleep, (seconds), unsigned int seconds)

{

......

act.sa_handler = sleep_handler;

act.sa_flags = 0;

if (sigemptyset (&act.sa_mask) < 0 ||

sigaction (SIGALRM, &act, &oact)< 0)

return seconds;

before = time ((time_t *) NULL);

remaining = alarm (seconds);

......

sigsuspend (&oset);

after = time ((time_t *) NULL);

(void) sigaction (SIGALRM, &oact, (struct sigaction *) NULL);

......

}

代码去掉了很多无关重要的细节,加粗的代码是实现的主要部分,流程大概如下:

1) 调用alarm(nsec)系统调用设置一个定时器, nsec秒后内核会发送SIGALRM信号给进程.

2) 调用sigsuspend()系统调用等待信号的发生. Sigsuspend()系统调用会阻塞当前进程,直到当前进程收到信号才会被唤醒.

调用alarm()系统调用会进入到内核态,并调用sys_alarm()函数, sys_alarm()函数会调用_setitimer()函数来,而_setitimer()函数最终会调用add_timer()函数把当前进程添加到定时器队列中, add_timer()代码如下:

void add_timer(struct timer_list * timer)

{

......

p = &timer_head;

save_flags(flags);

cli();

do {

p = p->next;

} while (timer->expires > p->expires);

timer->next = p;

timer->prev = p->prev;

p->prev = timer;

timer->prev->next = timer;

restore_flags(flags);

}

从代码实现可以看到,就是把当前进程添加到timer_head队列中, timer_head就是定时器队列.

把当前进程添加到定时器队列后,内核会在什么时候发送SIGALRM信号给进程呢?答案就是:时钟中断.

时钟中断:Linux的OS时钟的物理产生原因是可编程定时/计数器产生的输出脉冲,这个脉冲送入CPU,就可以引发一个中断请求信号,我们就把它叫做时钟中断。

简单的说,时钟中断就是硬件定时产生的一个电信号,然后发送给CPU, CPU接收到这个电信号就会做一些动作:调用中断处理函数.而Linux的时钟中断处理函数是do_timer(),而do_timer()会调用mark_bh(TIMER_BH)来开启定时器的下半部处理.定时器下半部会调用timer_bh()函数来处理,然后timer_bh()函数调用run_timer_list()函数来处理定时器. run_timer_list()代码如下:

static inline void run_timer_list(void)

{

struct timer_list * timer;

cli();

while ((timer = timer_head.next)

!= &timer_head && timer->expires <= jiffies)

{

void (*fn)(unsigned long) = timer->function;

unsigned long data = timer->data;

timer->next->prev = timer->prev;

timer->prev->next = timer->next;

timer->next = timer->prev = NULL;

sti();

fn(data);

cli();

}

sti();

}

从代码可以知道, run_timer_list()函数就是遍历timer_head队列,如果其中的定时器到期了,那就调用定时器的回调函数(timer的function字段).

而对于alarm定时器的回调函数是it_real_fn(). it_real_fn()函数代码如下:

void it_real_fn(unsigned long __data)

{

struct task_struct * p = (struct task_struct *)__data;

unsigned long interval;

send_sig(SIGALRM, p, 1);

interval = p->it_real_incr;

if (interval) {

unsigned long timeout = jiffies + interval;

if (timeout < interval)

timeout = ULONG_MAX;

p->real_timer.expires = timeout;

add_timer(&p->real_timer);

}

}

上面加粗的代码就是用来发送SIGALRM信号给进程.

从上面的分析可以知道,当进程调用sleep()函数时会调用alarm()系统调用设置一个定时器,然后调用sigsuspend()等待信号的发生.当定时器到期时,内核通过send_sig()函数发送SIGALARM信号给进程,进程收到SIGALARM信号后会被唤醒.返回搜狐,查看更多

责任编辑:

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值