搜档网
当前位置:搜档网 › linux内核定时器详解及实例

linux内核定时器详解及实例

linux内核定时器详解及实例
linux内核定时器详解及实例

Linux内核定时器详解

80X86体系结构上,常用的定时器电路

实时时钟(RTC)

RTC内核通过IRQ8上发出周期性的中断,频率在2-8192HZ之间,掉电后依然工作,内核通过访问0x70和0x71 I/O端口访问RTC。

时间戳计时器(TSC)

利用CLK输入引线,接收外部振荡器的时钟信号,该计算器是利用64位的时间戳计时器寄存器来实现额,与可编程间隔定时器传递来的时间测量相比,更为精确。

可编程间隔定时器(PIT)

PIT的作用类似于微波炉的闹钟,PIT永远以内核确定的固定频率发出中断,但频率不算高。

CPU本地定时器

利用PIC或者APIC总线的时钟计算。

高精度时间定时器(HPET)

功能比较强大,家机很少用,也不去记了。

ACPI电源管理定时器

它的时钟信号拥有大约为3.58MHZ的固定频率,该设备实际上是一个简单的计数器,为了读取计算器的值,内核需要访问某个I/O端口,需要初始化

定时器的数据结构

利用timer_opts描述定时器

Timer_opts的数据结构

Name :标志定时器员的一个字符串

Mark_offset :记录上一个节拍开始所经过的时间,由时钟中断处理程序调用

Get_offset 返回自上一个节拍开始所经过的时间

Monotonic_clock :返回自内核初始化开始所经过的纳秒数

Delay:等待制定数目的“循环”

定时插补

就好像我们要为1小时35分34秒进行定时,我们不可能用秒表去统计,肯定先使用计算时的表,再用计算分的,最后才用秒表,在80x86架构的定时器也会使用各种定时器去进行定时插补,我们可以通过cur_timer指针来实现。

单处理器系统上的计时体系结构

所有与定时有关的活动都是由IRQ线0上的可编程间隔定时器的中断触发。

初始化阶段

1. 初始化间,time_init()函数被调用来建立计时体系结构

2. 初始化xtime变量(xtime变量存放当前时间和日期,它是一个timespec 类型的数据结构)

3. 初始化wall_to_monotonic变量,它跟xtime是同一类型的,但它存放将加在xtime上的描述和纳秒数,这样即使突发改变xtime也不会受到影响。

4. 看是否支持高精度计时器HPET

5. 调用select_timer()挑选系统中可利用的最好的定时资源,并让

cur_timer变量指向该定时器

6. 调用setup_irq(0,&irq0)来创建与IRQ相应的中断门。

时钟中断处理程序

1. 在xtime_lock顺序锁产生一个write_seqlock()来保护与定时相关的内核变量,这样防止中断让该进程被阻止。

2. 执行cur_timer定时器对象的mark_offset方法(记录上一个节拍开始所经过的时间,由时钟中断处理程序调用)

3. 调用do_timer_interrupt函数,步骤为

a) 使jiffies_64值增1

b) 调用updata_times()函数来更新系统日期和时间。

c) 调用updata_process_times()函数为本地CPU执行几个与定时相关的计数器作用。

d) 调用profile_tick()函数

e) 如果利用外部时钟来同步系统时钟,则每隔660秒,调用一次

st_rtc_mmss()函数来调整实时时钟。

f) 调用write_sequnlokc()释放xtime_lock顺序锁。

4. 返回值1,报告中断已经有效地处理了。

这个还算简单,接下来是多处理器系统上的计时体系设计。

多处理器系统上的计时体系

初始化阶段

通过calibrate_APIC_clock()计算本地APIC多久才产生一次中断。

全局时钟中断处理程序

SMP版本的timer_interrupt()处理程序与UP版本的处理程序在几个地方有差异。

Timer_interrupt()调用函数do_timer_interrupt()向I/O APIC芯片的一个端口写入,以应答定时器的中断要求。

Updata_process_times()函数不被调用,因为该函数执行与特定CPU相关的操作

Profile_tick()不被调用,因为该函数同样执行与特定CPU相关的操作。

动态定时器

这部分应用很容易,但要理解动态定时器的机理,真的囧,就说说用的部分吧。

动态定时器存放在timer_list结构中

Struct time_list{

Struct list_head entry;

Spinlock_t lock;

Unsigned long magic;

Void (*function)(unsigned long);

Unsigned long data;

Tvec_base_t *base

};

Entry字段用于将软定时器插入双向循环链表队列中,其值该链表根据定时器expires字段的值将他们分组放开(如果对动态定时器实现原理没兴趣的,可以无视,不需要要设置的项目)

Expries字段给出定时器到期时间,时间用拍子数表示,一般都是 unsigned long expire=timeout+jiffies

Lock自旋锁

Function 定时产生中断后,执行得函数

Data,可以定义一个单独的通用函数来处理多个设备驱动程序超时的问题

关于间隔定时器

所谓“间隔定时器(Interval Timer,简称itimer)就是指定时器采用“间隔”值(interval)来作

为计时方式,当定时器启动后,间隔值interval将不断减小。当interval值减到0时,我们就说该间隔定时器到期。与上一节所说的内核动态定时器相比,二者最大的区别在于定时器的计时方式不同。内核定时器是通过它的到期时刻expires值来计时的,当全局变量jiffies值大于或等于内核动态定时器的expires值时,我们说内核内核定时器到期。而间隔定时器则实际上是通过一个不断减小的计数器来计时的。虽然这两种定时器并不相同,但却也是相互联系的。假如我们每个时钟节拍都使间隔定时器的间隔计数器减1,那么在这种情形下间隔定时器实际上就是内核动态定时器(下面我们会看到进程的真实间隔定时器就是这样通过内核定时器来实现的)。

间隔定时器主要被应用在用户进程上。每个Linux进程都有三个相互关联的间隔定时器。其各自的间隔计数器都定义在进程的task_struct结构中,如下所示(include/linux/sched.h):struct task_struct{

……

unsigned long it_real_value, it_prof_value, it_virt_value;

unsigned long it_real_incr, it_prof_incr, it_virt_incr;

struct timer_list real_timer;

……

}

(1)真实间隔定时器(ITIMER_REAL):这种间隔定时器在启动后,不管进程是否运行,每个时钟滴答都将其间隔计数器减1。当减到0值时,内核向进程发送SIGALRM信号。结构类型t ask_struct中的成员it_real_incr则表示真实间隔定时器的间隔计数器的初始值,而成员it_rea l_value则表示真实间隔定时器的间隔计数器的当前值。由于这种间隔定时器本质上与上一节的内核定时器时一样的,因此Linux实际上是通过real_timer这个内嵌在task_struct结构中的内核动态定时器来实现真实间隔定时器ITIMER_REAL的。

2)虚拟间隔定时器ITIMER_VIRT:也称为进程的用户态间隔定时器。结构类型task_struc t中成员it_virt_incr和it_virt_value分别表示虚拟间隔定时器的间隔计数器的初始值和当前值,二者均以时钟滴答次数位计数单位。当虚拟间隔定时器启动后,只有当进程在用户态下运行时,一次时钟滴答才能使间隔计数器当前值it_virt_value减1。当减到0值时,内核向进程发送SIGVT ALRM信号(虚拟闹钟信号),并将it_virt_value重置为初值it_virt_incr。具体请见7.4.3节中的do_it_virt()函数的实现。

(3)PROF间隔定时器ITIMER_PROF:进程的task_struct结构中的it_prof_value和it _prof_incr成员分别表示PROF间隔定时器的间隔计数器的当前值和初始值(均以时钟滴答为单位)。当一个进程的PROF间隔定时器启动后,则只要该进程处于运行中,而不管是在用户态或核心态下执行,每个时钟滴答都使间隔计数器it_prof_value值减1。当减到0值时,内核向进程发

送SIGPROF信号,并将it_prof_value重置为初值it_prof_incr。具体请见7.4.3节的do_it_ prof()函数。

Linux在include/linux/time.h头文件中为上述三种进程间隔定时器定义了索引标识,如下所示:

#define ITIMER_REAL 0

#define ITIMER_VIRTUAL 1

#define ITIMER_PROF 2

7.7.1 数据结构itimerval

虽然,在内核中间隔定时器的间隔计数器是以时钟滴答次数为单位,但是让用户以时钟滴答为单位来指定间隔定时器的间隔计数器的初值显然是不太方便的,因为用户习惯的时间单位是秒、毫秒或微秒等。所以Linux定义了数据结构itimerval来让用户以秒或微秒为单位指定间隔定时器的时间间隔值。其定义如下(include/linux/time.h):

struct itimerval {

struct timeval it_interval; /* timer interval */

struct timeval it_value; /* current value */

};

其中,it_interval成员表示间隔计数器的初始值,而it_value成员表示间隔计数器的当前值。这两个成员都是timeval结构类型的变量,因此其精度可以达到微秒级。

timeval与jiffies之间的相互转换

由于间隔定时器的间隔计数器的内部表示方式与外部表现方式互不相同,因此有必要实现以微秒为单位的timeval结构和为时钟滴答次数单位的jiffies之间的相互转换。为此,Linux在kern el/itimer.c中实现了两个函数实现二者的互相转换——tvtojiffies()函数和jiffiestotv()函数。它们的源码如下:

static unsigned long tvtojiffies(struct timeval *value)

{

unsigned long sec = (unsigned) value->tv_sec;

unsigned long usec = (unsigned) value->tv_usec;

if (sec > (ULONG_MAX / HZ))

return ULONG_MAX;

usec += 1000000 / HZ - 1;

usec /= 1000000 / HZ;

return HZ*sec+usec;

}

static void jiffiestotv(unsigned long jiffies, struct timeval *value)

{

value->tv_usec = (jiffies % HZ) * (1000000 / HZ);

value->tv_sec = jiffies / HZ;

7.7.2 真实间隔定时器ITIMER_REAL的底层运行机制

间隔定时器ITIMER_VIRT和ITIMER_PROF的底层运行机制是分别通过函数do_it_virt()函数和do_it_prof()函数来实现的,这里就不再重述(可以参见7.4.3节)。

由于间隔定时器ITIMER_REAL本质上与内核动态定时器并无区别。因此内核实际上是通过内核动态定时器来实现进程的ITIMER_REAL间隔定时器的。为此,task_struct结构中专门设立一个timer_list结构类型的成员变量real_timer。动态定时器real_timer 的函数指针function总是被task_struct结构的初始化宏INIT_TASK设置为指向函数it_real_fn()。如下所示(includ e/linux/sched.h):

#define INIT_TASK(tsk)

……

real_timer:{

function:it_real_fn

}

……

}

而real_timer链表元素list和data成员总是被进程创建时分别初始化为空和进程task_str uct结构的地址,如下所示(kernel/fork.c):int do_fork(……)

{

p->it_real_value = p->it_virt_value = p->it_prof_value = 0;

p->it_real_incr = p->it_virt_incr = p->it_prof_incr = 0;

init_timer(&p->real_timer);

p->real_timer.data = (unsigned long)p;

……

}

当用户通过setitimer()系统调用来设置进程的ITIMER_REAL间隔定时器时,it_real_incr 被设置成非零值,于是该系统调用相应地设置好real_timer.expires值,然后进程的real_timer 定时器就被加入到内核动态定时器链表中,这样该进程的ITIMER_REAL间隔定时器就被启动了。当real_timer定时器到期时,它的关联函数it_real_fn()将被执行。注意!所有进程的real_ti mer定时器的function函数指针都指向it_real_fn()这同一个函数,因此it_real_fn()函数必须通过其参数来识别是哪一个进程,为此它将unsigned long类型的参数p解释为进程task_struc t结构的地址。该函数的源码如下(kernel/itimer.c):

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) {

if (interval > (unsigned long) LONG_MAX)

interval = LONG_MAX;

p->real_timer.expires = jiffies + interval;

add_timer(&p->real_timer);

}

}

函数it_real_fn()的执行过程大致如下:

(1)首先将参数p通过强制类型转换解释为进程的task_struct结构类型的指针。

(2)向进程发送SIGALRM信号。

(3)在进程的it_real_incr非0的情况下继续启动real_timer定时器。首先,计算real_ti mer定时器的expires值为(jiffies+it_real_incr)。然后,调用add_timer()函数将real_ti mer加入到内核动态定时器链表中。

7.7.3 itimer定时器的系统调用

与itimer定时器相关的syscall有两个:getitimer()和setitimer()。其中,getitimer()用于查询调用进程的三个间隔定时器的信息,而setitimer()则用来设置调用进程的三个间隔定时器。这两个syscall都是现在kernel/itimer.c文件中。

7.7.3.1 getitimer()系统调用的实现

函数sys_getitimer()有两个参数:(1)which,指定查询调用进程的哪一个间隔定时器,其取值可以是ITIMER_REAL、ITIMER_VIRT和ITIMER_PROF三者之一。(2)value指针,指向用户空间中的一个itimerval结构,用于接收查询结果。该函数的源码如下:/* SMP: Only we modify our itimer values. */

asmlinkage long sys_getitimer(int which, struct itimerval *value)

{

int error = -EFAULT;

struct itimerval get_buffer;

if (value) {

error = do_getitimer(which, &get_buffer);

if (!error &&

copy_to_user(value, &get_buffer, sizeof(get_buffer)))

error = -EFAULT;

}

return error;

}

显然,sys_getitimer()函数主要通过do_getitimer()函数来查询当前进程的间隔定时器信息,并将查询结果保存在内核空间的结构变量get_buffer中。然后,调用copy_to_usr()宏将get_b uffer中结果拷贝到用户空间缓冲区中。

函数do_getitimer()的源码如下(kernel/itimer.c):

int do_getitimer(int which, struct itimerval *value)

{

register unsigned long val, interval;

switch (which) {

case ITIMER_REAL:

interval = current->it_real_incr;

val = 0;

/*

* FIXME! This needs to be atomic, in case the kernel timer happens!

*/

if (timer_pending(¤t->real_timer)) {

val = current->real_timer.expires - jiffies;

/* look out for negative/zero itimer.. */

if ((long) val <= 0)

val = 1;

}

break;

case ITIMER_VIRTUAL:

val = current->it_virt_value;

interval = current->it_virt_incr;

break;

case ITIMER_PROF:

val = current->it_prof_value;

interval = current->it_prof_incr; break;

default:

return(-EINVAL);

}

jiffiestotv(val, &value->it_value); jiffiestotv(interval, &value->it_interval);

return 0;

}

查询的过程如下:

(1)首先,用局部变量val和interval分别表示待查询间隔定时器的间隔计数器的当前值和初始值。

(2)如果which=ITIMER_REAL,则查询当前进程的ITIMER_REAL间隔定时器。于是从current->it_real_incr中得到ITIMER_REAL间隔定时器的间隔计数器的初始值,并将其保存到interval局部变量中。而对于间隔计数器的当前值,由于ITITMER_REAL间隔定时器是通过real _timer这个内核动态定时器来实现的,因此不能通过current->it_real_value来获得ITIMER_ REAL间隔定时器的间隔计数器的当前值,而必须通过real_timer来得到这个值。为此先用time r_pending()函数来判断current->real_timer是否已被起动。如果未启动,则说明ITIMER_R EAL间隔定时器也未启动,因此其间隔计数器的当前值肯定是0。因此将val变量简单地置0就可以了。如果已经启动,则间隔计数器的当前值应该等于(timer_real.expires-jiffies)。

(3)如果which=ITIMER_VIRT,则查询当前进程的ITIMER_VIRT间隔定时器。于是简单地将计数器初值it_virt_incr和当前值it_virt_value分别保存到局部变量interval和val中。

(4)如果which=ITIMER_PROF,则查询当前进程的ITIMER_PROF间隔定时器。于是简单地将计数器初值it_prof_incr和当前值it_prof_value分别保存到局部变量interval和val中。

(5)最后,通过转换函数jiffiestotv()将val和interval转换成timeval格式的时间值,并保存到value->it_value和value->it_interval中,作为查询结果返回。

7.7.3.2 setitimer()系统调用的实现

函数sys_setitimer()不仅设置调用进程的指定间隔定时器,而且还返回该间隔定时器的原有信息。它有三个参数:(1)which,含义与sys_getitimer()中的参数相同。(2)输入参数val

ue,指向用户空间中的一个itimerval结构,含有待设置的新值。(3)输出参数ovalue,指向用户空间中的一个itimerval结构,用于接收间隔定时器的原有信息。

该函数的源码如下(kernel/itimer.c):

/* SMP: Again, only we play with our itimers, and signals are SMP safe

* now so that is not an issue at all anymore.

*/

asmlinkage long sys_setitimer(int which, struct itimerval *value,

struct itimerval *ovalue)

{

struct itimerval set_buffer, get_buffer;

int error;

if (value) {

if(copy_from_user(&set_buffer, value, sizeof(set_buffer)))

return -EFAULT;

} else

memset((char *) &set_buffer, 0, sizeof(set_buffer));

error = do_setitimer(which, &set_buffer, ovalue ? &get_buffer : 0);

if (error || !ovalue)

return error;

if (copy_to_user(ovalue, &get_buffer, sizeof(get_buffer)))

return -EFAULT;

return 0;

}

对该函数的注释如下:

(1)在输入参数指针value非空的情况下,调用copy_from_user()宏将用户空间中的待设置信息拷贝到内核空间中的set_buffer结构变量中。如果value指针为空,则简单地将set_buf fer结构变量全部置0。

(2)调用do_setitimer()函数完成实际的设置操作。如果输出参数ovalue指针有效,则以内核变量get_buffer的地址作为do_setitimer()函数的第三那个调用参数,这样当do_setitim er()函数返回时,get_buffer结构变量中就将含有当前进程的指定间隔定时器的原来信息。Do_s etitimer()函数返回0值表示成功,非0值表示失败。

(3)在do_setitimer()函数返回非0值的情况下,或者ovalue指针为空的情况下(不需要输出间隔定时器的原有信息),函数就可以直接返回了。

(4)如果ovalue指针非空,调用copy_to_user()宏将get_buffer()结构变量中值拷贝到ovalue所指向的用户空间中去,以便让用户得到指定间隔定时器的原有信息值。

函数do_setitimer()的源码如下(kernel/itimer.c):

int do_setitimer(int which, struct itimerval *value, struct itimerval *ovalue) {

register unsigned long i, j;

int k;

i = tvtojiffies(&value->it_interval);

j = tvtojiffies(&value->it_value);

if (ovalue && (k = do_getitimer(which, ovalue)) < 0) return k;

switch (which) {

case ITIMER_REAL:

del_timer_sync(¤t->real_timer);

current->it_real_value = j;

current->it_real_incr = i;

if (!j)

break;

if (j > (unsigned long) LONG_MAX)

j = LONG_MAX;

i = j + jiffies;

current->real_timer.expires = i;

add_timer(¤t->real_timer); break;

case ITIMER_VIRTUAL:

if (j)

j++;

current->it_virt_value = j; current->it_virt_incr = i; break;

case ITIMER_PROF:

if (j)

j++;

current->it_prof_value = j; current->it_prof_incr = i; break;

default:

return -EINVAL;

}

return 0;

}

对该函数的注释如下:

(1)首先调用tvtojiffies()函数将timeval格式的初始值和当前值转换成以时钟滴答为单位的时间值。并分别保存在局部变量i和j中。

(2)如果ovalue指针非空,则调用do_getitimer()函数查询指定间隔定时器的原来信息。如果do_getitimer()函数返回负值,说明出错。因此就要直接返回错误值。否则继续向下执行开始真正地设置指定的间隔定时器。

(3)如果which=ITITMER_REAL,表示设置ITIMER_REAL间隔定时器。(a)调用del _timer_sync()函数(该函数在单CPU系统中就是del_timer()函数)将当前进程的real_timer 定时器从内核动态定时器链表中删除。(b)将it_real_incr和it_real_value分别设置为局部变量i和j。(c)如果j=0,说明不必启动real_timer定时器,因此执行break语句退出switch…case控制结构,而直接返回。(d)将real_timer的expires成员设置成(jiffies+当前值j),然后调用add_timer()函数将当前进程的real_timer定时器加入到内核动态定时器链表中,从而启动该定时器。

(4)如果which=ITIMER_VIRT,则简单地用局部变量i和j的值分别更新it_virt_incr和it_virt_value就可以了。

(5)如果which=ITIMER_PROF,则简单地用局部变量i和j的值分别更新it_prof_incr

和it_prof_value就可以了。

(6)最后,返回0值表示成功。

7.7.3.3 alarm系统调用

系统调用alarm可以让调用进程在指定的秒数间隔后收到一个SIGALRM信号。它只有一个参数seconds,指定以秒数计的定时间隔。函数sys_alarm()的源码如下(kernel/timer.c):/*

* For backwards compatibility? This can be done in libc so Alpha

* and all newer ports shouldn't need it.

*/

asmlinkage unsigned long sys_alarm(unsigned int seconds)

{

struct itimerval it_new, it_old;

unsigned int oldalarm;

it_new.it_https://www.sodocs.net/doc/161955236.html,_sec = it_new.it_https://www.sodocs.net/doc/161955236.html,_usec = 0;

it_new.it_https://www.sodocs.net/doc/161955236.html,_sec = seconds;

it_new.it_https://www.sodocs.net/doc/161955236.html,_usec = 0;

do_setitimer(ITIMER_REAL, &it_new, &it_old);

oldalarm = it_old.it_https://www.sodocs.net/doc/161955236.html,_sec;

/* ehhh.. We can't return 0 if we have an alarm pending.. */

/* And we'd better return too much than too little anyway */

Linux内核—文件系统模块的设计和开发

Linux内核—文件系统模块的设计和开发 郑小辉 摘要:目前,Linux技术已经成为IT技术发展的热点,投身于Linux技术研究的社区、研究机构和软件企业越来越多,支持Linux的软件、硬件制造商和解决方案提供商也迅速增加,Linux在信息化建设中的应用范围也越来越广,Linux产业链已初步形成,并正在得到持续的完善。随着整个Linux产业的发展,Linux技术也处在快速的发展过程中,形成了若干技术热点。 本文介绍了Linux的发展和特点,以及与其他文件系统的区别。文中主要是对Linux2.4.0内核文件系统源代码的分析,并参考其文件格式设计一个简洁的文件系统。源代码的分析主要介绍了VFS文件系统的结构,Linux自己的Ext2文件系统结构,以及文件系统中的主要函数操作。 在设计的简洁文件系统中,通过调用一些系统函数实现了用户的登录、浏览目录、创建目录、更改目录、创建文件以及退出系统功能。 关键字:Linux 源代码分析文件系统Ext2 Linux内核

Linux kernel -Design and development for the File System Module Zheng xiaohui Abstract: Currently, Linux IT technology has become a hot development technology. Participating in Linux technology research communities, research institutes and software enterprises are in support of Linux more and more, software and hardware manufacturers and solution providers have increased rapidly, In the development of the information industry the Linux application is also increasing, Linux industry chain has taken shape, and is sustained improvemently. With the entire industry in the development of Linux, and Linux is also at the rapid development process, formed a number of technical points. This paper presents the development of Linux and features, and with other file system differences. The main text of the document is Linux2.4.0 system kernel source code analysis, and I reference its file format to design a simple file system. The analysis of the source code mainly on the VFS file system structure, Linux Ext2 its own file system structures, file systems and the main function operation. In the design of the file simple system, some system function is used to achieve function such as: the user's login, browse catalogs, create directories, Change directory, create documents and withdraw from the system function and etc. Key words: Linux, the source code, file system, Ext2, Linux kernel

Linux内核修改与编译图文教程

Linux 内核修改与编译图文教程 1

1、实验目的 针对Ubuntu10.04中,通过下载新的内核版本,并且修改新版本内核中的系统调用看,然后,在其系统中编译,加载新内核。 2、任务概述 2.1 下载新内核 https://www.sodocs.net/doc/161955236.html,/ 2.2 修改新内核系统调用 添加新的系统调用函数,用来判断输入数据的奇偶性。 2.3 进行新内核编译 通过修改新版内核后,进行加载编译。最后通过编写测试程序进行测试 3、实验步骤 3.1 准备工作 查看系统先前内核版本: (终端下)使用命令:uname -r 2

3.2 下载最新内核 我这里使用的内核版本是 3.3 解压新版内核 将新版内核复制到“/usr/src”目录下 在终端下用命令:cd /usr/src进入到该文件目录 解压内核:linux-2.6.36.tar.bz2,在终端进入cd /usr/src目录输入一下命令: bzip2 -d linux-2.6.36.tar.bz2 tar -xvf linux-2.6.36.tar 文件将解压到/usr/src/linux目录中 3

使用命令: ln -s linux-2.6.36 linux 在终端下输入一下命令: sudo apt-get install build-essential kernel-package libncurses5-dev fakeroot sudo aptitude install libqt3-headers libqt3-mt-dev libqt3-compat-headers libqt3-mt 4

linux定时器详解

Linux内核定时器详解 80X86体系结构上,常用的定时器电路 实时时钟(RTC) RTC内核通过IRQ8上发出周期性的中断,频率在2-8192HZ之间,掉电后依然工作,内核通过访问0x70和0x71 I/O端口访问RTC。 时间戳计时器(TSC) 利用CLK输入引线,接收外部振荡器的时钟信号,该计算器是利用64位的时间戳计时器寄存器来实现额,与可编程间隔定时器传递来的时间测量相比,更为精确。 可编程间隔定时器(PIT) PIT的作用类似于微波炉的闹钟,PIT永远以内核确定的固定频率发出中断,但频率不算高。 CPU本地定时器 利用PIC或者APIC总线的时钟计算。 高精度时间定时器(HPET) 功能比较强大,家机很少用,也不去记了。 ACPI电源管理定时器 它的时钟信号拥有大约为3.58MHZ的固定频率,该设备实际上是一个简单的计数器,为了读取计算器的值,内核需要访问某个I/O端口,需要初始化 定时器的数据结构 利用timer_opts描述定时器 Timer_opts的数据结构 Name :标志定时器员的一个字符串 Mark_offset :记录上一个节拍开始所经过的时间,由时钟中断处理程序调用 Get_offset 返回自上一个节拍开始所经过的时间

Monotonic_clock :返回自内核初始化开始所经过的纳秒数 Delay:等待制定数目的“循环” 定时插补 就好像我们要为1小时35分34秒进行定时,我们不可能用秒表去统计,肯定先使用计算时的表,再用计算分的,最后才用秒表,在80x86架构的定时器也会使用各种定时器去进行定时插补,我们可以通过cur_timer指针来实现。 单处理器系统上的计时体系结构 所有与定时有关的活动都是由IRQ线0上的可编程间隔定时器的中断触发。 初始化阶段 1. 初始化间,time_init()函数被调用来建立计时体系结构 2. 初始化xtime变量(xtime变量存放当前时间和日期,它是一个timespec 类型的数据结构) 3. 初始化wall_to_monotonic变量,它跟xtime是同一类型的,但它存放将加在xtime上的描述和纳秒数,这样即使突发改变xtime也不会受到影响。 4. 看是否支持高精度计时器HPET 5. 调用select_timer()挑选系统中可利用的最好的定时资源,并让 cur_timer变量指向该定时器 6. 调用setup_irq(0,&irq0)来创建与IRQ相应的中断门。 时钟中断处理程序 1. 在xtime_lock顺序锁产生一个write_seqlock()来保护与定时相关的内核变量,这样防止中断让该进程被阻止。 2. 执行cur_timer定时器对象的mark_offset方法(记录上一个节拍开始所经过的时间,由时钟中断处理程序调用) 3. 调用do_timer_interrupt函数,步骤为 a) 使jiffies_64值增1 b) 调用updata_times()函数来更新系统日期和时间。

探究linux内核,超详细解析子系统

探究linux内核,超详细解析子系统 Perface 前面已经写过一篇《嵌入式linux内核的五个子系统》,概括性比较强,也比较简略,现在对其进行补充说明。 仅留此笔记,待日后查看及补充!Linux内核的子系统 内核是操作系统的核心。Linux内核提供很多基本功能,如虚拟内存、多任务、共享库、需求加载、共享写时拷贝(Copy-On-Write)以及网络功能等。增加各种不同功能导致内核代码不断增加。 Linux内核把不同功能分成不同的子系统的方法,通过一种整体的结构把各种功能集合在一起,提高了工作效率。同时还提供动态加载模块的方式,为动态修改内核功能提供了灵活性。系统调用接口用户程序通过软件中断后,调用系统内核提供的功能,这个在用户空间和内核提供的服务之间的接口称为系统调用。系统调用是Linux内核提供的,用户空间无法直接使用系统调用。在用户进程使用系统调用必须跨越应用程序和内核的界限。Linux内核向用户提供了统一的系统调用接口,但是在不同处理器上系统调用的方法

各不相同。Linux内核提供了大量的系统调用,现在从系统 调用的基本原理出发探究Linux系统调用的方法。这是在一个用户进程中通过GNU C库进行的系统调用示意图,系 统调用通过同一个入口点传入内核。以i386体系结构为例,约定使用EAX寄存器标记系统调用。 当加载了系统C库调用的索引和参数时,就会调用0x80软件中断,它将执行system_call函数,这个函数按照EAX 寄存器内容的标示处理所有的系统调用。经过几个单元测试,会使用EAX寄存器的内容的索引查system_call_table表得到系统调用的入口,然后执行系统调用。从系统调用返回后,最终执行system_exit,并调用resume_userspace函数返回用户空间。 linux内核系统调用的核心是系统多路分解表。最终通过EAX寄存器的系统调用标识和索引值从对应的系统调用表 中查出对应系统调用的入口地址,然后执行系统调用。 linux系统调用并不单层的调用关系,有的系统调用会由

linux内核的网络配置

文章来源 https://www.sodocs.net/doc/161955236.html,/p/2088592067 第9节, Networking support 关于网络支持 上图 讲解; RF switch subsystem support 这个一般是要的,因为有些无线和蓝牙放在一张卡上 选m,wireless(无线)里面的一些选项随之会自动选m,上图 注意: cfg80211 wireless extensions compatibility 这个兼容选项要选择,3.7默认是没有选择

如果没有选择,iwconfig会报告没有扩展 Bluetooth subsystem support 蓝牙,可以自己选择,如果有m就行 还有子选项自己看下 如果还有红外线,无线电,对应选择,这个设备应该是很少networking option最上面的,全局网络选项,上图

Packet socket和Unix domain sockets 备必,而且不能成模块,不然udev会报一段信息给你 Transformation user configuration interface 选m,其实也很少用,像ipsec,下面的ipsec也可以选成模块 TCP/IP networking 要的,要的,子选项大部分不用,你也可以选上 IP: multicasting 多播 IP: advanced router 高级路由 你需要选上 IP: TCP syncookie support ~~sync flooding,同时还必须。。。个人没什么意义Large Receive Offload提高网络的东西,这个Y,如果你觉得现在不用,先m TCP: advanced congestion control这个你也可以Y The IPv6 protocol 很多要用到,虽然在兲现在没用,像systemd就要了 Security Marking和Network packet filtering framework (Netfilter) 个人没什么意义,你可以试下

Linux复习(成熟期版)

前面的可能考选择题,填空题,全部题型如下: 一、选择题(10个题,每题2分,共20分) 二、填空题(10个空,每空2分,共20分) 三、程序阅读题(2道题,共20分) 四、程序注释(1道题,共20分)可能考I/O 五、编程题(1道题,共20分) P 154 文件I/O操作open()、read()、write()、lseek()和close() 仔细阅读程序,能写注释或填空。 /* copy_file.c */ #include #include #include #include #include #include #define BUFFER_SIZE 1024/* 每次读写缓存大小1KB,关于《linux读写文件运行效率》的传送门*/ #define SRC_FILE_NAME "src_file" /* 源文件名,之后用SRC_FILE_NAME代替src_file */ #define DEST_FILE_NAME "dest_file" /* 目标文件名文件名,之后用DEST_FILE_NAME代替dest_file */ #define OFFSET 10240/* 复制的数据大小 1MB,也是lseek函数的偏移量*/ int main() { int src_file, dest_file; //定义了2个变量 unsigned char buff[BUFFER_SIZE]; //设定缓存的大小,定义为数组,存放读写缓存 int real_read_len; /* 以只读方式打开源文件 */ src_file = open(SRC_FILE_NAME, O_RDONLY); //前面的src_file是变量名,成功时为3,失败时为-1 //也可以写成src_file = open("src_file", O_RDONLY); /* 以只写方式打开目标文件,若此文件不存在则创建该文件, 访问权限值为644 */ dest_file = open(DEST_FILE_NAME, O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); //前面的dest_file是变量名,成功时为4,失败时为-1 //也可以写成 d est_file = open("dest_file",O_WRONLY|O_CREAT,644); if (src_file < 0 || dest_file < 0) //打开文件失败时 { printf("Open file error\n"); //输出这句话 exit(1); // exit()中,1是返回给操作系统的,0是正常退出 } /* 将源文件的读写指针移到最后10KB的起始位置*/ lseek(src_file, -OFFSET, SEEK_END); //从文件的结尾处向前移动10KB // OFFSET 是偏移量,正值向前移,负值向后移。这里在 OFFSET 前加了负号。 // SEEK_END 表示从文件的结尾开始。 //也可以写成lseek("src_file", -10240, SEEK_END); /* 读取源文件的最后10KB数据并写到目标文件中,每次读写1KB */ while ((real_read_len = read(src_file, buff, sizeof(buff))) > 0)//读取成功时返回读到的字节数//红字部分也可以写成 read("src_file", buff, 1024) { write(dest_file, buff, real_read_len); // real_read_len 在上一步被赋值为 1KB,即real_read_len = 1024; //也可以写成write("src_file", buff, 1024)

linux内核IMQ源码实现分析

本文档的Copyleft归wwwlkk所有,使用GPL发布,可以自由拷贝、转载,转载时请保持文档的完整性,严禁用于任何商业用途。 E-mail: wwwlkk@https://www.sodocs.net/doc/161955236.html, 来源: https://www.sodocs.net/doc/161955236.html,/?business&aid=6&un=wwwlkk#7 linux2.6.35内核IMQ源码实现分析 (1)数据包截留并重新注入协议栈技术 (1) (2)及时处理数据包技术 (2) (3)IMQ设备数据包重新注入协议栈流程 (4) (4)IMQ截留数据包流程 (4) (5)IMQ在软中断中及时将数据包重新注入协议栈 (7) (6)结束语 (9) 前言:IMQ用于入口流量整形和全局的流量控制,IMQ的配置是很简单的,但很少人分析过IMQ的内核实现,网络上也没有IMQ的源码分析文档,为了搞清楚IMQ的性能,稳定性,以及借鉴IMQ的技术,本文分析了IMQ的内核实现机制。 首先揭示IMQ的核心技术: 1.如何从协议栈中截留数据包,并能把数据包重新注入协议栈。 2.如何做到及时的将数据包重新注入协议栈。 实际上linux的标准内核已经解决了以上2个技术难点,第1个技术可以在NF_QUEUE机制中看到,第二个技术可以在发包软中断中看到。下面先介绍这2个技术。 (1)数据包截留并重新注入协议栈技术

(2)及时处理数据包技术 QoS有个技术难点:将数据包入队,然后发送队列中合适的数据包,那么如何做到队列中的数

激活状态的队列是否能保证队列中的数据包被及时的发送吗?接下来看一下,激活状态的队列的 证了数据包会被及时的发送。 这是linux内核发送软中断的机制,IMQ就是利用了这个机制,不同点在于:正常的发送队列是将数据包发送给网卡驱动,而IMQ队列是将数据包发送给okfn函数。

如何安装Linux内核源代码

如何获取Linux内核源代码 下载Linux内核当然要去官方网站了,网站提供了两种文件下载,一种是完整的Linux 内核,另一种是内核增量补丁,它们都是tar归档压缩包。除非你有特别的原因需要使用旧版本的Linux内核,否则你应该总是升级到最新版本。 使用Git 由Linus领头的内核开发队伍从几年前就开始使用Git版本控制系统管理Linux内核了(参考阅读:什么是Git?),而Git项目本身也是由Linus创建的,它和传统的CVS不一样,Git是分布式的,因此它的用法和工作流程很多开发人员可能会感到很陌生,但我强烈建议使用Git下载和管理Linux内核源代码。 你可以使用下面的Git命令获取Linus内核代码树的最新“推送”版本: $ git clone git://https://www.sodocs.net/doc/161955236.html,/pub/scm/linux/kernel/git/torvalds/linux-2.6.git 然后使用下面的命令将你的代码树与Linus的代码树最新状态同步: $ git pull 安装内核源代码 内核包有GNU zip(gzip)和bzip2格式。Bzip2是默认和首选格式,因为它的压缩比通常比gzip更好,bzip2格式的Linux内核包一般采用linux-x.y.z.tar.bz2形式的文件名,这里的x.y.z是内核源代码的具体版本号,下载到源代码包后,解压和抽取就很简单了,如果你下载的是bzip2包,运行: $ tar xvjf linux-x.y.z.tar.bz2 如果你下载的是gzip包,则运行: $ tar xvzf linux-x.y.z.tar.gz 无论执行上面哪一个命令,最后都会将源代码解压和抽取到linux-x.y.z目录下,如果你使用Git下载和管理内核源代码,你不需要下载tar包,只需要运行git clone命令,它就会自动下载和解压。 内核源代码通常都会安装到/usr/src/linux下,但在开发的时候最好不要使用这个源代码树,因为针对你的C库编译的内核版本通常也链接到这里的。 应用补丁

Linux内核结构详解教程

Linux内核结构详解教程 ─────Linux内核教程 linux内核就像人的心脏,灵魂,指挥中心。 内核是一个操作系统的核心,它负责管理系统的进程,内存,设备驱动程序,文件和网络系统,决定着系统的性能和稳定性。内核以独占的方式执行最底层任务,保证系统正常运行。协调多个并发进程,管理进程使用的内存,使它们相互之间不产生冲突,满足进程访问磁盘的请求等等. 严格说Linux并不能称做一个完整的操作系统.我们安装时通常所说的Linux,是有很多集合组成的.应称为GNU/Linux. 一个Linux内核很少1.2M左右,一张软盘就能放下. 内容基础,语言简短简洁 红联Linux论坛是致力于Linux技术讨论的站点,目前网站收录的文章及教程基本能满足不同水平的朋友学习。 红联Linux门户: https://www.sodocs.net/doc/161955236.html, 红联Linux论坛: https://www.sodocs.net/doc/161955236.html,/bbs 红联Linux 论坛大全,所有致力点都体现在这 https://www.sodocs.net/doc/161955236.html,/bbs/rf/linux/07.htm

目录 Linux内核结构详解 Linux内核主要五个子系统详解 各个子系统之间的依赖关系 系统数据结构 Linux的具体结构 Linux内核源代码 Linux 内核源代码的结构 从何处开始阅读源代码 海量Linux技术文章

Linux内核结构详解 发布时间:2006-11-16 19:05:29 Linux内核主要由五个子系统组成:进程调度,内存管理,虚拟文件系统,网络接口,进程间通信。

Linux内核主要五个子系统详解 发布时间:2006-11-16 19:05:54 1.进程调度(SCHED):控制进程对CPU的访问。当需要选择下一个进程运行时,由调度程序选择最值得运行的进程。可运行进程实际上是仅等待CPU资源的进程,如果某个进程在等待其它资源,则该进程是不可运行进程。Linux使用了比较简单的基于优先级的进程调度算法选择新的进程。 2.内存管理(MM)允许多个进程安全的共享主内存区域。Linux的内存管理支持虚拟内存,即在计算机中运行的程序,其代码,数据,堆栈的总量可以超过实际内存的大小,操作系统只是把当前使用的程序块保留在内存中,其余的程序块则保留在磁盘中。必要时,操作系统负责在磁盘和内存间交换程序块。内存管理从逻辑上分为硬件无关部分和硬件有关部分。硬件无关部分提供了进程的映射和逻辑内存的对换;硬件相关的部分为内存管理硬件提供了虚拟接口。 3.虚拟文件系统(VirtualFileSystem,VFS)隐藏了各种硬件的具体细节,为所有的设备提供了统一的接口,VFS提供了多达数十种不同的文件系统。虚拟文件系统可以分为逻辑文件系统和设备驱动程序。逻辑文件系统指Linux所支持的文件系统,如ext2,fat等,设备驱动程序指为每一种硬件控制器所编写的设备驱动程序模块。 4.网络接口(NET)提供了对各种网络标准的存取和各种网络硬件的支持。网络接口可分为网络协议和网络驱动程序。网络协议部分负责实现每一种可能的网络传输协议。网络设备驱动程序负责与硬件设备通讯,每一种可能的硬件设备都有相应的设备驱动程序。 5.进程间通讯(IPC) 支持进程间各种通信机制。 处于中心位置的进程调度,所有其它的子系统都依赖它,因为每个子系统都需要挂起或恢复进程。一般情况下,当一个进程等待硬件操作完成时,它被挂起;当操作真正完成时,进程被恢复执行。例如,当一个进程通过网络发送一条消息时,网络接口需要挂起发送进程,直到硬件成功地完成消息的发送,当消息被成功的发送出去以后,网络接口给进程返回一个代码,表示操作的成功或失败。其他子系统以相似的理由依赖于进程调度。

史上最全linux内核配置详解

对于每一个配置选项,用户可以回答"y"、"m"或"n"。其中"y"表示将相应特性的支持或设备驱动程序编译进内核;"m"表示将相应特性的支持或设备驱动程序编译成可加载模块,在需要时,可由系统或用户自行加入到内核中去;"n"表示内核不提供相应特性或驱动程序的支持。只有<>才能选择M 1. General setup(通用选项) [*]Prompt for development and/or incomplete code/drivers,设置界面中显示还在开发或者还没有完成的代码与驱动,最好选上,许多设备都需要它才能配置。 [ ]Cross-compiler tool prefix,交叉编译工具前缀,如果你要使用交叉编译工具的话输入相关前缀。默认不使用。嵌入式linux更不需要。 [ ]Local version - append to kernel release,自定义版本,也就是uname -r可以看到的版本,可以自行修改,没多大意义。 [ ]Automatically append version information to the version string,自动生成版本信息。这个选项会自动探测你的内核并且生成相应的版本,使之不会和原先的重复。这需要Perl的支持。由于在编译的命令make-kpkg 中我们会加入- –append-to-version 选项来生成自定义版本,所以这里选N。 Kernel compression mode (LZMA),选择压缩方式。 [ ]Support for paging of anonymous memory (swap),交换分区支持,也就是虚拟内存支持,嵌入式不需要。 [*]System V IPC,为进程提供通信机制,这将使系统中各进程间有交换信息与保持同步的能力。有些程序只有在选Y的情况下才能运行,所以不用考虑,这里一定要选。 [*]POSIX Message Queues,这是POSIX的消息队列,它同样是一种IPC(进程间通讯)。建议你最好将它选上。 [*]BSD Process Accounting,允许进程访问内核,将账户信息写入文件中,主要包括进程的创建时间/创建者/内存占用等信息。可以选上,无所谓。 [*]BSD Process Accounting version 3 file format,选用的话统计信息将会以新的格式(V3)写入,注意这个格式和以前的v0/v1/v2 格式不兼容,选不选无所谓。 [ ]Export task/process statistics through netlink (EXPERIMENTAL),通过通用的网络输出工作/进程的相应数据,和BSD不同的是,这些数据在进程运行的时候就可以通过相关命令访问。和BSD类似,数据将在进程结束时送入用户空间。如果不清楚,选N(实验阶段功能,下同)。 [ ]Auditing support,审计功能,某些内核模块需要它(SELINUX),如果不知道,不用选。 [ ]RCU Subsystem,一个高性能的锁机制RCU 子系统,不懂不了解,按默认就行。 [ ]Kernel .config support,将.config配置信息保存在内核中,选上它及它的子项使得其它用户能从/proc/ config.gz中得到内核的配置,选上,重新配置内核时可以利用已有配置Enable access to .config through /proc/config.gz,上一项的子项,可以通过/proc/ config.gz访问.config配置,上一个选的话,建议选上。 (16)Kernel log buffer size (16 => 64KB, 17 => 128KB) ,内核日志缓存的大小,使用默认值即可。12 => 4 KB,13 => 8 KB,14 => 16 KB单处理器,15 => 32 KB多处理器,16 => 64 KB,17 => 128 KB。 [ ]Control Group support(有子项),使用默认即可,不清楚可以不选。 Example debug cgroup subsystem,cgroup子系统调试例子 Namespace cgroup subsystem,cgroup子系统命名空间 Device controller for cgroups,cgroups设备控制器

Linux设置内核参数的方法

Linux设置内核参数的方法 1内核参数的查看方法 使用“sysctl -a”命令可以查看所有正在使用的内核参数。内核参数比较多(一般多达500项),按照前缀主要分为以下几大类:net.ipv4、net.ipv6、net.core、vm、fs、dev.parport、dev.cdrom 、dev.raid、kernel等等。相同的linux,安装的组件和使用的方式不一样,正在使用的内核参数是不一样的。 所有的内核参数的说明文档是放到/usr/src/linux/Documentation/sysctl中的,如果想知道对内核参数的说明,可以到该目录下查看相应的说明文档。 2内核参数的的设置方法 由于Linux的内核参数信息都存在内存中,因此可以通过命令直接修改,并且修改后直接生效。也可以通过文件的方式进行设置。下面就介绍这两种修改方法。 2.1命令设置的方式 可以用两种方法实现。 1、使用“sysctl -w 参数名=值”的方式 假设我们把net.ipv4.ip_forward的值修改为1,使用命令“sysctl -w net.ipv4.ip_forward=1”。 2、修改内核参数对应的proc文件 内核参数位于/proc/sys/之下,参数名称是以文件所在的路径,并将“/”以“.”来取代。举例来说,/proc/sys/net/ip_forward的参数名称为net.ipv4.ip_forward。 同样把net.ipv4.ip_forward的值修改为1,使用命令“echo “1”> /proc/sys/net/ipv4/ip_forward”。 注意,这里proc文件跟普通的文件不一样。一般一个文件用echo写入内容之后,会变成一个文本文件,但echo修改proc文件之后还是个空文件。 2.2文件设置的方式 更改的内核参数默认保存在/etc/sysctl.conf文件中。修改的时候可以直接用vi编辑sysctl.conf文件,增加要修改的内核参数内容,修改的格式为:参数名=值。例如,把net.ipv4.ip_forward的值修改为1,在sysctl.conf中增加下面这行内容:net.ipv4.ip_forward=1 文件修改好后,进行保存。然后使用“sysctl -p 配置文件名”来使配置生效,如果配置文件是默认的,可以不用输配置文件名,即使用“sysctl -p”。 通过文件设置的方式修改的内核参数是在系统重启后将失效(我之前认为修改后的内核参数放在文件中,系统启动的时候会读这个文件,重启后设置应该不会失效。但经过验证,一般会失效,但如果把将默认的boot.sysctl服务打开,所以系统启动时就会执行这个文件的设置)。把我们修改参数的命令写入启动执行脚本文件里/etc/rc.local,这样系统重启后配置就不会失效。 文件方式的好处是内核参数设置的值可以用文件保留下来,调用“sysctl -p”可以使文

Linux内核驱动模块编写概览-ioctl,class_create,device_create

如果你对内核驱动模块一无所知,请先学习内核驱动模块的基础知识。 如果你已经入门了内核驱动模块,但是仍感觉有些模糊,不能从整体来了解一个内核驱动模块的结构,请赏读一下这篇拙文。 如果你已经从事内核模块编程N年,并且道行高深,也请不吝赐教一下文中的疏漏错误。 本文中我将实现一个简单的Linux字符设备,旨在大致勾勒出linux内核模块的编写方法的轮廓。其中重点介绍ioctl的用途。 我把这个简单的Linux字符设备模块命名为hello_mod. 设备类型名为hello_cl ass 设备名为hello 该设备是一个虚拟设备,模块加载时会在/sys/class/中创建名为hello_class 的逻辑设备,在/dev/中创建hello的物理设备文件。模块名为hello_mod,可接受输入字符串数据(长度小于128),处理该输入字符串之后可向外输出字符串。并且可以接受ioctl()函数控制内部处理字符串的方式。 例如: a.通过write函数写入“Tom”,通过ioctl函数设置langtype=chinese,通过read函数读出的数据将会是“你好!Tom/n” b.通过write函数写入“Tom”,通过ioctl函数设置langtype=english,通过read函数读出的数据将会是“hello!Tom/n” c.通过write函数写入“Tom”,通过ioctl函数设置langtype=pinyin,通过read函数读出的数据将会是“ni hao!Tom/n” 一般的内核模块中不会负责设备类别和节点的创建,我们在编译完之后会得到.o或者.k o文件,然后insmod之后需要mk nod来创建相应文件,这个简单的例子 中我们让驱动模块加载时负责自动创建设备类别和设备文件。这个功能有两个步骤, 1)创建设备类别文件class_cr eate(); 2)创建设备文件dev ice_create(); 关于这两个函数的使用方法请参阅其他资料。 linux设备驱动的编写相对wi ndows编程来说更容易理解一点因为不需要处理IR P,应用层函数和内核函数的关联方式浅显易懂。 比如当应曾函数对我的设备调用了open()函数,而最终这个应用层函数会调用我的设备中的自定义open()函数,这个函数要怎么写呢, 我在我的设备中定义的函数名是hello_mod_open,注意函数名是可以随意定义,但是函数签名是要符合内核要求的,具体的定义是怎么样请看 static int hello_mod_open(struct inode *, struct file *); 这样就定义了内核中的open函数,这只是定义还需要与我们自己的模块关联起来,这就要用到一个结构 struct file_operations 这个结构里面的成员是对应于设备操作的各种函数的指针。 我在设备中用到了这些函数所以就如下定义,注意下面的写法不是标准ANSI C的语法,而是GNU扩展语法。 struct file_operations hello_mod_fops = { .owner = THIS_MODULE, .open = hello_mod_open,

linux定时器和Jiffies

1.linux HZ Linux核心几个重要跟时间有关的名词或变数,以下将介绍HZ、tick与jiffies。 HZ Linux核心每隔固定周期会发出timer interrupt (IRQ 0),HZ是用来定义每一秒有几次timer interrupts。举例来说,HZ为1000,代表每秒有1000次timer interrupts。 HZ可在编译核心时设定,如下所示(以核心版本 adrian@adrian-desktop:~$ cd /usr/src/linux adrian@adrian-desktop:/usr/src/linux$ make menuconfig Processor type and features ---> Timer frequency (250 HZ) ---> 其中HZ可设定100、250、300或1000。 小实验 观察/proc/interrupt的timer中断次数,并于一秒后再次观察其值。理论上,两者应该相差250左右。 adrian@adrian-desktop:~$ cat /proc/interrupts | grep timer && sleep 1 && cat /proc/interrupts | grep timer 0: 9309306 IO-APIC-edge timer 0: 9309562 IO-APIC-edge timer 上面四个栏位分别为中断号码、CPU中断次数、PIC与装置名称。

要检查系统上HZ的值是什么,就执行命令 cat kernel/.config | grep '^CONFIG_HZ=' 2.Tick Tick是HZ的倒数,意即timer interrupt每发生一次中断的时间。如HZ为250时,tick为4毫秒(millisecond)。 3.Jiffies Jiffies为Linux核心变数(unsigned long),它被用来记录系统自开机以来,已经过了多少tick。每发生一次timer interrupt,Jiffies变数会被加一。值得注意的是,Jiffies于系统开机时,并非初始化成零,而是被设为-300*HZ (arch/i386/kernel/time.c),即代表系统于开机五分钟后,jiffies 便会溢位。那溢位怎么办?事实上,Linux核心定义几个macro(timer_after、time_after_eq、time_before与time_before_eq),即便是溢位,也能借由这几个macro正确地取得jiffies的内容。 另外,80x86架构定义一个与jiffies相关的变数jiffies_64 ,此变数64位元,要等到此变数溢位可能要好几百万年。因此要等到溢位这刻发生应该很难吧。

Linux内核分析-网络[五]:网桥

看完了路由表,重新回到netif_receive_skb ()函数,在提交给上层协议处理前,会执行下面一句,这就是网桥的相关操作,也是这篇要讲解的容。 view plaincopy to clipboardprint? 1. s kb = handle_bridge(skb, &pt_prev, &ret, orig_dev); 网桥可以简单理解为交换机,以下图为例,一台linux机器可以看作网桥和路由的结合,网桥将物理上的两个局域网LAN1、LAN2当作一个局域网处理,路由连接了两个子网1.0和2.0。从eth0和eth1网卡收到的报文在Bridge模块中会被处理成是由Bridge收到的,因此Bridge也相当于一个虚拟网卡。 STP五种状态 DISABLED BLOCKING LISTENING LEARNING FORWARDING 创建新的网桥br_add_bridge [net\bridge\br_if.c] 当使用SIOCBRADDBR调用ioctl时,会创建新的网桥br_add_bridge。 首先是创建新的网桥: view plaincopy to clipboardprint?

1. d ev = new_bridge_dev(net, name); 然后设置dev->dev.type为br_type,而br_type是个全局变量,只初始化了一个名字变量 view plaincopy to clipboardprint? 1. S ET_NETDEV_DEVTYPE(dev, &br_type); 2. s tatic struct device_type br_type = { 3. .name = "bridge", 4. }; 然后注册新创建的设备dev,网桥就相当一个虚拟网卡设备,注册过的设备用ifconfig 就可查看到: view plaincopy to clipboardprint? 1. r et = register_netdevice(dev); 最后在sysfs文件系统中也创建相应项,便于查看和管理: view plaincopy to clipboardprint? 1. r et = br_sysfs_addbr(dev); 将端口加入网桥br_add_if() [net\bridge\br_if.c] 当使用SIOCBRADDIF调用ioctl时,会向网卡加入新的端口br_add_if。 创建新的net_bridge_port p,会从br->port_list中分配一个未用的port_no,p->br会指向br,p->state设为BR_STATE_DISABLED。这里的p实际代表的就是网卡设备。 view plaincopy to clipboardprint? 1. p = new_nbp(br, dev); 将新创建的p加入CAM表中,CAM表是用来记录mac地址与物理端口的对应关系;而刚刚创建了p,因此也要加入CAM表中,并且该表项应是local的[关系如下图],可以看到,CAM表在实现中作为net_bridge的hash表,以addr作为hash值,链入 net_bridge_fdb_entry,再由它的dst指向net_bridge_port。

linux定时器详解

查看文章 Linux 定时器设置(一)2010-04-12 17:07定时器设置函数alarm设置的定时器只能精确到秒,而以下函数理论上可以精确到微妙: #include #include int getitimer(int which, struct itimerval *value); int setitimer(int which, const struct itimerval *value, struct itimerval *ovalue); 函数setitimer可以提供三种定时器,它们相互独立,任意一个定时完成都将发送定时信号到进程,并且自动重新计时。参数which确定了定时器的类型,如表10-6所示:表10-6 参数which与定时器类型取值含义信号发送ITIMER_REAL 定时真实时间,与alarm类型相同。SIGALRM ITIMER_VIRT 定时进程在用户态下的实际执行时间。 SIGVTALRM ITIMER_PROF 定时进程在用户态和核心态下的实际执行时间。SIGPROF 这三种定时器定时完成时给进程发送的信号各不相同,其中ITIMER_REAL类定时器发送SIGALRM信号,ITIMER_VIRT类定时器发送SIGVTALRM信号,ITIMER_REAL类定时器发送SIGPROF信号。函数alarm本质上设置的是低精确、非重载的ITIMER_REAL类定时器,它只能精确到秒,并且每次设置只能产生一次定时。函数setitimer设置的定时器则不同,它们不但可以计时到微妙(理论上),还能自动循环定时。 在一个Unix进程中,不能同时使用alarm和ITIMER_REAL类定时器。结构itimerval描述了定时器的组成:struct itimerval { struct tim. it_interval; /* 下次定时取值*/ struct tim. it_value; /* 本次定时设置值*/} 结构tim.描述了一个精确到微妙的时间:struct tim. { long tv_sec; /* 秒(1000000微秒)*/ long tv_usec; /* 微妙*/}函数setitimer设置一个定时器,参数value指向一个itimerval结构,该结构决定了设置的定时器信息,结构成员it_value指定首次定时的时间,结构成员it_interval指定下次定时的时间。定时器工作时,先将it_value的时间值减到0,发送一个信号,再将it_value赋值为it_interval的值,重新开始定时,如此反复。如果it_value 值被设置为0,则定时器停止定时;如果it_value值不为0但it_interval值为0,则定时器在一次定时后终止。函数setitimer调用成功时返回0,否则返回-1,参数ovalue如果不为空,返回上次的定时器状态。函数getitimer获取当前的定时器状态,整型参数which指定了读取的定时器类型,参数value返回定时器状态。函数调用成功返回0,否则返回-1。 例1. 设置一个定时器,每2.5秒产生一个SIGALRM信号。答:将itimerval结构的成员it_interval和成员it_value均赋值为 2.5秒即可:struct itimerval value;value.it_https://www.sodocs.net/doc/161955236.html,_sec=2;value.it_https://www.sodocs.net/doc/161955236.html,_usec=500000;value.it_https://www.sodocs.net/doc/161955236.html,_sec=2;value.it_i https://www.sodocs.net/doc/161955236.html,_usec=500000;setitimer(ITIMER_REAL, &value, NULL);函数setitimer设置的定时器可以重复定时,无需多次调用。例2. 设置一个定时器,进程在用户态下执行1秒钟后发出首次信号,以后进程每在用户态下执行3秒钟,发送一个信号。答:将itimerval结构的成员it_value均赋值为1秒,成员it_interval赋值为3秒即可:struct itimerval value;value.it_https://www.sodocs.net/doc/161955236.html,_sec=1;value.it_https://www.sodocs.net/doc/161955236.html,_usec=0;value.it_https://www.sodocs.net/doc/161955236.html,_sec=3;value.it_interva https://www.sodocs.net/doc/161955236.html,_usec=0;setitimer(ITIMER_VIRT, &value, NULL);例3. 取消一个ITIMER_PROF类定时器。答:将itimerval结构的成员it_value均赋值为0秒即可:struct itimerval value;value.it_https://www.sodocs.net/doc/161955236.html,_sec=1;value.it_https://www.sodocs.net/doc/161955236.html,_usec=0;setitimer(ITIMER_PROF, &value, NULL);例4. 设置一个定时1.5秒的真实时间定时器,它仅发送一次信号就自动取消。答:将itimerval结构的成员it_value均赋值为1.5秒,成员it_interval赋值为0秒即可:struct

相关主题