linux内核定时中断最小时间,Linux内核中断及定时器实现_空间一号的博客-程序员宅基地

技术标签: linux内核定时中断最小时间  

内核中断及定时器实现分析

定时器是Linux提供的一种定时服务的机制。它在某个特定的时间唤醒某个进程,来做一些工作。Linux初始化时,init_IRQ()函数设定8253的定时周期为10ms(一个tick值)。同样,在初始化时,time_init()用setup_irq()设置时间中断向量irq0,中断服务程序为timer_interrupt。

在2.4版内核及较早的版本当中,定时器的中断处理采用底半机制,底半处理函数的注册在start_kernel()函数中调用sechd_init(),在这个函数中又调用init_bh(TIMER_BH, timer_bh)注册了定时器的底半处理函数。然后系统才调用time_init()来注册定时器的中断向量和中断处理函数。

在中断处理函数timer_interrupt()中,主要是调用do_timer()函数完成工作。do_timer()函数的主要功能就是调用mark_bh()产生软中断,随后处理器会在合适的时候调用定时器底半处理函数timer_bh()。在timer_bh()中,实现了更新定时器的功能。2.4.23版的do_timer()函数代码如下(经过简略):

void do_timer(struct pt_regs *regs)

{

(*(unsigned long *)&jiffies)++;

update_process_times(user_mode(regs));

mark_bh(TIMER_BH);

}

而在内核2.6版本以后,定时器中断处理采用了软中断机制而不是底半机制。时钟中断处理函数仍然为timer_interrup()-> do_timer_interrupt()-> do_timer_interrupt_hook()-> do_timer()。不过do_timer()函数的实现有所不同:

void do_timer(struct pt_regs *regs)

{

jiffies_64++;

update_process_times(user_mode(regs));

update_times();

}

两者所调用的函数基本相同,但是2.4.23版内核与2.6.6内核中,对于update_process_times()函数的实现不同:

void update_process_times(int user_tick)

{

struct task_struct *p = current;

int cpu = smp_processor_id(), system = user_tick ^ 1;

update_one_process(p, user_tick, system, cpu);

run_local_timers();

scheduler_tick(user_tick, system);

}

update_process_times()调用run_local_timers()引起TIMER_SOFTIRQ定时器软中断,处理器在随后的合适的时机运行软中断处理函数run_timer_softirq,这个函数是在init_timers()函数中注册的:

void __init init_timers(void)

{

timer_cpu_notify(&timers_nb, (unsigned long)CPU_UP_PREPARE,

(void *)(long)smp_processor_id());

register_cpu_notifier(&timers_nb);

open_softirq(TIMER_SOFTIRQ, run_timer_softirq, NULL);

}

事实上软中断处理函数run_timer_softirq()并没有做什么工作,主要的任务还是通过调用__run_timers()函数完成的,这个函数相当于2.4.23内核当中的run_timer_list()函数的功能。

static inline void __run_timers(tvec_base_t *base)

{

struct timer_list *timer;

spin_lock_irq(&base->lock);

/*这里进入定时器处理循环,利用系统全局jiffies与定时器基准jiffies进行对比,如果前者大,则表明有某些定时器需要进行处理了,否则表示所有的定时器都没有超时*/

while (time_after_eq(jiffies, base->timer_jiffies)) {

struct list_head work_list = LIST_HEAD_INIT(work_list);

struct list_head *head = &work_list;

int index = base->timer_jiffies & TVR_MASK;

/*

在时间列表数据结构当中查找是否存在需要进行超时处理的定时器,时间列表的数据结构定义如下:

typedef struct tvec_s {

struct list_head vec[TVN_SIZE];

} tvec_t;

typedef struct tvec_root_s {

struct list_head vec[TVR_SIZE];

} tvec_root_t;

struct tvec_t_base_s {

spinlock_t lock;

unsigned long timer_jiffies;

struct timer_list *running_timer;

tvec_root_t tv1;

tvec_t tv2;

tvec_t tv3;

tvec_t tv4;

tvec_t tv5;

} ____cacheline_aligned_in_smp;

*/

if (!index &&

(!cascade(base, &base->tv2, INDEX(0))) &&

(!cascade(base, &base->tv3, INDEX(1))) &&

!cascade(base, &base->tv4, INDEX(2)))

cascade(base, &base->tv5, INDEX(3));

++base->timer_jiffies;

list_splice_init(base->tv1.vec + index, &work_list);

repeat:

/*如果当前找到的时间数组对应的列表不为空,则表明该列表上串连的所有定时器都已经超时,循环调用每个定时器的处理函数,并将其从列表中删除,直到列表为空为止。*/

if (!list_empty(head)) {

void (*fn)(unsigned long);

unsigned long data;

timer = list_entry(head->next,struct timer_list,entry);

fn = timer->function;

data = timer->data;

list_del(&timer->entry);

set_running_timer(base, timer);

smp_wmb();

timer->base = NULL;

spin_unlock_irq(&base->lock);

fn(data);

spin_lock_irq(&base->lock);

goto repeat;

}

}

set_running_timer(base, NULL);

spin_unlock_irq(&base->lock);

}

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_36329368/article/details/116610173

智能推荐

java动态规划算法_南风知易✓✓✓的博客-程序员宅基地

java 动态规划算法递归算法的时间复杂度=递归的次数递归函数本身的时间复杂度*

典型电路的收集与记录__Jason^_^的博客-程序员宅基地

5V to 3V3通过AMS1117-3.3,将电压降至3V3,钽电容是必须的。蜂鸣器电路通过ss8050的NPN三极管实现对蜂鸣器的控制LED电路通过限流电阻对LED的电流和亮度进行控制。但每种颜色所配合的电阻还需实验,达到LED能亮同时又不晃眼睛的效果。...

Spinner的两种调用数据放式_faith_boys的博客-程序员宅基地

ArrayAdapter adapter = new ArrayAdapter(this, android.R.layout.simple_spinner_item, mLmtdReptStr);adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);mSpinner.setAd

ROS arbotix踩坑:‘rospkg‘、except UnicodeDecodeError, exc、dynamic module does not define init function_#君君#的博客-程序员宅基地_ros 安装rospkg

在执行roslaunch mbot_description arbotix_mbot_with_camera_xacro.launch文件时,即包含arbotix模块的程序时,报错No module named ‘rospkg’ 。报错No module named 'rospkg'经过对大家文章的查看,发现基本集中在把环境配置为python3.或者安装rospkg。但是仍没有解决。查看opt/ros/melodic/lib/python2.7/dist-packages,发现根本没有ro.

面试题:实现数组扁平化_weixin_34056162的博客-程序员宅基地

什么是数组扁平化数组扁平化是指将一个多维数组变为一维数组reduce 方法实现reduce 本身就是一个迭代循环器,通常用于累加,所以根据这一特点有以下:const arr1 = [1,[4,6],[8,3,[19,38]]]function flatten(arr) { return arr.reduce((result, item)=> { return re...

JfreeChart用法总结_shiyan0634的博客-程序员宅基地

一、简介 WW 的发展使得基于因特网的应用程序不再局限于静态或者简单的动态内容提供。传统的一些以软件包形式发布应用程序例如报表系统等都在逐渐搬到因特网上。但是这两者之间有着天壤之别,虽然对于数据获取、业务处理等方面基本类似,但是最大的差别在于用户界面。为了能在web浏览器上显示要求用户界面使用 HTML以及图片的方式来展现数据,而传统的一些利用操作系统本身的控件来开发的用户界面无法适应琳琅满目

随便推点

聊聊身份认证的那些事(开篇)_罗斯839的博客-程序员宅基地_cas流程

开篇身份认证,对于一个安全的应用来说,是第一道门槛,它为后续所有的安全措施提供了“身份”这样一个关键信息。通常每个公司会有好几个外部应用,如果每一个应用使用独立的账号体系,管理起来会非常复杂,用户也需要保存好几个账号密码,严重影响使用体验。而且公司内部的各个管理或开发系统,比如各种服务器、confluence、jira、gitlab等也都使用单独的账号,一个开发人员要记住各种纷繁杂乱的账号和密码,如果不提前保存下来,根本不可能能靠记忆完成输入。这时,如何设计一个简单易用的身份认证体系就显得尤为重要

Vscode的使用小技巧_weixin_30399821的博客-程序员宅基地

命令行启动code如果你的系统是Linux系统(我使用的是Ubuntu 16.04)这样就可以直接使用 code + filename来编辑文件(就像vi + filename)如果你的系统是MacOS 就需要在vscode里面按 command + shift + p 之后输入 shell 基本上在第一提示里面就会显示安装code,如图所示转载于:https://www.cnblo...

SpringMVC -> 转发(forward)和重定向(redirect)_欧皇小德子的博客-程序员宅基地_springmvc转发的关键字是redirect

默认写法就是转发:return的字符串将对应的前端页面转发到要求的url上配置了视图解析器的viewsresolver @RequestMapping("/hello/{a}/{b}") public String hello(@PathVariable int a, @PathVariable int b, Model model) { model.addAttribute("haha", "结果:" + (a + b)); return "hello"; }没有视图解析器

ros kinetic 自带opencv3 与 opencv 2 的兼容问题_原野寻踪的博客-程序员宅基地

问题分析自己写的系统必须基于opencv 2.x,而ros kinetic自带了opencv3的版本。于是在编译时报错:/usr/bin/ld: CMakeFiles/xxxx.cpp.o: undefined reference to symbol '_ZN2cv6String10deallocateEv'/opt/ros/kinetic/lib/x86_64-linux-gnu/l...

Latex002 | 详细教程:LaTeX 编译器哪个好?——如何在 Visual Studio Code 中全流程编写 LaTeX(上篇)_爱学习的Allan的博客-程序员宅基地_latex编译器

你是否在编写 LaTeX 过程中遇到了编译器“不给力”,无法自动补全、缩进等问题?本文比较了流行的 LaTeX 的编译器,并简要分析了其优势与不足,最终给出解决方案。