linux驱动之内核定时器驱动设计

linux驱动之内核定时器驱动设计
linux驱动之内核定时器驱动设计

linux驱动之内核定时器驱动设计

我的环境:

Fedora 14 内核版本为2.6.38.1

开发板:ARM9 TQ2440

移植内核版本:linux-2.6.30.4

定时器在linux内核中主要是采用一个结构体实现的。但是需要注意定时器是一个只运行一次的对象,也就是

当一个定时器结束以后,还需要重现添加定时器。但是

可以采用mod_timer()函数动态的改变定时器到达时间。这个驱动主要实现内核定时器的基本操作。内核定时器

主要是是通过下面的结构体struct timer_list实现。需要的头文件包括#include;,但是在实际开发过程中不需要包含该头文件,因为在sched.h中包含了该头文件。

struct timer_list {

struct list_head entry;

unsigned long expires;

void (*function)(unsigned long);

unsigned long data;

struct tvec_base *base;

#ifdef CONFIG_TIMER_STATS

void *start_site;

char start_comm[16];

int start_pid;

#endif

#ifdef CONFIG_LOCKDEP

struct lockdep_map lockdep_map;

#endif

};

定时器的实现主要是该结构体的填充和部分函数的配合即可完成。其中红色的部分是最主要的几个元素,1、expires主要是用来定义定时器到期的时间,通常采用jiffies这个全局变量和HZ这个全局变量配合设置该元素的值。比如expires = jiffies + n*HZ,其中jiffies 是自启动以来的滴答数,HZ是一秒种的滴答数。

2、function可以知道是一个函数指针,该函数就是定

时器的处理函数,类似我们在中断中的中断函数,其实

定时器和中断有很大的相似性。定时器处理函数是自己

定义的函数。

3、data通常是实现参数的传递,从function的参数类

型可以知道,data可以作为定时器处理函数的参数。

其他的元素可以通过内核的函数来初始化。

初始化函数为:

init_timer(struct timer_list * timer);

或者直接DEFINE_TIMER宏实现定义和初始化操作。

#define DEFINE_TIMER(_name, _function,

_expires, _data)

\

struct timer_list _name =

\

TIMER_INITIALIZER(_function, _expires, _data) 添加定时器到内核的函数:

void add_timer(struct timer_list *timer)

{

BUG_ON(timer_pending(timer));

mod_timer(timer, timer->;expires);

}

删除定时器函数,如果定时器的定时时间还没有到达,那么才可以删除定时器:

int del_timer(struct timer_list *timer)

修改定时器的到达时间,该函数的特点是,不管定时器是否到达时间,都会重现添加一个定时器到内核。所

以可以在定时处理函数中可以调用该函数修改需要重新

定义的到达时间。

int mode_timer(struct timer_list *timer,unsigned long expires)

int mod_timer(struct timer_list *timer, unsigned long expires)

{

/*

* This is a common optimization triggered by the

* networking code - if the timer is re-modified

* to be the same thing then just return:

*/

if (timer->;expires == expires &&

timer_pending(timer))

return 1;

/*注意调用的条件,也就是说明当前的定时器为链

表的最后一个*/

return __mod_timer(timer, expires, false); }

static inline int

__mod_timer(struct timer_list *timer, unsigned long expires, bool pending_only)

{

struct tvec_base *base, *new_base;

unsigned long flags;

int ret;

ret = 0;

timer_stats_timer_set_start_info(timer);

BUG_ON(!timer->;function);

base = lock_timer_base(timer, &flags); if (timer_pending(timer)) {

detach_timer(timer, 0);

ret = 1;

} else {

if (pending_only)

goto out_unlock;

}

debug_timer_activate(timer);

new_base = __get_cpu_var(tvec_bases);

if (base != new_base) {

/*

* We are trying to schedule the timer on the local CPU.

* However we can't change timer's base while it is running,

* otherwise del_timer_sync() can't detect that the timer's

* handler yet has not finished. This also guarantees that

* the timer is serialized wrt itself.

*/

if (likely(base->;running_timer != timer)) { /* See the comment in lock_timer_base() */ timer_set_base(timer, NULL);

spin_unlock(&base->;lock);

base = new_base;

spin_lock(&base->;lock);

timer_set_base(timer, base);

}

}

timer->;expires = expires;

internal_add_timer(base, timer);

out_unlock:

spin_unlock_irqrestore(&base->;lock, flags); return ret;

}

static void internal_add_timer(struct

tvec_base *base, struct timer_list *timer)

{

unsigned long expires = timer->;expires;

unsigned long idx = expires -

base->;timer_jiffies;

struct list_head *vec;

if (idx ;tv1.vec + i;

} else if (idx ;>; TVR_BITS) & TVN_MASK;

vec = base->;tv2.vec + i;

} else if (idx ;>; (TVR_BITS + TVN_BITS)) & TVN_MASK;

vec = base->;tv3.vec + i;

} else if (idx ;>; (TVR_BITS + 2 * TVN_BITS)) & TVN_MASK;

vec = base->;tv4.vec + i;

} else if ((signed long) idx ;tv1.vec +

(base->;timer_jiffies & TVR_MASK);

} else {

int i;

/* If the timeout is larger than 0xffffffff on 64-bit

* architectures then we use the maximum timeout:

*/

if (idx >; 0xffffffffUL) {

idx = 0xffffffffUL;

expires = idx + base->;timer_jiffies;

}

i = (expires >;>; (TVR_BITS + 3 * TVN_BITS)) & TVN_MASK;

vec = base->;tv5.vec + i;

}

/*

* Timers are FIFO:

*/

/*添加到链表的最后,这说明mod_timer实现了重新注册一个定时器的操作*/

list_add_tail(&timer->;entry, vec);

}

从上面的分析可以看出,mod_timer的实现过程比较复杂,但是基本上说明了mod_timer函数重新注册定时器的操作过程。

一般而言定时器的基本操作主要是上面的几个函数。

我的基于内核定时器的驱动函数如下,参考了宋宝华的Linux设备驱动开发详解(第二版)。

驱动程序:

#include;

#include;

#include;

#include;

#include;

#include;

#include;

#include;

#include;

#include;

#include;

/*采用宏定义设置设备的主设备号*/

#define SECOND_MAJOR

/*静态的分别保存静态主设备号的变量*/

static int second_major = SECOND_MAJOR;

/*设备结构体,通常在设备中包含需要的设备,比如字符、块等类型*/

struct second_dev{

/*添加设备类型,

我认为可以采用一个联合体,

包含块设备或者字符设备,类似inode的实现方法,这样可以提高结构体的通用性

*/

struct cdev cdev;

/*原子变量,用来统计*/

atomic_t counter;

/*添加内核定时器结构体变量*/

struct timer_list s_timer;

/*用于动态创建设备文件的设备类*/

struct class *myclass;

};

/*结构体指针或者采用全局变量直接定义结构都可以*/

struct second_dev *second_devp;

/*如果定时时间到了,定时器的处理函数*/

static void second_timer_handler(unsigned long arg)

{

/*

修改定时器中的到期时间,增加时间为1s,

需要注意的是mod_timer函数是重新注册定时器到

内核

而不管定时器是否被运行过

*/

mod_timer(&second_devp->;s_timer,jiffies + HZ); /*原子变量的增加*/

atomic_inc(&second_devp->;counter);

/*输出jiffies值*/

printk(KERN_NOTICE "Current jiffies

is %d\n",jiffies);

}

/*open函数实现*/

static int second_open(struct inode

*inode,struct file *filp)

{

/*初始化定义的内核定时器*/

init_timer(&second_devp->;s_timer);

/*指定内核定时器的处理函数是上面定义好的函数*/

second_devp->;s_timer.function =

second_timer_handler;

/*指定定时间隔是1s*/

second_devp->;s_timer.expires = jiffies + HZ; /*将定时器添加到内核*/

add_timer(&second_devp->;s_timer);

/*同时设备相关的统计值为0*/

atomic_set(&second_devp->;counter,0);

return 0;

}

/*release函数的实现*/

static int second_release(struct inode

*inode,struct file *filp)

{

/*如果没有到时间就关闭设备,直接删除定时器*/

del_timer(&second_devp->;s_timer);

return 0;

}

/*read函数的实现*/

static ssize_t second_read(struct file

*filp,char __user *buf,size_t count,loff_t *ppos) {

int counter;

/*读当前的值*/

counter = atomic_read(&second_devp->;counter); /*

采用put_user实现数值的传送

put_user函数存在对指针变量的检查,

因此不需要检测指针是否正确

*/

if(put_user(counter,(int *)buf))

return -EFAULT;

else

/*返回数据大小*/

return sizeof(unsigned int);

}

/*具体的文件操作集合*/

static const struct file_operations second_fops =

{

/*这是拥有者*/

.owner = THIS_MODULE,

.open = second_open,

.release = second_release,

.read = second_read,

};

/*初始化函数*/

static int __init second_init(void) {

int ret;

/*设备号的申请,创建*/

dev_t devno = MKDEV(second_major,0); /*静态申请设备号*/

if(second_major)

{

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网卡驱动的配置

RedHat5.1下安装Realtek8168网卡驱动 1.先识别机器上的网卡型号: [root@localhost kernel]#kudzu --probe --class=network 2、使用命令查看kernel包是否都装全了,具体如下: [root@localhost kernel]# rpm -qa | grep kernel 如果装全了,会显示如下几个包: kernel-xen-devel-2.6.18-8.el5 kernel-devel-2.6.18-8.el5 kernel-2.6.18-8.el5 kernel-xen-2.6.18-8.el5 kernel-headers-2.6.18-8.el5 如果没有装全,就在redhat enterprise Linux 5.0第一个ISO的Server文件目录下可以找到对应的包,拷贝到某一具体目录下,进入目录后,用如下命令就可以进行安装: [root@localhost kernel]# rpm -ivh kernel-devel-2.6.18-8.el5.x86_64.rpm

注意:如果不装全这些包,在网卡编译时就会报错说找不到src目录文件。 用如下命令查看GCC是否安装,通常都装上的,还是检查一下比较好。 [root@localhost 2.6.18-8.el5]# whereis -b gcc gcc : /usr/bin/gcc /usr/lib/gcc /usr/libexec/gcc 3、将r8168-8.aaa.bb.tar.bz2解压出来变成r8168-8.aaa.bb文件夹形式,拷到U盘 在redhat下挂载U盘: 插入U盘 [root@localhost kernel]#mount /dev/sdb1 /mnt/usb 4.将r8168-8.aaa.bb文件夹拷都本地,如/home目录下 首先将u盘中的驱动程序包拷贝到/home目录下 [root@localhost media]# cp r8168-8\[1\].011.00.tar.bz2 /home/ [root@localhost media]# cd /home/ [root@localhost home]# ls r8168-8[1].011.00.tar.bz2 5.其次解压驱动程序包 [root@localhost home]# tar -vjxf r8168-8\[1\].011.00.tar.bz2

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下安装编译网卡驱动的方法

linux下安装编译网卡驱动的方法 你还在为不知道linux下安装编译网卡驱动的方法而不知所措么?下面来是小编为大家收集的linux下安装编译网卡驱动的方法,欢迎大家阅读: linux下安装编译网卡驱动的方法 安装linux操作系统后发现没有网卡驱动,表现为 system → Administration → Network下Hardware列表为空。 以下为安装编译网卡驱动的过程,本人是菜鸟,以下是我从网上找的资料进行整理,并实际操作的过程,仅供借鉴。 一.检测linux系统内核版本和网卡类型,相关命令如下: uname -r 查看linux内核版本(uname -a 可显示所有信息)

lsmod 设备加载情况 ls /usr/share/hwdata 查看硬件设备 lspci 查看pci网卡设备ethernet controller 厂商和型号,modprobe **** ****为网卡型号,例如modprobe RTL8101E ,如果出错,说明模块不存在,该型号不识别 我在这一步时查找不到网卡型号,无奈只能由同时采购的其他相同型号预装win7的电脑下查看网卡型号,是个笨办法,嘿嘿…… 找到网卡型号后就到驱动之家下载了相应网卡的linux驱动,这些需要根据自己的实际情况下载,不多说了,重点是后面。 二.下载网卡驱动 Intel_e1000e-1.9.5.zip 为我下载的所需的网卡驱动,这个在linux下需自己编译. 三.安装网卡驱动

1.检测编译需要用到内核的源代码包和编译程序gcc。所以如果没有的话,要先装。 [root@localhost ~]# rpm -qa|grep kernel kernel-xen-2.6.18-8.el5 kernel-xen-devel-2.6.18-8.el5 kernel-headers-2.6.18-8.el5 [root@localhost ~]# rpm -qa|grep gcc gcc-c++-4.1.1-52.el5 libgcc-4.1.1-52.el5 gcc-4.1.1-52.el5 gcc-gfortran-4.1.1-52.el5 如果缺少kernel-xen-devel-2.6.18-8.el5,可以去安装光

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系统下如何安装网卡驱动

2011年研发二部工作 周报月报 作者:赵玉武 时间:2012-6-13

目录 一、整理漏扫系统的结构 (1) 1、整理NetScan目录中的程序。 (1) 2、整理proftpd目录中内容(插件的检测信息)。 (4) 3、整理proxyd目录中的安管(安管平台)。 (8) 二、熟悉Nessus的工作原理 (8) 1、Nessus扫描引擎的工作原理... 错误!未定义书签。 三、整理运行的整个流程.............. 错误!未定义书签。 1、通过客户端下发策略,上传到服务器上。错误!未定 义书签。 2、服务端:接收客户端下发的策略。错误!未定义书签。 3、服务端进行身份的认证....... 错误!未定义书签。

一、整理漏扫系统的结构 网卡是Linux服务器中最重要网络设备。据统计,Linux网络故障有35%在物理层、25%在数据链路层、10%在网络层、10%在传输层、10%在对话、7%在表示层、3%在应用层。由此可以看出,网络故障通常发生在网络七层模型的下三层,即物理层、链路层和网络层。对应于实际网络也就是使用的网络线缆、网卡、交换机、路由器等设备故障。Linux的网络实现是模仿FreeBSD的,它支持FreeBSD 的带有扩展的Sockets(套接字)和TCP/IP协议。它支持两个主机间的网络连接和Sockets通讯模型,实现了两种类型的Sockets:BSD Sockets和INET Sockets。它为不同的通信模型和服务质量提供了两种传输协议,即不可靠的、基于消息的UDP传输协议和可靠的、基于流的传输协议TCP,并且都是在IP网络协议上实现的。INET sockets 是在以上两个协议及IP协议之上实现的。 由于交换机、路由器通常独立于Linux或者其他操作系统。网卡设置故障是造成Linux 服务器故障最主要原因。可能因为硬件的质量或性能、磨损老化、人为误操作、不正确的网络设置、管理问题、Linux软件的BUG、系统受到黑客攻击和Linux病毒等原因造成。 Linux 服务器网卡故障排除的思路是:应当遵循先硬件后软件的方法。因为硬件如果出现物理损坏那么如何设定网卡都不能解决故障。解决问题的方法可以从自身Linux计算机的网卡查起,如果确定硬件没有问题了,再来考虑软件的设定。 1、网卡的选择 一般来说,2.4版本以后的 Linux可以支持的网卡芯片组数量已经很完备了,包括著名厂商如:Intel 以及使用广泛的 RealTek, Via 等网卡芯片都已经被支持,所以使用者可以很轻易的设定好他们的网

linux驱动程序的编写

linux驱动程序的编写 一、实验目的 1.掌握linux驱动程序的编写方法 2.掌握驱动程序动态模块的调试方法 3.掌握驱动程序填加到内核的方法 二、实验内容 1. 学习linux驱动程序的编写流程 2. 学习驱动程序动态模块的调试方法 3. 学习驱动程序填加到内核的流程 三、实验设备 PentiumII以上的PC机,LINUX操作系统,EL-ARM860实验箱 四、linux的驱动程序的编写 嵌入式应用对成本和实时性比较敏感,而对linux的应用主要体现在对硬件的驱动程序的编写和上层应用程序的开发上。 嵌入式linux驱动程序的基本结构和标准Linux的结构基本一致,也支持模块化模式,所以,大部分驱动程序编成模块化形式,而且,要求可以在不同的体系结构上安装。linux是可以支持模块化模式的,但由于嵌入式应用是针对具体的应用,所以,一般不采用该模式,而是把驱动程序直接编译进内核之中。但是这种模式是调试驱动模块的极佳方法。 系统调用是操作系统内核和应用程序之间的接口,设备驱动程序是操作系统内核和机器硬件之间的接口。设备驱动程序为应用程序屏蔽了硬件的细节,这样在应用程序看来,硬件设备只是一个设备文件,应用程序可以像操作普通文件一样对硬件设备进行操作。同时,设备驱动程序是内核的一部分,它完成以下的功能:对设备初始化和释放;把数据从内核传送到硬件和从硬件读取数据;读取应用程序传送给设备文件的数据和回送应用程序请求的数据;检测和处理设备出现的错误。在linux操作系统下有字符设备和块设备,网络设备三类主要的设备文件类型。 字符设备和块设备的主要区别是:在对字符设备发出读写请求时,实际的硬件I/O一般就紧接着发生了;块设备利用一块系统内存作为缓冲区,当用户进程对设备请求满足用户要求时,就返回请求的数据。块设备是主要针对磁盘等慢速设备设计的,以免耗费过多的CPU时间来等待。 1 字符设备驱动结构 Linux字符设备驱动的关键数据结构是cdev和file_operations结构体。

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.360docs.net/doc/c412661404.html,_sec=2;value.it_https://www.360docs.net/doc/c412661404.html,_usec=500000;value.it_https://www.360docs.net/doc/c412661404.html,_sec=2;value.it_i https://www.360docs.net/doc/c412661404.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.360docs.net/doc/c412661404.html,_sec=1;value.it_https://www.360docs.net/doc/c412661404.html,_usec=0;value.it_https://www.360docs.net/doc/c412661404.html,_sec=3;value.it_interva https://www.360docs.net/doc/c412661404.html,_usec=0;setitimer(ITIMER_VIRT, &value, NULL);例3. 取消一个ITIMER_PROF类定时器。答:将itimerval结构的成员it_value均赋值为0秒即可:struct itimerval value;value.it_https://www.360docs.net/doc/c412661404.html,_sec=1;value.it_https://www.360docs.net/doc/c412661404.html,_usec=0;setitimer(ITIMER_PROF, &value, NULL);例4. 设置一个定时1.5秒的真实时间定时器,它仅发送一次信号就自动取消。答:将itimerval结构的成员it_value均赋值为1.5秒,成员it_interval赋值为0秒即可:struct

Linux下网卡驱动程序

Linux下8019网卡驱动程序 福建鑫诺通信有限公司陈光平(chenggp_fj@https://www.360docs.net/doc/c412661404.html,) 本文以S3C44B0的CPU为例,详细解析了linux下RTL8019网卡驱动程序工作原理,其间知识大多来源互联网络,特别是浙大潘纲的论文,在此不一一列出,此文目的只是让嵌入式linux爱好者得到更多网卡驱动的资料,并获得交流机会,不足之处请指正 (一)、硬件相关部份 1、CPU与网卡的连接方式 (s3c44B0 CPU) (RTL 8019网卡) CPU与网卡接线图 上图为S3c44b0CPU和网卡的接线图,此接法并非固定,如接法不同,则牵涉到很多相应的改动,下面会详细分析不同之处 从硬件部门得到:网卡在CPU的存储空间上接BANK4,即0x08000000(看44B0手册)外部中断号为:EXTINT3 (irqs.h文件获得值为22) 上面两个值可以查CPU手册,或询问硬件设计人员 由上图可以知道以下数据: (1)、网卡与CPU地址线连接错开8位(A0接A8) (2)、总共连线,其实4根就足够用了,因为每根线可以译码4个地址空间,总共是16个地址空间,每个地址空间对应一个寄存器地址,而8019总共就是16个寄存器(3)、一般是跳线模式,不使用9346芯片 1-1 基地址算法 首先8019的基地址是300H(见RTL8019芯片资料:选择IO总线地址),但是有些硬件已在芯片中做过了偏移,比如我们的网卡已做了处理,基址已偏移到0x08000000, 那么因为网卡A0接CPU的A8,表示基地址左移8位,下一个寄存器reg0的地址就是:0x08000100(0000,0000,0001 0001,0000,0000) 还不理解的话我们看另一种接法:

LINUX内核时钟中断机制

Linux内核的时钟中断机制 opyright © 2003 by 詹荣开 E-mail:zhanrk@https://www.360docs.net/doc/c412661404.html, Linux-2.4.0 Version 1.0.0,2003-2-14 摘要:本文主要从内核实现的角度分析了Linux 2.4.0内核的时钟中断、内核对时间的表示等。本文是为那些想要了解Linux I/O子系统的读者和Linux驱动程序开发人员而写的。 关键词:Linux、时钟、定时器 申明:这份文档是按照自由软件开放源代码的精神发布的,任何人可以免费获得、使用和重新发布,但是你没有限制别人重新发布你发布内容的权利。发布本文的目的是希望它能对读者有用,但没有任何担保,甚至没有适合特定目的的隐含的担保。更详细的情况请参阅GNU 通用公共许可证(GPL),以及GNU自由文档协议(GFDL)。 你应该已经和文档一起收到一份GNU通用公共许可证(GPL)的副本。如果还没有,写信给:The Free Software Foundation, Inc., 675 Mass Ave, Cambridge,MA02139, USA 欢迎各位指出文档中的错误与疑问。 前言 时间在一个操作系统内核中占据着重要的地位,它是驱动一个OS内核运行的“起博器”。一般说来,内核主要需要两种类型的时间: (1)、在内核运行期间持续记录当前的时间与日期,以便内核对某些对象和事件作时间标记(timestamp,也称为“时间戳”),或供用户通过时间syscall进行检索。 (2)、维持一个固定周期的定时器,以提醒内核或用户一段时间已经过去了。 PC机中的时间是有三种时钟硬件提供的,而这些时钟硬件又都基于固定频率的晶体振荡

Linux下查看网卡驱动和版本信息

Linux下查看网卡驱动和版本信息 查看网卡生产厂商和信号 查看基本信息:lspci 查看详细信息:lspci -vvv # 3个小写的v 查看网卡信息:lspci | grep Ethernet 查看网卡驱动 查看网卡驱动信息:lspci -vvv # 找到网卡设备的详细信息,包括网卡驱动 # lsmod 列出加载的所有驱动,包括网卡驱动 查看网卡驱动版本 查看模块信息:modifo # 其中包含version信息 或# ethtool-i RHEL 6.3中的网卡驱动版本: # modinfo igb filename: /lib/modules/2.6.32-279.el6.x86_64/kernel/drivers/net/igb/igb.ko version: 3.2.10-k license: GPL description: Intel(R) Gigabit Ethernet Network Driver # modinfo e1000e filename: /lib/modules/2.6.32-279.el6.x86_64/kernel/drivers/net/e1000e/e1000e.ko version: 1.9.5-k license: GPL

description: Intel(R) PRO/1000 Network Driver author: Intel Corporation, # modinfo e1000 filename: /lib/modules/2.6.32-279.el6.x86_64/kernel/drivers/net/e1000/e1000.ko version: 8.0.35-NAPI license: GPL description: Intel(R) PRO/1000 Network Driver # modinfo ixgbe filename: /lib/modules/2.6.32-279.el6.x86_64/kernel/drivers/net/ixgbe/ixgbe.ko version: 3.6.7-k license: GPL description: Intel(R) 10 Gigabit PCI Express NetworkDriver # modinfo r8169 filename: /lib/modules/2.6.32-279.el6.x86_64/kernel/drivers/net/r8169.ko version: 2.3LK-NAPI license: GPL description: RealTek RTL-8169 Gigabit Ethernet driver 查看网络接口队列数 查看网卡接口的中断信息:#cat /proc/interrupts | grep eth0 或# ethtool-S eth0 查看网卡驱动源码的版本号 解压Intel网卡驱动源码,打开解压缩目录下的*.spec文件查看驱动的版本。 例如:解压e1000-8.0.35.tar.gz网卡驱动后,查看e1000.spec文件。

内核2.6.X中的时钟与定时器

内核2.6.X中的时钟与定时器 2007年07月11日星期三 15:47 时钟和定时器对Linux内核来说十分重要。首先内核要管理系统的运行时间(uptime)和当前墙上时间(wall time),即当前实际时间。其次,内核中大量的活动由时间驱动(time driven)。其中一些活动是周期性的,比如调度调度器(scheduler)中的运行队列(runqueue)或者刷新屏幕这样的活动,它们以固有的频率定时发生;同时,内核要非周期性地调度某些函数在未来某个时间发生,比如推迟执行的磁盘I/O操作等。 实时时钟 --------------------------------------------------------- 内核必须借助硬件来实现时间管理。实时时钟(real time clock)是用来持久存放系统时间的设备,它与CMOS集成在一起,并通过主板电池供电,所以即便在关闭计算机系统之后,实时时钟仍然能继续工作。 系统启动时,内核读取实时时钟,将所读的时间存放在变量xtime 中作为墙上时间(wall time),xtime保存着从1970年1月1日0:00 到当前时刻所经历的秒数。虽然在Intel x86机器上,内核会周期性地将当前时间存回实时时钟中,但应该明确,实时时钟的主要作用就是在启动时初始化墙上时间xtime。

系统定时器与动态定时器 --------------------------------------------------------- 周期性发生的事件都是由系统定时器(system timer)驱动。在X86体系结构上,系统定时器通常是一种可编程硬件芯片(如8254 CMOS芯片),又称可编程间隔定时器(PIT, Programmable Interval Timer),其产生的中断就是时钟中断(timer interrupt)。时钟中断对应的处理程序负责更新系统时间和执行周期性运行的任务。系统定时器的频率称为节拍率(tick rate),在内核中表示为HZ。 以X86为例,在2.4之前的内核中其大小为100;从内核2.6开始,HZ = 1000,也就是说每秒时钟中断发生1000次。这一变化使得系统定时器的精度(resolution)由10ms提高到1ms,这大大提高了系统对于时间驱动事件调度的精确性。过于频繁的时钟中断不可避免地增加了系统开销(overhead),但是总的来说,在现在计算机系统上,HZ = 1000不会导致难以接受的系统开销。 与系统定时器相对的是动态定时器(dynamic timer),它是调度事件(执行调度程序)在未来某个时刻发生的时机。内核可以动态地创建或销毁动态定时器。 系统定时器及其中断处理程序是内核管理机制的中枢,下面是一些利用系统定时器周期执行的工作(中断处理程序所做的工作): (1) 更新系统运行时间(uptime) (2) 更新当前墙上时间(wall time)

Linux驱动框架及驱动加载

本讲主要概述Linux设备驱动框架、驱动程序的配置文件及常用的加载驱动程序的方法;并且介绍Red Hat Linux安装程序是如何加载驱动的,通过了解这个过程,我们可以自己将驱动程序放到引导盘中;安装完系统后,使用kudzu自动配置硬件程序。 Linux设备驱动概述 1. 内核和驱动模块 操作系统是通过各种驱动程序来驾驭硬件设备,它为用户屏蔽了各种各样的设备,驱动硬件是操作系统最基本的功能,并且提供统一的操作方式。正如我们查看屏幕上的文档时,不用去管到底使用nVIDIA芯片,还是ATI芯片的显示卡,只需知道输入命令后,需要的文字就显示在屏幕上。硬件驱动程序是操作系统最基本的组成部分,在Linux内核源程序中也占有较高的比例。 Linux内核中采用可加载的模块化设计(LKMs ,Loadable Kernel Modules),一般情况下编译的Linux内核是支持可插入式模块的,也就是将最基本的核心代码编译在内核中,其它的代码可以选择是在内核中,或者编译为内核的模块文件。 如果需要某种功能,比如需要访问一个NTFS分区,就加载相应的NTFS模块。这种设计可以使内核文件不至于太大,但是又可以支持很多的功能,必要时动态地加载。这是一种跟微内核设计不太一样,但却是切实可行的内核设计方案。 我们常见的驱动程序就是作为内核模块动态加载的,比如声卡驱动和网卡驱动等,而Linux最基础的驱动,如CPU、PCI总线、TCP/IP协议、APM(高级电源管理)、VFS等驱动程序则编译在内核文件中。有时也把内核模块就叫做驱动程序,只不过驱动的内容不一定是硬件罢了,比如ext3文件系统的驱动。 理解这一点很重要。因此,加载驱动时就是加载内核模块。下面来看一下有关模块的命令,在加载驱动程序要用到它们:lsmod、modprob、insmod、rmmod、modinfo。 lsmod

linux 内核定时器 timer_list

linux 内核定时器timer_list linux 内核使用timer_list 结构体当作定时器。#include “linux/timer.h” #include “linux/module.h”MODULE_LICENSE(“GPL”);//不加这句话,虽然不影响功能,但有时候程序执行时会打印错误,类似Disabling lock debugging //due to kernel taint 之类的话struct timer_list tm;static int num;static void func(){ num++; mod_timer(&tm,jiffies+1*HZ); //timer 一旦超时,就 会执行fuc 函数,然后永远的休眠,//所以如果没有这mod_timer,hello world 只会执行一次,也就是timer 第一次超时时执行的那次。//mod_timer 可以激活timer。如果你没有add_timer(),激活也没用printk(“hello,world\n ,%d”,num);}static int timer_init(void){ init_timer(&tm); //初始化定时 器,必须在所有下面复制操作前进行定时器初始化tm.expires = jiffies +1*HZ; //超时1 秒,执行function tm.function = func; //超时后执行的函数add_timer(&tm); //将定时器加入定时器等待队列中return 0;}static void timer_destory(void){ del_timer(&tm); printk(“remove timer\n”);}tips:感谢大家 的阅读,本文由我司收集整编。仅供参阅!

linux下安装无线网卡驱动

换了linux系统后发现无线不能使用,很是苦恼,于是在网上找到各位大神的做法,终于成功安装。下面给大家介绍一下。本例以安装Fedora 14无线网卡驱动为例。参考文章 https://www.360docs.net/doc/c412661404.html,/Linux/2011-05/35366.htm。 首先在https://www.360docs.net/doc/c412661404.html,/support/802.11/linux_sta.php 上下载32位的驱动, 下载完成之后进入到下载的文件夹下 #cd ~/Downloads 按照readme的做法,新建一个文件夹,并且将该压缩文件移动到新建的文件夹当中 #mkdri hybrid_wl #mv hybrid-portsrc-x86_32-v5.60.48.36.tar.gz hybrid_wl/ 进入文件夹,进行解压, #cd hybrid_wl/ #tar -zxvf hybrid-portsrc-x86_32-v5.60.48.36.tar.gz 由于自述文件当中的做法是指定解压目录,因此为了保险,再把这个压缩文件移动到上一级目录 #mv hybrid-portsrc-x86_32-v5.60.48.36.tar.gz ../ 根据自述文件的说法(The cards with the following PCI Device IDs are supported with this driver.),查看支持的版本 # lspci -n | grep 14e4 0c:00.0 0280: 14e4:4315 (rev 01) BRCM PCI PCI DellProduct Name Vendor ID Device ID Product ID ------------- ---------- --------- ----------- 4311 2.4 Ghz 0x14e4 0x4311 Dell 1390 4311 Dualband 0x14e4 0x4312 Dell 1490

Linux网卡驱动程序详解

当网络上一台计算机准备发送数据时,他的网卡开始工作了,首先网卡的芯片侦听在网络上是否有数据在流动,如果没有,他就把数据发送到网络上,在侦听和发送之间有一段极小的时间延迟,在这段时间内,也有可能在网络上有其他的计算机也准备发送数据,也侦听到网络上没有数据在流动,这就可能两台甚至多台的数据一起发送到网络上,产生数据的碰撞,发送数据的计算机的网卡芯片当然要在发送完成后再校验返回的数据,如果发现和发送的数据不一致,那就是说产生了碰撞,所以在一个以太网络中的计算机数量不宜过多,他不但会增加广播包在网络中的数量,也请也会增加数据包的碰撞次数. 我们的计算机的网卡芯片在接收到一完整的数据包后,芯片的一引脚通知8259中断控制器,中断控制器再发出中断给CPU,由此,CPU随即调用该网卡的中断例程,如: DOS是这样的 屏蔽所有中断(cli) push any register 因为中断向量在段0 所以xor ax,ax mov ds,ax mul ax,中断号 那么在数据段的[ax]偏移处是该中断例程的指针了call [ax]就到该中断例程了...(DOS是比较遥远的事情了,我所描述的是他的原理,当然不会这么简单,如果那位网友有兴趣详细描述一下上面的原理,纠正或替换掉我所写的就感激不尽了) 总之,在本例程中,CPU将调用elintr中断例程,并带有参数unit即该种网卡的第几块(因为在计算机中,你有可能装了相同的网卡有几块),elintr的作用是把数据从网卡的数据存储器中读到我们在该网卡初始化时预先分配好的数据缓冲区中,他调用的函数就只有elread,同样elread也只调用了elget一个函数.elread函数比较简单,就是调用elget,elget则相对比较复杂一点,涉及到核心内存分配mbuf,mbuf是比较恐怖的东西,正如STEVEN所写的,为了节约当时"巨大"的4M内存,牺牲了性能搞出了这个mbuf东东,mbuf是必须要弄懂的,虽然在设备驱动程序中调用他的宏和函数不多,但在后面的IP协议,TCP协议中有不少涉及的地方. 关于数据发送方面和接收差不多,在上层协议放置好数据到mbuf链后,调用el_start函数,该函数把mbuf链中的数据放置到本块网卡的发送队列缓冲el_pktbuf中,然后再调用el_xmit 函数,此函数把发送队列缓冲el_pktbuf中的数据有传递到网卡的数据存储器中.我认为,这中间的内存拷贝是多于的,应该在el_start函数中直接把mbuf中的数据传递到网卡的数据存储器中,这样会使性能有较大幅度的提升,因为在驱动程序设计时,最好减少大量的内存拷贝,他占用的时间太多了. */ /* FreeBSD的3COM以太网设备驱动程序*/ /*本段头文件是在编译核心时产生的*/ #include "el.h" /*此三文件为编译时产生的头文件,内容是定制核心的一些常量*/ #include "opt_inet.h"

相关文档
最新文档