CPU内部有一个RTC,会在上电的时候调用mktime函数算出从1970年1月1日0时开始到当前开机点所过的秒数

long kernel_mktime (struct tm *tm)
{long res;int year;year = tm->tm_year - 70; // 从70 年到现在经过的年数(2 位表示方式),// 因此会有2000 年问题。/* magic offsets (y+1) needed to get leapyears right. *//* 为了获得正确的闰年数,这里需要这样一个魔幻偏值(y+1) */res = YEAR * year + DAY * ((year + 1) / 4);  // 这些年经过的秒数时间 + 每个闰年时多1 天res += month[tm->tm_mon];    // 的秒数时间,在加上当年到当月时的秒数。/* and (y+2) here. If it wasn't a leap-year, we have to adjust *//* 以及(y+2)。如果(y+2)不是闰年,那么我们就必须进行调整(减去一天的秒数时间)。 */if (tm->tm_mon > 1 && ((year + 2) % 4))res -= DAY;res += DAY * (tm->tm_mday - 1);    // 再加上本月过去的天数的秒数时间。res += HOUR * tm->tm_hour;  // 再加上当天过去的小时数的秒数时间。res += MINUTE * tm->tm_min;    // 再加上1 小时内过去的分钟数的秒数时间。res += tm->tm_sec;      // 再加上1 分钟内已过的秒数。return res;            // 即等于从1970 年以来经过的秒数时间。
}

而给mktime函数传来的时间结构体的赋值是由初始化时从RTC(CMOS

)中读出的

// 该子程序取CMOS 时钟,并设置开机时间 startup_time(为从1970-1-1-0 时起到开机时的秒数)。
static void time_init(void)
{struct tm time;do {// 参见后面CMOS 内存列表。time.tm_sec = CMOS_READ(0);time.tm_min = CMOS_READ(2);time.tm_hour = CMOS_READ(4);time.tm_mday = CMOS_READ(7);time.tm_mon = CMOS_READ(8);time.tm_year = CMOS_READ(9);} while (time.tm_sec != CMOS_READ(0));BCD_TO_BIN(time.tm_sec);BCD_TO_BIN(time.tm_min);BCD_TO_BIN(time.tm_hour);BCD_TO_BIN(time.tm_mday);BCD_TO_BIN(time.tm_mon);BCD_TO_BIN(time.tm_year);time.tm_mon--;startup_time = kernel_mktime(&time);
}

可以看一下里面还有一个do while循环,是怕没有读到当前的值。

从main.c文件里进行的初始化。可以看到通过CMOS进行赋值的。这个就和硬件有关系了,咱也不懂,就当它能知道当前的时间吧。

所以这时就可以把时间存入全局变量中,会为JIFFIES所用

JIFFIES是一个系统的时钟滴答,一个系统滴答是10ms,也是一个定时器。

10ms一个系统滴答---->每隔10ms会引发一个定时器中断

而这个中断服务函数(timer_interrupt

)中,首先进行了jiffies的自加

_timer_interrupt:push ds ;// save ds,es and put kernel data spacepush es ;// into them. %fs is used by _system_callpush fspush edx ;// we save %eax,%ecx,%edx as gcc doesn'tpush ecx ;// save those across function calls. %ebxpush ebx ;// is saved as we use that in ret_sys_callpush eaxmov eax,10h ;// ds,es 置为指向内核数据段。mov ds,axmov es,axmov eax,17h ;// fs 置为指向局部数据段(出错程序的数据段)。mov fs,axinc dword ptr _jiffies    //jiffies自加
;// 由于初始化中断控制芯片时没有采用自动EOI,所以这里需要发指令结束该硬件中断。mov al,20h ;// EOI to interrupt controller ;//1out 20h,al ;// 操作命令字OCW2 送0x20 端口。
;// 下面3 句从选择符中取出当前特权级别(0 或3)并压入堆栈,作为do_timer 的参数。mov eax,dword ptr [R_CS+esp]and eax,3 ;// %eax is CPL (0 or 3, 0=supervisor)push eax
;// do_timer(CPL)执行任务切换、计时等工作,在kernel/shched.c,305 行实现。call _do_timer ;// 'do_timer(long CPL)' does everything fromadd esp,4 ;// task switching to accounting ...jmp ret_from_sys_call

就是中断程序的正常流程,自加之后会调用一个do_timer函数

// 参数cpl 是当前特权级0 或3,0 表示内核代码在执行。
// 对于一个进程由于执行时间片用完时,则进行任务切换。并执行一个计时更新工作。
void do_timer (long cpl)
{extern int beepcount;      // 扬声器发声时间滴答数(kernel/chr_drv/console.c,697)extern void sysbeepstop (void);  // 关闭扬声器(kernel/chr_drv/console.c,691)// 如果发声计数次数到,则关闭发声。(向0x61 口发送命令,复位位0 和1。位0 控制8253// 计数器2 的工作,位1 控制扬声器)。if (beepcount)if (!--beepcount)sysbeepstop ();// 如果当前特权级(cpl)为0(最高,表示是内核程序在工作),则将超级用户运行时间stime 递增;// 如果cpl > 0,则表示是一般用户程序在工作,增加utime。if (cpl)current->utime++;elsecurrent->stime++;// 如果有用户的定时器存在,则将链表第1 个定时器的值减1。如果已等于0,则调用相应的处理
// 程序,并将该处理程序指针置为空。然后去掉该项定时器。if (next_timer){                // next_timer 是定时器链表的头指针(见270 行)。next_timer->jiffies--;while (next_timer && next_timer->jiffies <= 0){void (*fn) ();  // 这里插入了一个函数指针定义!!!??fn = next_timer->fn;next_timer->fn = NULL;next_timer = next_timer->next;(fn) ();      // 调用处理函数。}}
// 如果当前软盘控制器FDC 的数字输出寄存器中马达启动位有置位的,则执行软盘定时程序(245 行)。if (current_DOR & 0xf0)do_floppy_timer ();if ((--current->counter) > 0)return;         // 如果进程运行时间还没完,则退出。current->counter = 0;if (!cpl)return;         // 对于超级用户程序,不依赖counter 值进行调度。schedule ();
}

CPL变量就是内核中用来指示被中断程序的特权,也就是0表示内核进程,3表示用户进程。

current就是指向当前进程数据结构的指针,指向task_struct

所以utime就是用户程序的运行时间,stime是内核程序的运行时间

而这个next_timer就是一个时间链表的指针,也就是个定时器链表的指针

// 定时器链表结构和定时器数组。
static struct timer_list
{long jiffies;          // 定时滴答数。void (*fn) ();     // 定时处理程序。struct timer_list *next;  // 下一个定时器。
}
timer_list[TIME_REQUESTS], *next_timer = NULL;

通过这个数据结构再看上面的程序代码,就可以知道,其实上面那段,就是在进行定时器中断的时候,会调用这个do_timer函数,而这个函数里面就会遍历判断这个定时器链表有没有当前定时器为0的,如果为0则证明到定的时间了,就会执行这个next_timer的fn,这个定时处理的函数。

而下面的current->counter

current上面提到了是执行进程的指针,而这个counter就是我们常听到的时间片。

如果时间片--后大于0,就直接返回,就是此进程还有时间片

CPU调度的,后面学了再写吧。

了解了这个系统滴答,和这个中断函数do_timer之后,就可以搞懂很多东西了,比如说异步的操作,可能是我太笨了,一开始对异步这种东西,完全没一点头绪,知道它能干什么,但是不知道它是怎么来了,后来再我看了点redis源码后,发现redis里面有一个时间事件time event这个东西,才明白异步就是通过定时器来完成的

而学习了这个linux内核的系统滴答后,才真正明白了这个操作是怎么完成的。

JIFFIES——系统滴答相关推荐

  1. 滴答定时器的计数模式_SysTick(系统滴答定时器)

    一.SysTick概述 Systick定时器是一个24 位的倒计数定时器,计到0时,将从RELOAD 寄存器中自动重装载定时初值.只要不把它在SysTick 控制及状态寄存器中的使能位清除,就永不停息 ...

  2. STM32系统滴答定时器(systick)应用

    一:系统滴答定时器(systick) 1.systick介绍 Systick就是一个定时器而已,只是它放在了NVIC中,主要的目的是为了给操作系统提供一个硬件上的中断(号称滴答中断).滴答中断?这里来 ...

  3. stm32系统滴答定时器使用

     STM32菜鸟成长记录---系统滴答定时器(systick)应用 标签: delay任务测试reference编译器工作 2012-08-19 22:55 47395人阅读 评论(4) 收藏 举 ...

  4. stm32滴答计时器_stm32中的系统滴答定时器使用

    系统滴答定时器对于stm32的初学者来说还是非常重要的,因为随着你学习的深入编程过程中肯定会调用延时函数,比如我之前的一些gpio相关的实验中.那么延时函数的编写也是几种方法的,一般开始接触都是让系统 ...

  5. STM32F10x_StdPeriph_Lib_V3.5.0库与系统滴答定时器(Systick)

    在V3.5的库中,关于系统滴答的配置是在core_cm3.h文件中,其代码如下: static __INLINE uint32_t SysTick_Config(uint32_t ticks) { i ...

  6. STM32——系统滴答定时器

    STM32--系统滴答定时器 宗旨:技术的学习是有限的,分享的精神是无限的. 一.SysTick[内核中] [风格:先描述一下库对寄存器的封装,再举例实现某些功能] SysTick定时器被捆绑在NVI ...

  7. 定时器开始时延时了十几秒_第六章--系统滴答定时器

    第六章--系统滴答定时器 简介:系统滴答定时器是内核(这里指M4)定时器使用的是内核时钟源168MZ或可以选择外部时钟源21MZ 应用场合: 为UCOS系统提供时钟节拍 作为简单的定时器延时使用 // ...

  8. 系统滴答定时器的应用

    1 实验目的 (1) 理解滴答定时器SysTick 定时器的工作原理: (2) 学会使用中断函数. 2 实验任务 (1) 编写 SysTick 定时器初始化程序: (2) 编写 SysTick 定时器 ...

  9. SysTick系统滴答定时器

    工作原理 SysTick系统滴答定时器是一个24位递减计数器计数器,向下计数,最大计数值为() SysTick系统滴答定时器,每个时钟周期-1,减到0后申请中断,并且会自动重装初值. 注意: 定时器的 ...

最新文章

  1. [HTTP协议]入门篇
  2. 20个必会的JavaScript面试题
  3. Python代码实现飞机大战(经典)
  4. mysql 无论输入什么都是现实 not found_NotAPanda
  5. 收藏 | 小目标检测的一些问题,思路和方案
  6. Python迭代列表
  7. 小试ESP8266(一) 一只电阻, 几条语句, 摆脱深度睡眠反复重启的困扰
  8. hannoi塔(汉诺塔)移动过程解析
  9. 内事不决问张昭,外事不决问周瑜,“ 排序 ”不决问威少
  10. 如何在android中设置背景图片,在Android中设置窗口背景图
  11. MyHDL中文手册(十)——转换成Verilog和VHDL
  12. 算法笔记:找考试座位号问题
  13. 【动手学深度学习PyTorch版】6 权重衰退
  14. tcpdump+wireshark双剑合璧
  15. 使用FFmpeg 批量处理视频
  16. 计算机毕业设计论文网站综合比较
  17. 福娃Fuwa和海宝Haibao
  18. 面试腾讯软件测试经过1面2面3面,最终面试官对我竖起大拇指!
  19. 用python处理excel文件_python 读写 Excel文件
  20. vo,dto,po的区别

热门文章

  1. WLAN划分与子网划分的区别
  2. 2017.10.26试题(NOIP DAY1难度)
  3. Jquery 防止事件冒泡
  4. 二、【手机摄影】手机专业拍照模式介绍
  5. 软件测试工作中收藏网址
  6. 绝地求生亚服怎么修复服务器维修,《绝地求生》Xbox One北美服务器之前瘫痪已修复 重新开启...
  7. JavaScript的排序算法——快速排序
  8. java随机生成中文姓名
  9. Opengl画衣服1
  10. 智能手机普及游戏 国内外巨头上演GPU芯片争霸