linux设备驱动学习10
嵌入式Linux驱动开发教程PDF

嵌入式Linux驱动开发教程PDF嵌入式Linux驱动开发教程是一本非常重要和实用的教材,它主要介绍了如何在Linux操作系统上开发嵌入式硬件设备的驱动程序。
嵌入式系统是指将计算机系统集成到其他设备或系统中的特定应用领域中。
嵌入式设备的驱动程序是连接操作系统和硬件设备的关键接口,所以对于嵌入式Linux驱动开发的学习和理解非常重要。
嵌入式Linux驱动开发教程通常包括以下几个主要的内容:1. Linux驱动程序的基础知识:介绍了Linux设备模型、Linux内核模块、字符设备驱动、块设备驱动等基本概念和原理。
2. Linux驱动编程的基本步骤:讲解了如何编译和加载Linux内核模块,以及编写和注册设备驱动程序所需的基本代码。
3. 设备驱动的数据传输和操作:阐述了如何通过驱动程序与硬件设备进行数据的传输和操作,包括读写寄存器、中断处理以及与其他设备的通信等。
4. 设备驱动的调试和测试:介绍了常用的驱动调试和测试技术,包括使用调试器进行驱动程序的调试、使用模拟器进行驱动程序的测试、使用硬件调试工具进行硬件和驱动的联合调试等。
通常,嵌入式Linux驱动开发教程的PDF版本会提供示例代码、实验步骤和详细的说明,以帮助读者更好地理解和掌握嵌入式Linux驱动开发的核心技术和要点。
读者可以通过跟随教程中的示例代码进行实际操作和实验,深入了解和体验嵌入式Linux驱动开发的过程和方法。
总之,嵌入式Linux驱动开发教程是一本非常重要和实用的教材,对于想要在嵌入式领域从事驱动开发工作的人员来说,具有非常重要的指导作用。
通过学习嵌入式Linux驱动开发教程,读者可以系统地了解和学习嵌入式Linux驱动开发的基本原理和技术,提高自己在嵌入式Linux驱动开发方面的能力和水平。
Linux设备驱动之Ioctl控制

Linux设备驱动之Ioctl控制 ⼤部分驱动除了需要具备读写设备的能⼒之外,还需要具备对硬件控制的能⼒。
⼀、在⽤户空间,使⽤ioctl系统调⽤来控制设备,原型如下:int ioctl(int fd,unsigned long cmd,...);/*fd:⽂件描述符cmd:控制命令...:可选参数:插⼊*argp,具体内容依赖于cmd*/ ⽤户程序所作的只是通过命令码告诉驱动程序它想做什么,⾄于怎么解释这些命令和怎么实现这些命令,这都是驱动程序要做的事情。
⼆、驱动ioctl⽅法:int (*ioctl) (struct inode *inode,struct file *filp,unsigned int cmd,unsigned long arg);/*inode与filp两个指针对应于应⽤程序传递的⽂件描述符fd,这和传递open⽅法的参数⼀样。
cmd 由⽤户空间直接不经修改的传递给驱动程序arg 可选。
*/ 在驱动程序中实现的ioctl函数体内,实际上是有⼀个switch {case}结构,每⼀个case对应⼀个命令码,做出⼀些相应的操作。
怎么实现这些操作,这是每⼀个程序员⾃⼰的事情,因为设备都是特定的。
关键在于怎么样组织命令码,因为在ioctl中命令码是唯⼀联系⽤户程序命令和驱动程序⽀持的途径。
在Linux核⼼中是这样定义⼀个命令码的:____________________________________| 设备类型 | 序列号 | ⽅向 | 数据尺⼨ ||----------|--------|------|-------- || 8 bit | 8 bit | 2 bit |8~14 bit||----------|--------|------|-------- | 这样⼀来,⼀个命令就变成了⼀个整数形式的命令码。
但是命令码⾮常的不直观,所以Linux Kernel中提供了⼀些宏,这些宏可根据便于理解的字符串⽣成命令码,或者是从命令码得到⼀些⽤户可以理解的字符串以标明这个命令对应的设备类型、设备序列号、数据传送⽅向和数据传输尺⼨。
Linux设备驱动程序原理及框架-内核模块入门篇

Linux设备驱动程序原理及框架-内核模块入门篇内核模块介绍应用层加载模块操作过程内核如何支持可安装模块内核提供的接口及作用模块实例内核模块内核模块介绍Linux采用的是整体式的内核结构,这种结构采用的是整体式的内核结构,采用的是整体式的内核结构的内核一般不能动态的增加新的功能。
为此,的内核一般不能动态的增加新的功能。
为此,Linux提供了一种全新的机制,叫(可安装) 提供了一种全新的机制,可安装) 提供了一种全新的机制模块” )。
利用这个机制“模块”(module)。
利用这个机制,可以)。
利用这个机制,根据需要,根据需要,在不必对内核重新编译链接的条件将可安装模块动态的插入运行中的内核,下,将可安装模块动态的插入运行中的内核,成为内核的一个有机组成部分;成为内核的一个有机组成部分;或者从内核移走已经安装的模块。
正是这种机制,走已经安装的模块。
正是这种机制,使得内核的内存映像保持最小,的内存映像保持最小,但却具有很大的灵活性和可扩充性。
和可扩充性。
内核模块内核模块介绍可安装模块是可以在系统运行时动态地安装和卸载的内核软件。
严格来说,卸载的内核软件。
严格来说,这种软件的作用并不限于设备驱动,并不限于设备驱动,例如有些文件系统就是以可安装模块的形式实现的。
但是,另一方面,可安装模块的形式实现的。
但是,另一方面,它主要用来实现设备驱动程序或者与设备驱动密切相关的部分(如文件系统等)。
密切相关的部分(如文件系统等)。
课程内容内核模块介绍应用层加载模块操作过程内核如何支持可安装模块内核提供的接口及作用模块实例内核模块应用层加载模块操作过程内核引导的过程中,会识别出所有已经安装的硬件设备,内核引导的过程中,会识别出所有已经安装的硬件设备,并且创建好该系统中的硬件设备的列表树:文件系统。
且创建好该系统中的硬件设备的列表树:/sys 文件系统。
(udev 服务就是通过读取该文件系统内容来创建必要的设备文件的。
)。
Linux下的硬件驱动——USB设备

Linux下的硬件驱动——USB设备什么是USB设备?USB即Universal Serial Bus,翻译过来就是通用串行总线。
它是一种规范化的、快速的、热插拔的串行输入/输出接口。
USB接口常被用于连接鼠标、键盘、打印机、扫描仪、音频设备、存储设备等外围设备。
Linux下的USB驱动在Linux系统中,每个USB设备都需要一个相应的驱动程序来驱动。
从Linux 2.4开始,内核提供了完整的USB设备支持。
对于每个USB设备,内核都会自动加载对应的驱动程序。
Linux下的USB设备驱动程序主要分为以下几个部分:USB核心驱动程序USB核心驱动程序是操作系统内核中处理USB设备的核心模块,负责与各种类型的USB设备进行通信,包括主机控制器、USB总线、USB设备等。
它与驱动程序和应用程序之间起到了桥梁的作用,为驱动程序提供了USB设备的基础支持。
USB设备驱动程序USB设备驱动程序是与特定USB设备相对应的驱动程序,为USB设备提供具体的读写功能和其他控制功能。
USB核心驱动程序和USB设备驱动程序之间的接口USB核心驱动程序和USB设备驱动程序之间的接口是指USB层和应用程序层之间的接口,负责传递各种USB操作的命令和数据。
如何编译一个USB设备驱动编译一个USB设备驱动程序需要按照以下步骤进行:步骤一:安装必要的软件包首先需要安装编译和调试USB设备驱动所需的软件包,包括编译工具链、内核源代码、内核头文件等。
sudo apt-get install build-essential linux-source linux-headers-`una me -r`步骤二:编写代码现在可以编写USB设备驱动程序的代码,此处不做详细介绍。
步骤三:编译代码在终端窗口中进入USB设备驱动程序所在的目录下,输入以下命令进行编译:make此命令将会编译USB设备驱动程序,并生成一个将驱动程序与内核进行连接的模块文件。
linux驱动模型

我们将详细的介绍Linux的设备驱动模型。
Linux设备驱动模型是一个相当复杂的系统,对于初学者来说真有些无从入手。
而且更加困难的是,随着新的Linux Kernel的release,Linux 的设备驱动模型总会有或大或小的变化,我们将尽量展现Linux Kernel 的这种变化。
早期的Linux内核(版本2.4之前)并没有实现一个统一的设备模型,设备节点的创建一般是mknod命令手动创建或利用devfs文件系统创建。
早期的Linux发行版一般会采用手动创建的方式预先把通常用到的节点都创建出来,而嵌入式系统则会采用devfs的方式。
起初Linux 2.6 内核还支持devfs,但从2.6.18开始,内核完全移除了devfs系统而采用的udev的方式动态的创建设备节点。
因此,新的Linux发行版都采用udev的方式管理设备节点文件。
Linux2.6设备驱动模型的基本元素是Class、Bus、Device、Driver,下面我们分别介绍各个部分。
Class 和Class Device驱动模型最基本的概念是设备及其类别,Linux中使用struct class 和struct class_device来管理不同类别的设备。
由于设备驱动模型是一个复杂的系统,我们还是从一个简单的例子开始介绍,然后在逐步展开。
其实实现设备节点的动态创建是一个很简单的事情,并不需要太多的代码。
我们修改我们的驱动初始化函数如下:#include <linux/device.h>#define DEVNAME "hello"static dev_t dev;static struct class *hello_c lass;static struct cdev *hello_cdev;static int __init hello_init(void){int error;error = alloc_chrdev_region(&dev, 0, 2, "hello");if (error){printk("hello: alloc_chardev_region failed! ");goto out;}hello_cdev = cdev_alloc();if (hello_cdev == NULL){printk("hello: alloc cdev failed! ");error = -ENOMEM;goto out_chrdev;}hello_cdev->ops = &hello_fops;hello_cdev->owner = THIS_MODULE;error = cdev_add(hello_cdev, dev, 1);if (error){printk("hello: cdev_add failed! ");goto out_cdev;}hello_class = class_create(THIS_MODULE, DEVNAME);if (IS_ERR(hello_class)){error = PTR_ERR(hello_class);goto out_chrdev;}class_device_create(hello_class, NULL, dev, NULL, DEVNAME);memset (hello_buf, 0, sizeof(hello_buf));memcpy(hello_buf, DEFAULT_MSG, sizeof(DEFAULT_MSG));printk("hello: Hello World! ");return 0;out_cdev:cdev_del(hello_cdev);out_chrdev:unregister_chrdev_region(hello_cdev->dev, 2);out:return error;}static void __exit hello_exit(void){class_device_destroy(hello_class, dev);class_destroy(hello_class);unregister_chrdev_region(hello_cdev->dev, 2);cdev_del(hello_cdev);printk("hello: Goodbye World ");}重新编译这个驱动程序,当加载这个驱动到内核中时,系统(一般是hotplug和udev系统)就会自动的创建我们指定的设备名字:/dev/hello,同时,你也可以发现在sysfs系统中添加了新的文件:/sys/class/hello/hello/。
如何在Linux系统中安装驱动程序

如何在Linux系统中安装驱动程序Linux系统作为一个开源的操作系统,广泛应用于各种设备和领域。
而安装驱动程序是在Linux系统中使用外部硬件设备的关键步骤之一。
在本文中,我们将学习如何在Linux系统中安装驱动程序的方法和步骤。
1. 检查硬件设备在安装驱动程序之前,首先需要确定硬件设备的型号和制造商。
可以通过查询设备的型号或者查看设备的相关文档来获取这些信息。
这是非常重要的,因为不同的设备可能需要不同的驱动程序来正确地工作。
2. 更新系统在安装驱动程序之前,确保你的Linux系统已经是最新的状态。
可以通过在终端中运行以下命令来更新系统:```sudo apt-get updatesudo apt-get upgrade```更新系统可以确保你拥有最新的软件包和驱动程序,以获得更好的兼容性和性能。
3. 查找合适的驱动程序一般来说,大部分硬件设备的驱动程序都可以在Linux系统的软件仓库中找到。
可以通过使用包管理器(如apt、yum等)来查找并安装合适的驱动程序。
运行以下命令来搜索并安装特定的驱动程序:```sudo apt-cache search 驱动程序名称sudo apt-get install 驱动程序名称```注意替换“驱动程序名称”为具体的驱动程序名称。
安装驱动程序可能需要输入管理员密码和确认安装。
如果你无法在软件仓库中找到合适的驱动程序,可以转向设备的制造商网站或者开源社区来获取。
下载驱动程序后,根据驱动程序提供的文档和说明来安装。
4. 编译和安装驱动程序有些驱动程序可能需要手动编译和安装。
在这种情况下,你需要确保你的系统已经安装了编译工具(如GCC、make等)。
在终端中切换到驱动程序所在的目录,并按照以下步骤进行编译和安装:```./configuremakesudo make install```以上命令将分别进行配置、编译和安装驱动程序。
在进行安装之前,可能需要输入一些配置选项或者确认安装。
Linux设备驱动开发详解讲座
华清远见
自旋锁:
• •
自旋锁 VS 信号量
忙等待,无调度开销 进程抢占被禁止 锁定期间不能睡觉
spinlock_t lock; spin_lock_init(&lock);
• • •
spin_lock (&lock) ; // 获取自旋锁,保护临界区 . . ./ / 临界区 spin_unlock (&lock) ; // 解锁
信号量
• • • • • • •
拿不到就切换进程,有调度开销 锁定期间可以睡觉,不用于中断上下文
// 定义信号量 DECLARE_MUTEX(mount_sem); down(&mount_sem);// 获取信号量,保护临界区 . . . critical section // 临界区 . . . up(&mount_sem);// 释放信号量
•
•
•
•
•
•
•
•
signal ()绑定
用户空间
f c n t l( f d, F _ S E T O W N, g e t p i d( ) ) f c n t l( f d , F _GE T F L)
信号处理函数
执行 导致
信号
内核设置 filp>f_owner
设备驱动 fasync()函数
等待队列:进程等待被唤醒的一种机制 阻塞与非阻塞使用模板
阻塞非阻塞
华清远见
• • • • • • • • • • • • • • • • • • •
polling
驱动中 POLL 模板
1 static unsigned int xxx_poll(struct file *filp, poll_table *wait) 2 { 3 unsigned int mask = 0; 4 struct xxx_dev *dev = filp>private_data; /* 获得设备结构体指针 */ 6 ... 8 poll_wait(filp, &dev>wait, wait); 9 10 if (...)// 可读 11 { 12 mask |= POLLIN | POLLRDNORM; /* 标示数据可获得 */ 13 } 15 if (...)// 可写 16 { 17 mask |= POLLOUT | POLLWRNORM; /* 标示数据可写入 */ 18 } 19 20 ... 21 return mask; 22 }
Linux开发入门必读书单推荐给新手的经典书籍
Linux开发入门必读书单推荐给新手的经典书籍Linux操作系统作为一款开源的操作系统,越来越受到广大开发者的青睐。
对于想要从事Linux开发的新手来说,掌握一些经典的Linux开发技术是非常重要的。
下面是一份推荐给Linux开发新手的必读书单,这些经典书籍将帮助你打下坚实的基础,提升自己的技术水平。
1.《鸟哥的Linux私房菜》这本书是很多Linux爱好者的入门圣经。
作者详细介绍了Linux的基础知识、常用命令、系统管理和网络配置等内容。
通过学习这本书,你可以全面了解Linux的基本概念和用法,为后续学习打下坚实的基础。
2.《Linux内核设计与实现》Linux内核是Linux操作系统的核心,学习Linux内核的设计与实现对于深入理解Linux系统非常重要。
本书由著名的操作系统专家Robert Love撰写,详细介绍了Linux内核的基本结构、进程管理、内存管理、文件系统等内容。
通过深入学习内核原理,你将能够更好地理解Linux系统的运行机制。
3.《UNIX网络编程》网络编程是Linux开发中的关键技术之一。
这本经典著作由W. Richard Stevens所著,介绍了UNIX环境下网络编程的基本概念、套接字编程等内容。
通过学习这本书,你将深入了解网络编程的原理和实践技巧,为开发网络应用程序打下基础。
4.《Linux设备驱动程序》对于想要从事Linux设备驱动开发的新手来说,《Linux设备驱动程序》是一本不可或缺的书籍。
作者Jonathan Corbet、Alessandro Rubini和Greg Kroah-Hartman结合实际案例,详细介绍了Linux设备驱动的开发过程和驱动程序框架。
通过学习这本书,你将能够掌握Linux 设备驱动开发的核心技术。
5.《Linux性能优化指南》Linux性能优化是提高系统性能的重要手段。
这本书由Brendan Gregg和Jim Mauro合著,介绍了Linux性能调优的基本原理、工具和技术。
LINUX设备驱动开发详解
LINUX设备驱动开发详解概述LINUX设备驱动开发是一项非常重要的任务,它使得硬件设备能够与操作系统进行有效地交互。
本文将详细介绍LINUX设备驱动开发的基本概念、流程和常用工具,帮助读者了解设备驱动开发的要点和技巧。
设备驱动的基本概念设备驱动是连接硬件设备和操作系统的桥梁,它负责处理硬件设备的输入和输出,并提供相应的接口供操作系统调用。
设备驱动一般由设备驱动程序和设备配置信息组成。
设备驱动程序是编写解决设备驱动的代码,它负责完成设备初始化、IO操作、中断处理、设备状态管理等任务。
设备驱动程序一般由C语言编写,使用Linux内核提供的API函数进行开发。
设备配置信息是定义硬件设备的相关参数和寄存器配置的文件,它告诉操作系统如何与硬件设备进行交互。
设备配置信息一般以设备树或者直接编码在设备驱动程序中。
设备驱动的开发流程设备驱动的开发流程包括设备初始化、设备注册、设备操作函数编写和设备驱动注册等几个主要步骤。
下面将详细介绍这些步骤。
设备初始化设备初始化是设备驱动开发的第一步,它包括硬件初始化和内存分配两个主要任务。
硬件初始化是对硬件设备进行基本的初始化工作,包括寄存器配置、中断初始化等。
通过操作设备的寄存器,将设备设置为所需的状态。
内存分配是为设备驱动程序分配内存空间以便于执行。
在设备初始化阶段,通常需要为设备驱动程序分配一块连续的物理内存空间。
设备注册设备注册是将设备驱动程序与设备对象进行关联的过程,它使得操作系统能够正确地管理设备。
设备注册包括设备号分配、设备文件创建等操作。
设备号是设备在系统中的唯一标识符,通过设备号可以找到设备对象对应的设备驱动程序。
设备号分配通常由操作系统负责,设备驱动程序通过注册函数来获取设备号。
设备文件是用户通过应用程序访问设备的接口,它是操作系统中的一个特殊文件。
设备文件的创建需要通过设备号和驱动程序的注册函数来完成。
设备操作函数编写设备操作函数是设备驱动程序的核心部分,它包括设备打开、设备关闭、读和写等操作。
Linux设备驱动程序学习(10)-时间、延迟及延缓操作
Linux设备驱动程序学习(10)-时间、延迟及延缓操作Linux设备驱动程序学习(10)-时间、延迟及延缓操作度量时间差时钟中断由系统定时硬件以周期性的间隔产生,这个间隔由内核根据HZ 值来设定,HZ 是一个体系依赖的值,在<linux/param.h>中定义或该文件包含的某个子平台相关文件中。
作为通用的规则,即便如果知道HZ 的值,在编程时应当不依赖这个特定值,而始终使用HZ。
对于当前版本,我们应完全信任内核开发者,他们已经选择了最适合的HZ值,最好保持HZ 的默认值。
对用户空间,内核HZ几乎完全隐藏,用户HZ 始终扩展为100。
当用户空间程序包含param.h,且每个报告给用户空间的计数器都做了相应转换。
对用户来说确切的HZ 值只能通过/proc/interrupts 获得:/proc/interrup ts 的计数值除以/proc/uptime 中报告的系统运行时间。
对于ARM体系结构:在<linux/param.h>文件中的定义如下:也就是说:HZ 由__KERNEL__和CONFIG_HZ决定。
若未定义__KERNEL__,H Z为100;否则为CONFIG_H Z。
而CONFIG_HZ是在内核的根目录的.config文件中定义,并没有在make menuconfig的配置选项中出现。
Linux的\arch\arm\configs\s3c2410_defconfig文件中的定义为:所以正常情况下s3c24x0的HZ为200。
这一数值在后面的实验中可以证实。
每次发生一个时钟中断,内核内部计数器的值就加一。
这个计数器在系统启动时初始化为0,因此它代表本次系统启动以来的时钟嘀哒数。
这个计数器是一个64-位变量( 即便在32-位的体系上)并且称为“jiffies_64”。
但是驱动通常访问jiffies 变量(unsigned long)(根据体系结构的不同:可能是jiffies_64 ,可能是jiffies_64 的低32位)。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
Linux设备驱动程序学习(10)-时间、延迟及延缓操作度量时间差时钟中断由系统定时硬件以周期性的间隔产生,这个间隔由内核根据HZ 值来设定,HZ 是一个体系依赖的值,在<linux/param.h>中定义或该文件包含的某个子平台相关文件中。
作为通用的规则,即便如果知道HZ 的值,在编程时应当不依赖这个特定值,而始终使用HZ。
对于当前版本,我们应完全信任内核开发者,他们已经选择了最适合的HZ值,最好保持HZ 的默认值。
对用户空间,内核HZ几乎完全隐藏,用户HZ 始终扩展为100。
当用户空间程序包含param.h,且每个报告给用户空间的计数器都做了相应转换。
对用户来说确切的HZ 值只能通过/proc/interrupts 获得:/proc/interrupts 的计数值除以/proc/uptime 中报告的系统运行时间。
对于ARM体系结构:在<linux/param.h>文件中的定义如下:也就是说:HZ 由__KERNEL__和CONFIG_HZ决定。
若未定义__KERNEL__,HZ为100;否则为CONFIG_HZ。
而CONFIG_HZ是在内核的根目录的.config文件中定义,并没有在make menuconfig 的配置选项中出现。
Linux的\arch\arm\configs\s3c2410_defconfig文件中的定义为:所以正常情况下s3c24x0的HZ为200。
这一数值在后面的实验中可以证实。
每次发生一个时钟中断,内核内部计数器的值就加一。
这个计数器在系统启动时初始化为0,因此它代表本次系统启动以来的时钟嘀哒数。
这个计数器是一个64-位变量( 即便在32-位的体系上)并且称为“jiffies_64”。
但是驱动通常访问jiffies 变量(unsigned long)(根据体系结构的不同:可能是jiffies_64 ,可能是jiffies_64 的低32位)。
使用jiffies 是首选,因为它访问更快,且无需在所有的体系上实现原子地访问64-位的jiffies_64 值。
使用jiffies 计数器这个计数器和用来读取它的工具函数包含在<linux/jiffies.h>,通常只需包含<linux/sched.h>,它会自动放入jiffies.h 。
jiffies 和jiffies_64 必须被当作只读变量。
当需要记录当前jiffies 值(被声明为volatile 避免编译器优化内存读)时,可以简单地访问这个unsigned long 变量,如:以下是一些简单的工具宏及其定义:用户空间的时间表述法(struct timeval 和struct timespec )与内核表述法的转换函数:访问jiffies_64 对于32-位处理器不是原子的,这意味着如果这个变量在你正在读取它们时被更新你可能读到错误的值。
若需要访问jiffies_64,内核有一个特别的辅助函数,为你完成适当的锁定:处理器特定的寄存器若需测量非常短时间间隔或需非常高的精度,可以借助平台依赖的资源。
许多现代处理器包含一个随时钟周期不断递增的计数寄存器,他是进行高精度的时间管理任务唯一可靠的方法。
最有名的计数器寄存器是TSC ( timestamp counter), 在x86 的Pentium 处理器开始引入并在之后所有的CPU 中出现(包括x86_64 平台)。
它是一个64-位寄存器,计数CPU 的时钟周期,可从内核和用户空间读取。
在包含了<asm/msr.h> (一个x86-特定的头文件, 它的名子代表"machine-specific registers")的代码中可使用这些宏:一些其他的平台提供相似的功能, 并且内核头文件提供一个体系无关的功能用来代替rdtsc,称get_cycles(定义在<asm/timex.h>( 由<linux/timex.h> 包含)),原型如下:获取当前时间驱动一般无需知道时钟时间(用年月日、小时、分钟、秒来表达的时间),只对用户程序才需要,如cron 和syslogd。
内核提供了一个将时钟时间转变为秒数值的函数:为了处理绝对时间, <linux/time.h> 导出了do_gettimeofday 函数,它填充一个指向struct timeval 的指针变量。
绝对时间也可来自xtime 变量,一个struct timespec 值,为了原子地访问它,内核提供了函数current_kernel_time。
它们的精确度由硬件决定,原型是:以上两个函数在ARM平台都是通过xtime 变量得到数据的。
全局变量xtime:它是一个timeval结构类型的变量,用来表示当前时间距UNIX时间基准1970-01-01 00:00:00的相对秒数值。
结构timeval是Linux内核表示时间的一种格式(Linux内核对时间的表示有多种格式,每种格式都有不同的时间精度),其时间精度是微秒。
该结构是内核表示时间时最常用的一种格式,它定义在头文件include/linux/time.h中,如下所示:struct timeval {time_t tv_sec; /* seconds */suseconds_t tv_usec; /* microseconds */};其中,成员tv_sec表示当前时间距UNIX时间基准的秒数值,而成员tv_usec则表示一秒之内的微秒值,且1000000>tv_usec>=0。
Linux内核通过timeval结构类型的全局变量xtime来维持当前时间,该变量定义在kernel/timer.c 文件中,如下所示:/* The current time */volatile struct timeval xtime __attribute__ ((aligned (16)));但是,全局变量xtime所维持的当前时间通常是供用户来检索和设置的,而其他内核模块通常很少使用它(其他内核模块用得最多的是jiffies),因此对xtime的更新并不是一项紧迫的任务,所以这一工作通常被延迟到时钟中断的底半部(bottom half)中来进行。
由于bottom half的执行时间带有不确定性,因此为了记住内核上一次更新xtime是什么时候,Linux内核定义了一个类似于jiffies的全局变量wall_jiffies,来保存内核上一次更新xtime时的jiffies值。
时钟中断的底半部分每一次更新xtime的时侯都会将wall_jiffies更新为当时的jiffies值。
全局变量wall_jiffies定义在kernel/timer.c文件中:/* jiffies at the most recent update of wall time */unsigned long wall_jiffies;原文网址:/freedom1013/archive/2007/03/13/1528310.aspx 延迟执行设备驱动常常需要延后一段时间执行一个特定片段的代码, 常常允许硬件完成某个任务.长延迟有时,驱动需要延后执行相对长时间,长于一个时钟嘀哒。
忙等待(尽量别用)若想延迟执行若干个时钟嘀哒,精度要求不高。
最容易的( 尽管不推荐) 实现是一个监视jiffy 计数器的循环。
这种忙等待实现的代码如下:对cpu_relex 的调用将以体系相关的方式执行,在许多系统中它根本不做任何事,这个方法应当明确地避免。
对于ARM体系来说:也就是说在ARM上运行忙等待相当于:这种忙等待严重地降低了系统性能。
如果未配置内核为抢占式, 这个循环在延时期间完全锁住了处理器,计算机直到时间j1 到时会完全死掉。
如果运行一个可抢占的内核时会改善一点,但是忙等待在可抢占系统中仍然是浪费资源的。
更糟的是, 当进入循环时如果中断碰巧被禁止, jiffies 将不会被更新, 并且while 条件永远保持真,运行一个抢占的内核也不会有帮助, 唯一的解决方法是重启。
让出处理器忙等待加重了系统负载,必须找出一个更好的技术:不需要CPU时释放CPU 。
这可通过调用schedule 函数实现(在<linux/sched.h> 中声明):在计算机空闲时运行空闲任务(进程号0, 由于历史原因也称为swapper)可减轻处理器工作负载、降低温度、增加寿命。
超时实现延迟的最好方法应该是让内核为我们完成相应的工作。
(1)若驱动使用一个等待队列来等待某些其他事件,并想确保它在一个特定时间段内运行,可使用:(2)为了实现进程在超时到期时被唤醒而又不等待特定事件(避免声明和使用一个多余的等待队列头),内核提供了schedule_timeout 函数:短延迟当一个设备驱动需要处理硬件的延迟(latency潜伏期), 涉及到的延时通常最多几个毫秒,在这个情况下, 不应依靠时钟嘀哒,而是内核函数ndelay, udelay和mdelay ,他们分别延后执行指定的纳秒数, 微秒数或者毫秒数,定义在<asm/delay.h>,原型如下:重要的是记住这3 个延时函数是忙等待; 其他任务在时间流失时不能运行。
每个体系都实现udelay, 但是其他的函数可能未定义; 如果它们没有定义, <linux/delay.h> 提供一个缺省的基于udelay 的版本。
在所有的情况中, 获得的延时至少是要求的值, 但可能更多。
udelay 的实现使用一个软件循环, 它基于在启动时计算的处理器速度和使用整数变量loos_per_jiffy确定循环次数。
为避免在循环计算中整数溢出, 传递给udelay 和ndelay的值有一个上限,如果你的模块无法加载和显示一个未解决的符号:__bad_udelay, 这意味着你调用udleay时使用太大的参数。
作为一个通用的规则:若试图延时几千纳秒, 应使用udelay 而不是ndelay; 类似地, 毫秒规模的延时应当使用mdelay 完成而不是一个更细粒度的函数。
有另一个方法获得毫秒(和更长)延时而不用涉及到忙等待的方法是使用以下函数(在<linux/delay.h> 中声明):若能够容忍比请求的更长的延时,应使用schedule_timeout, msleep 或ssleep。
内核定时器当需要调度一个以后发生的动作, 而在到达该时间点时不阻塞当前进程, 则可使用内核定时器。
内核定时器用来调度一个函数在将来一个特定的时间(基于时钟嘀哒)执行,从而可完成各类任务。
内核定时器是一个数据结构, 它告诉内核在一个用户定义的时间点使用用户定义的参数执行一个用户定义的函数,函数位于<linux/timer.h> 和kernel/timer.c。