📄 2.html
字号:
cli();<br> }<br> ++timer_jiffies;<br> tv1.index = (tv1.index + 1) & TVR_MASK;<br>}<br>sti();<br>}<br>对run_timer_list函数的说明如下:<br>关中。<br>判断jiffies是否大等于timer_jiffies,若不是,goto 8。<br>判断tv1.index是否为0(即此时系统已经扫描过整个tv1的256个timer_list链表,又回到的第一个链表处,此时需重整TVECS结构),若是,置n为1;若不是,goto 6。<br>调用cascade_timers()函数把TVECS[n]中由其index指定的那条链表上的timer放到TVECS[n-1]中来。注意:调用cascade_timers()函数后,index已经加1。<br>判断TVECS[n]->index是否为1,即原来为0。如果是(表明TVECS[n]上所有都已经扫描一遍,此时需对其后一级的TVECS[++n]调用cascade_timers()进行重整),把n加1,goto 4。<br>执行tv1.vec上由tv1->index指定的那条链表上的所有timer的服务函数,并把该timer从链表中移走。在执行服务函数的过程中,允许中断。<br>timer_jiffies加1,tv1->index加1,若tv1->index等于256,则重新置为0,goto 2。<br>开中,返回。<p>Linux提供了两种定时器服务。一种早期的由timer_struct等结构描述,由run_old_times函数处理。另一种“新”的服务由timer_list等结构描述,由add_timer、del_timer、cascade_time和run_timer_list等函数处理。<br>早期的定时器服务利用如下数据结构:<br>struct timer_struct {<br> unsigned long expires; /*本定时器被唤醒的时刻 */<br> void (*fn)(void); /* 定时器唤醒后的处理函数 */<br>}<br>struct timer_struct timer_table[32]; /*最多可同时启用32个定时器 */<br>unsigned long timer_active; /* 每位对应一定时器,置1表示启用 */<br>新的定时器服务依靠链表结构突破了32个的限制,利用如下的数据结构:<br>struct timer_list {<br> struct timer_list *next;<br> struct timer_list *prev;<br> unsigned long expires;<br> unsigned long data; /* 用来存放当前进程的PCB块的指针,可作为参数传<br> void (*function)(unsigned long); 给function */<br>}<p><br>表示上述数据结构的图示如下:<p><br> 在这里,顺便简单介绍一下旧的timer机制的运作情况。<br> 系统在每次调用函数do_bottom_half时,都会调用一次函数run_old_timers()。<br>函数run_old_timers()<br>该函数处理的很简单,只不过依次扫描timer_table中的32个定时器,若扫描到的定时器已经到期,并且已经被激活,则执行该timer的服务函数。<p>间隔定时器itimer<br>系统为每个进程提供了三个间隔定时器。当其中任意一个定时器到期时,就会发出一个信号给进程,同时,定时器重新开始运作。三种定时器描述如下:<br>ITIMER_REAL 真实时钟,到期时送出SIGALRM信号。<br>ITIMER_VIRTUAL 仅在进程运行时的计时,到期时送出SIGVTALRM信号。<br>ITIMER_PROF 不仅在进程运行时计时,在系统为进程运作而运行时它也计时,与ITIMER_VIRTUAL对比,该定时器通常为那些在用户态和核心态空间运行的应用所花去的时间计时,到期时送出SIGPROF信号。<br>与itimer有关的数据结构定义如下:<br>struct timespec {<br> long tv_sec; /* seconds */<br> long tv_nsec; /* nanoseconds */<br>};<br>struct timeval {<br> int tv_sec; /* seconds */<br> int tv_usec; /* microseconds */<br>};<br>struct itimerspec {<br> struct timespec it_interval; /* timer period */<br> struct timespec it_value; /* timer expiration */<br>};<br>struct itimerval {<br> struct timeval it_interval; /* timer interval */<br> struct timeval it_value; /* current value */<br>};<p>这三种定时器在task_struct中定义:<br>struct task_struct {<br> ……<br> unsigned long timeout;<br> unsigned long it_real_value,it_prof_value,it_virt_value;<br> unsigned long it_real_incr,it_prof_incr,it_virt_incr;<br> struct timer_list real_timer;<br> ……<br>}<br>在进程创建时,系统把it_real_fn函数的入口地址赋给real_timer.function。(见sched.h)<br>我们小组分析了三个系统调用:sys_getitimer,sys_setitimer,sys_alarm。<br>在这三个系统调用中,需用到以下一些函数:<br>函数static int _getitimer(int which, struct itimerval *value)<br>该函数的运行过程大致如下:<br>根据传进的参数which按三种itimer分别处理:<br>若是ITIMER_REAL,则设置interval为current进程的it_real_incr,val设置为0;判断current进程的real_timer有否设置并挂入TVECS结构中,若有,设置val为current进程real_timer的expires,并把real_timer重新挂到TVECS结构中,接着把val与当前jiffies作比较,若小等于当前jiffies,则说明该real_timer已经到期,于是重新设置val为当前jiffies的值加1。最后把val减去当前jiffies的值,goto 2。<br>若是ITIMER_VIRTUAL,则分别设置interval,val的值为current进程的it_virt_incr、it_virt_value,goto 2。<br>若是ITIMER_PROF,则分别设置interval,val的值为current进程的it_prof_incr、it_prof_value,goto 2。<br> (2)调用函数jiffiestotv把val,interval的jiffies值转换为timeval,返回0。<br>函数 int _setitimer(int which, struct itimerval *value, struct itimerval *ovalue)<br>该函数的运行过程大致如下:<br>调用函数tvtojiffies把value中的interval和value转换为jiffies i 和 j。<br>判断指针ovalue是否为空,若空,goto ;若不空,则把由which指定类型的itimer存入ovalue中,若存放不成功,goto 4;<br>根据which指定的itimer按三种类型分别处理:<br>若是ITIMER_REAL,则从TVECS结构中取出current进程的real_timer,并重新设置current进程的it_real_value和it_real_incr为j和i。若j等于0,goto 4;若不等于0,则把当前jiffies的值加上定时器剩余时间j,得到触发时间。若i小于j,则表明I已经溢出,应该重新设为ULONG_MAX。最后把current进程的real_timer的expires设为i,把设置过的real_timer重新加入TVECS结构,goto 4。<br>若是ITIMER_VIRTUAL,则设置current进程的it-_virt_value和it_virt_incr为j和i。<br>若是ITIMER_PROF,则设置current进程的it-_prof_value和it_prof_incr为j和i。<br> (4)返回0。<p>函数verify_area(int type, const void *addr, unsigned long size)<br>该函数的主要功能是对以addr为始址的,长度为size的一块存储区是否有type类型的操作权利。<p>函数memcpy_tofs(to, from, n)<br>该函数的主要功能是从以from为始址的存储区中取出长度为n的一块数据放入以to为始址的存储区。<p>函数memcpy_fromfs(from, to, n)<br>该函数的主要功能是从以from为始址的存储区中取出长度为n的一块数据放入以to为始址的存储区。<p>函数memset((char*)&set_buffer, 0, sizeof(set_buffer))<br>该函数的主要功能是把set_buffer中的内容置为0,在这里,即把it_value和it_interval置为0。<p>现在,我简单介绍一下这三个系统调用:<br>系统调用sys_getitimer(int which, struct itimerval *value)<p>首先,若value为NULL,则返回-EFAULT,说明这是一个bad address。<br>其次,把which类型的itimer取出放入get_buffer。<br>再次,若存放成功,再确认对value的写权利。<br>最后,则把get_buffer中的itimer取出,拷入value。<p>系统调用sys_setitimer(int which, struct itimerval *value,struct itimerval *ovalue)<p>首先,判断value是否为NULL,若不是,则确认对value是否有读的权利,并把set_buffer中的数据拷入value;若value为NULL,则把set_buffer中的内容置为0,即把it_value和it_interval置为0。<br>其次,判断ovalue是否为NULL,若不是,则确认对ovalue是否有写的权利。<br>再次,调用函数_setitimer设置由which指定类型的itimer。<br>最后,调用函数memcpy_tofs把get_buffer中的数据拷入ovalue,返回。<p>系统调用sys_alarm(unsigned int seconds)<p>该系统调用重新设置进程的real_itimer,若seconds为0,则把原先的alarm定时器删掉。并且设interval为0,故只触发一次,并把旧的real_timer存入oldalarm,并返回oldalarm。<p><p><p><center><A HREF="#Content">[目录]</A></center><hr><br><A NAME="I366" ID="I366"></A><center><b><font size=+2>from aka</font></b></center><br><center><A HREF="#Content">[目录]</A></center><hr><br><A NAME="I367" ID="I367"></A><center><b><font size=+2>硬件中断</font></b></center><br>硬件中断<p>硬件中断概述<p>中断可以用下面的流程来表示:<p>中断产生源 --> 中断向量表 (idt) --> 中断入口 ( 一般简单处理后调用相应的函数) --->do_IRQ--> 后续处理(软中断等工作)<p>具体地说,处理过程如下:<p>中断信号由外部设备发送到中断芯片(模块)的引脚<p>中断芯片将引脚的信号转换成数字信号传给CPU,例如8259主芯片引脚0发送的是0x20<p>CPU接收中断后,到中断向量表IDT中找中断向量<p>根据存在中断向量中的数值找到向量入口<p>由向量入口跳转到一个统一的处理函数do_IRQ<p>在do_IRQ中可能会标注一些软中断,在执行完do_IRQ后执行这些软中断。<p>下面一一介绍。<p>8259芯片<p>本文主要参考周明德《微型计算机系统原理及应用》和billpan的相关帖子<p>1.中断产生过程<p>(1)如果IR引脚上有信号,会使中断请求寄存器(Interrupt Request Register,IRR)相应的位置位,比如图中, IR3, IR4, IR5上有信号,那么IRR的3,4,5为1<p>(2)如果这些IRR中有一个是允许的,也就是没有被屏蔽,那么就会通过INT向CPU发出中断请求信号。屏蔽是由中断屏蔽寄存器(Interrupt Mask Register,IMR)来控制的,比如图中位3被置1,也就是IRR位3的信号被屏蔽了。在图中,还有4,5的信号没
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -