Linux设备驱动程式学习(5)-高级字符驱动程式操作[(2)阻塞型IO和休眠]
- 格式:doc
- 大小:86.00 KB
- 文档页数:14
《嵌入式Linux应用开发菜鸟进阶》第11章对字符设备驱动的模块框架有一个宏观的认识:#include <linux/module.h>#include <linux/types.h>#include <linux/fs.h>#include <linux/errno.h>︙#include<XXX>这里根据实际的驱动需要添加头文件static int mem_major = 251;︙/*这里定义驱动需要的一些静态数据或者指针,当然作为全局变量,一般不要轻易使用这些静态变量,它们很占内存,并且浪费资源*/︙实现file_operation中挂接的函数static const struct file_operations mem_operation={.owner = THIS_MODULE,︙};根据驱动需要实现相应的系统调用函数static int mymem_init(void)1{︙}模块驱动的注册函数static void mymem_exit(void){︙}模块的释放函数MODULE_AUTHOR("Lin Hui");MODULE_LICENSE("GPL");定义模块编写的作者以及遵循的协议module_init(mymem_init);module_exit(mymem_exit);定义模块初始化入口函数以上就是一个驱动基本不变的部分,针对字符变化的部分进行详细的讲解。
首先是字符设备的注册。
字符设备的注册主要分为4 步:设备号、分配设备号、定义并初始化file_operation结构体和字符设备的注册。
其中,设备号与分配设备号在11.1节中已经详述,这里不再重复。
下面介绍字符设备注册的详细步骤。
(1)设备号。
(2)分配设备号。
(3)定义并初始化file_operations结构体。
在Linux系统中,应用程序使用操作系统统一的系统调用接口来访问设备。
应用程序与驱动程序的交互方式有多种,包括查询、休眠唤醒(通过中断或线程)、poll以及异步通知。
Linux驱动分为三个基础大类:字符设备驱动,块设备驱动,网络设备驱动。
字符设备是一种能够像字节流一样被访问的设备,对字符设备发出读/写请求时,实际的硬件I/O操作一般紧接着发生。
应用程序APP的读取驱动程序的方式有阻塞、非阻塞、poll和异步通知。
相应的,驱动程序也提供这四种基本的读取和上报应用程序的编写模式。
这就是所谓的“提供能力,不提供策略”,即驱动程序应实现下述4种提供按键的方法,但是不应限制APP使用哪种方法。
linux课课程设计字符设备驱动一、教学目标本章节的教学目标是使学生掌握Linux系统中字符设备驱动的基本原理和编程方法。
通过本章节的学习,学生将能够:1.理解字符设备驱动的概念和作用;2.掌握字符设备驱动的原理和编程方法;3.能够编写简单的字符设备驱动程序。
二、教学内容本章节的教学内容主要包括:1.字符设备驱动的概念和作用;2.字符设备驱动的原理和编程方法;3.字符设备驱动的实例分析。
具体的教学大纲如下:1.字符设备驱动的概念和作用:介绍字符设备驱动的基本概念,解释其在Linux系统中的作用;2.字符设备驱动的原理:讲解字符设备驱动的工作原理,包括驱动程序的加载、设备文件的创建和使用;3.字符设备驱动的编程方法:介绍编写字符设备驱动程序的基本步骤和方法,包括文件操作、缓冲区管理和中断处理;4.字符设备驱动的实例分析:分析实际的字符设备驱动程序代码,让学生了解和掌握驱动程序的具体实现方法。
三、教学方法为了达到本章节的教学目标,将采用以下教学方法:1.讲授法:讲解字符设备驱动的基本概念、原理和编程方法;2.案例分析法:分析实际的字符设备驱动程序代码,让学生了解和掌握驱动程序的具体实现方法;3.实验法:让学生动手编写和调试字符设备驱动程序,巩固所学的知识和技能。
四、教学资源为了支持本章节的教学内容和教学方法的实施,将准备以下教学资源:1.教材:《Linux设备驱动程序设计与实现》;2.参考书:《Linux内核设计与实现》;3.多媒体资料:教学PPT、视频教程等;4.实验设备:计算机、开发板等。
五、教学评估为了全面、客观地评估学生在Linux字符设备驱动课程中的学习成果,将采用以下评估方式:1.平时表现:通过课堂参与、提问和讨论等方式评估学生的学习态度和理解程度;2.作业:布置相关的编程练习和理论作业,评估学生对知识的掌握和应用能力;3.考试:进行期中和期末考试,以评估学生对课程内容的整体理解和掌握程度。
[快速上手Linux设备驱动]之块设备驱动流程详解一[快速上手Linux设备驱动]之块设备驱动流程详解一walfred已经在[快速上手Linux设备驱动]之我看字符设备驱动一文中详细讲解了linux下字符设备驱动,并紧接着用四篇文章描述了Linux的设备模型,分别是总线、设备、驱动以及是类子系统。
为什么要在现在才开始讲解块设备驱动呢,这里面是有原因,当初walfred 自己学习时,是先看的块设备驱动然后才是linux设备模型,导致理解上面有了点偏差,现在我先将linux设备模型抛出之后,再叙述下linux下的块设备驱动。
[快速上手Linux设备驱动]之块设备驱动流程分两篇文章讲解,分别是:1、[快速上手Linux设备驱动]之块设备驱动流程详解一即是本文,主要讲解块设备去字符设备的区别以及块设备驱动中牵涉到的几个重要的结构体。
2、[快速上手Linux设备驱动]之块设备驱动流程详解二讲述了一个块设备驱动的模板1块设备与字符设备的区别1.1从字面上理解,块设备和字符设备最大的区别在于读写数据的基本单元不同。
块设备读写数据的基本单元为块,例如磁盘通常为一个sector(扇区),而字符设备的基本单元为字节。
所以Linux中块设备驱动往往为磁盘设备的驱动,但是由于磁盘设备的IO性能与CPU相比很差,因此,块设备的数据流往往会引入文件系统的Cache机制。
1.2从实现角度来看,Linux为块设备和字符设备提供了两套机制。
字符设备实现的比较简单,内核例程和用户态API一一对应,用户层的Read函数直接对应了内核中的Read例程,这种映射关系由字符设备的file_operations维护。
块设备接口相对于字符设备复杂,read、write API没有直接到块设备层,而是直接到文件系统层,然后再由文件系统层发起读写请求。
2相关结构体2.1 block_device_operations与字符设备驱动程序一样,块设备驱动程序也包含一个在<linux/fs.h>中定义的block_device_operations结构,其定义如下所示。
实验二:字符设备驱动实验一、实验目的通过本实验的学习,了解Linux操作系统中的字符设备驱动程序结构,并能编写简单的字符设备的驱动程序以及对所编写的设备驱动程序进行测试,最终了解Linux操作系统如何管理字符设备。
二、准备知识字符设备驱动程序主要包括初始化字符设备、字符设备的I/O调用和中断服务程序。
在字符设备驱动程序的file_operations结构中,需要定义字符设备的基本入口点。
open()函数;release()函数read()函数write()函数ioctl()函数select()函数。
另外,注册字符设备驱动程序的函数为register_chrdev()。
register_chrdev() 原型如下:int register_chrdev(unsigned int major, //主设备号const char *name, //设备名称struct file_operations *ops); //指向设备操作函数指针其中major是设备驱动程序向系统申请的主设备号。
如果major为0,则系统为该驱动程序动态分配一个空闲的主设备号。
name是设备名称,ops是指向设备操作函数的指针。
注销字符设备驱动程序的函数是unregister_chrdev(),原型如下:int unregister_chrdev(unsigned int major,const char *name);字符设备注册后,必须在文件系统中为其创建一个设备文件。
该设备文件可以在/dev目录中创建,每个设备文件代表一个具体的设备。
使用mknod命令来创建设备文件。
创建设备文件时需要使用设备的主设备号和从设备号作为参数。
阅读教材相关章节知识,了解字符设备的驱动程序结构。
三、实验内容根据教材提供的实例。
编写一个简单的字符设备驱动程序。
要求该字符设备包括open()、write()、read()、ioctl()和release()五个基本操作,并编写一个测试程序来测试所编写的字符设备驱动程序。
linux字符驱动框架(⽤户态的read,write,poll是怎么操作驱动的)前⾔这篇⽂章是通过对⼀个简单字符设备驱动的操作来解释,⽤户态的读写操作是怎么映射到具体设备的。
因为针对不同版本的linux内核,驱动的接⼝函数⼀直有变化,这贴出我测试的系统信息:root@ubuntu:~/share/dev/cdev-2# cat /etc/os-release |grep -i verVERSION="16.04.5 LTS (Xenial Xerus)"VERSION_ID="16.04"VERSION_CODENAME=xenialroot@ubuntu:~/share/dev/cdev-2#root@ubuntu:~/share/dev/cdev-2# uname -r4.15.0-33-generic字符驱动这⾥给出了⼀个不怎么标准的驱动,定义了⼀个结构体 struct dev,其中buffer成员模拟驱动的寄存器。
由wr,rd作为读写指针,len作为缓存buffer的长度。
具体步骤如下:1. 定义 init 函数,exit函数,这是在 insmod,rmmod时候调⽤的。
2. 定义驱动打开函数open,这是在⽤户态打开设备时候调⽤的。
3. 定义release函数,这是在⽤户态关闭设备时候⽤到的。
4. 定义read,write,poll函数,并挂接到 file_operations结构体中,所有⽤户态的read,write,poll都会最终调到这些函数。
chardev.c/*参考:深⼊浅出linux设备驱动开发*/#include <linux/module.h>#include <linux/init.h>#include <linux/fs.h>#include <linux/uaccess.h>#include <linux/wait.h>#include <linux/semaphore.h>#include <linux/sched.h>#include <linux/cdev.h>#include <linux/types.h>#include <linux/kdev_t.h>#include <linux/device.h>#include <linux/poll.h>#define MAXNUM 100#define MAJOR_NUM 400 //主设备号 ,没有被使⽤struct dev{struct cdev devm; //字符设备struct semaphore sem;int flag;poll_table* table;wait_queue_head_t outq;//等待队列,实现阻塞操作char buffer[MAXNUM+1]; //字符缓冲区char *rd,*wr,*end; //读,写,尾指针}globalvar;static struct class *my_class;int major=MAJOR_NUM;static ssize_t globalvar_read(struct file *,char *,size_t ,loff_t *);static ssize_t globalvar_write(struct file *,const char *,size_t ,loff_t *);static int globalvar_open(struct inode *inode,struct file *filp);static int globalvar_release(struct inode *inode,struct file *filp);static unsigned int globalvar_poll(struct file* filp, poll_table* wait);/*结构体file_operations在头⽂件 linux/fs.h中定义,⽤来存储驱动内核模块提供的对设备进⾏各种操作的函数的指针。
linux设备驱动第三篇:写⼀个简单的字符设备驱动在linux设备驱动第⼀篇:设备驱动程序简介中简单介绍了字符驱动,本篇简单介绍如何写⼀个简单的字符设备驱动。
本篇借鉴LDD中的源码,实现⼀个与硬件设备⽆关的字符设备驱动,仅仅操作从内核中分配的⼀些内存。
下⾯就开始学习如何写⼀个简单的字符设备驱动。
⾸先我们来分解⼀下字符设备驱动都有那些结构或者⽅法组成,也就是说实现⼀个可以使⽤的字符设备驱动我们必须做些什么⼯作。
1、主设备号和次设备号对于字符设备的访问是通过⽂件系统中的设备名称进⾏的。
他们通常位于/dev⽬录下。
如下:xxx@ubuntu:~$ ls -l /dev/total 0brw-rw---- 1 root disk 7, 0 3⽉ 25 10:34 loop0brw-rw---- 1 root disk 7, 1 3⽉ 25 10:34 loop1brw-rw---- 1 root disk 7, 2 3⽉ 25 10:34 loop2crw-rw-rw- 1 root tty 5, 0 3⽉ 25 12:48 ttycrw--w---- 1 root tty 4, 0 3⽉ 25 10:34 tty0crw-rw---- 1 root tty 4, 1 3⽉ 25 10:34 tty1crw--w---- 1 root tty 4, 10 3⽉ 25 10:34 tty10其中b代表块设备,c代表字符设备。
对于普通⽂件来说,ls -l会列出⽂件的长度,⽽对于设备⽂件来说,上⾯的7,5,4等代表的是对应设备的主设备号,⽽后⾯的0,1,2,10等则是对应设备的次设备号。
那么主设备号和次设备号分别代表什么意义呢?⼀般情况下,可以这样理解,主设备号标识设备对应的驱动程序,也就是说1个主设备号对应⼀个驱动程序。
当然,现在也有多个驱动程序共享主设备号的情况。
⽽次设备号有内核使⽤,⽤于确定/dev下的设备⽂件对应的具体设备。
Linux设备驱动程式学习(5) -高级字符驱动程式操作[(2)阻塞型I/O和休眠]
这一部分主要讨论:如果驱动程式无法即时满足请求,该怎么响应?(65865346) 一、休眠 进程被置为休眠,意味着他被标识为处于一个特别的状态并且从调度器的运行队列中移走。这个进程将不被在所有 CPU 上调度,即将不会运行。 直到发生某些事情改动了那个状态。安全地进入休眠的两条规则: (1) 永远不要在原子上下文中进入休眠,即当驱动在持有一个自旋锁、seqlock或 RCU 锁时不能睡眠;关闭中断也不能睡眠。持有一个信号量时休眠是合法的,但你应当仔细查看代码:如果代码在持有一个信号量时睡眠,所有其他的等待这个信号量的线程也会休眠。因此发生在持有信号量时的休眠必须短暂,而且决不能阻塞那个将最终唤醒你的进程。 (2)当进程被唤醒,他并不知道休眠了多长时间及休眠时发生什么;也不知道是否另有进程也在休眠等待同一事件,且那个进程可能在他之前醒来并获取了所等待的资源。所以不能对唤醒后的系统状态做所有的假设,并必须重新检查等待条件来确保正确的响应。 除非确信其他进程会在其他地方唤醒休眠的进程,否则也不能睡眠。使进程可被找到意味着:需要维护一个称为等待队列的数据结构。他是个进程链表,其中饱含了等待某个特定事件的所有进程。在 Linux 中, 一个等待队列由一个wait_queue_head_t 结构体来管理,其定义在中。wait_queue_head_t 类型的数据结构非常简单: struct __wait_queue_head { spinlock_t lock; struct list_head task_list; }; typedef struct __wait_queue_head wait_queue_head_t; 他包含一个自旋锁和一个链表。这个链表是个等待队列入口,他被声明做 wait_queue_t。wait_queue_head_t包含关于睡眠进程的信息和他想怎样被唤醒。 简单休眠(其实是高级休眠的宏) Linux 内核中最简单的休眠方式是称为 wait_event的宏(及其变种),他实现了休眠和进程等待的条件的检查。形式如下: wait_event(queue, condition)/*不可中断休眠,不推荐*/ wait_event_interruptible(queue, condition)/*推荐,返回非零值意味着休眠被中断,且驱动应返回 -ERESTARTSYS*/ wait_event_timeout(queue, condition, timeout) wait_event_interruptible_timeout(queue, condition, timeout) /*有限的时间的休眠;若超时,则不管条件为何值返回0,*/ 唤醒休眠进程的函数称为 wake_up,形式如下: void wake_up(wait_queue_head_t *queue); void wake_up_interruptible(wait_queue_head_t *queue); 惯例:用 wake_up 唤醒 wait_event ;用 wake_up_interruptible 唤醒wait_event_interruptible。 简单休眠实验 模块程式链接: sleepy 模块测试程式链接: sleepy-test 实验现象: [Tekkaman2440@SBC2440V4]#cd /lib/modules/ [Tekkaman2440@SBC2440V4]#insmod sleepy.ko [Tekkaman2440@SBC2440V4]#cd /dev/ [Tekkaman2440@SBC2440V4]#cat /proc/devices Character devices: 1 mem 2 pty 3 ttyp 4 /dev/vc/0 4 tty 4 ttyS 5 /dev/tty 5 /dev/console 5 /dev/ptmx 7 vcs 10 misc 13 input 14 sound 81 video4linux 89 i2c 90 mtd 116 alsa 128 ptm 136 pts 180 usb 189 usb_device 204 s3c2410_serial 252 sleepy 253 usb_endpoint 254 rtc Block devices: 1 ramdisk 256 rfd 7 loop 31 mtdblock 93 nftl 96 inftl 179 mmc [Tekkaman2440@SBC2440V4]#mknod -m 666 sleepy c 252 0 [Tekkaman2440@SBC2440V4]#cd /tmp/ [Tekkaman2440@SBC2440V4]#./sleepy_testr& [Tekkaman2440@SBC2440V4]#./sleepy_testr& [Tekkaman2440@SBC2440V4]#ps PID Uid VSZ Stat Command 1 root 1744 S init 2 root SW [kthreadd] 3 root SWN [ksoftirqd/0] 4 root SW [watchdog/0] 5 root SW [events/0] 6 root SW [khelper] 59 root SW [kblockd/0] 60 root SW [ksuspend_usbd] 63 root SW [khubd] 65 root SW [kseriod] 77 root SW [pdflush] 78 root SW [pdflush] 79 root SW [kswapd0] 80 root SW [aio/0] 707 root SW [mtdblockd] 708 root SW [nftld] 709 root SW [inftld] 710 root SW [rfdd] 742 root SW [kpsmoused] 751 root SW [kmmcd] 769 root SW [rpciod/0] 778 root 1752 S -sh 779 root 1744 S init 781 root 1744 S init 783 root 1744 S init 787 root 1744 S init 799 root 1336 S ./sleepy_testr 800 root 1336 S ./sleepy_testr 802 root 1744 R ps [Tekkaman2440@SBC2440V4]#./sleepy_testw read code=0 write code=0 [2] + Done ./sleepy_testr [Tekkaman2440@SBC2440V4]#ps PID Uid VSZ Stat Command 1 root 1744 S init 2 root SW [kthreadd] 3 root SWN [ksoftirqd/0] 4 root SW [watchdog/0] 5 root SW [events/0] 6 root SW [khelper] 59 root SW [kblockd/0] 60 root SW [ksuspend_usbd] 63 root SW [khubd] 65 root SW [kseriod] 77 root SW [pdflush] 78 root SW [pdflush] 79 root SW [kswapd0] 80 root SW [aio/0] 707 root SW [mtdblockd] 708 root SW [nftld] 709 root SW [inftld] 710 root SW [rfdd] 742 root SW [kpsmoused] 751 root SW [kmmcd] 769 root SW [rpciod/0] 778 root 1752 S -sh 779 root 1744 S init 781 root 1744 S init 783 root 1744 S init 787 root 1744 S init 799 root 1336 S ./sleepy_testr 804 root 1744 R ps [Tekkaman2440@SBC2440V4]#./sleepy_testw write code=0 [Tekkaman2440@SBC2440V4]#read code=0 [1] + Done ./sleepy_testr [Tekkaman2440@SBC2440V4]#ps PID Uid VSZ Stat Command 1 root 1744 S init 2 root SW [kthreadd] 3 root SWN [ksoftirqd/0] 4 root SW [watchdog/0] 5 root SW [events/0] 6 root SW [khelper] 59 root SW [kblockd/0] 60 root SW [ksuspend_usbd] 63 root SW [khubd] 65 root SW [kseriod] 77 root SW [pdflush] 78 root SW [pdflush] 79 root SW [kswapd0] 80 root SW [aio/0] 707 root SW [mtdblockd] 708 root SW [nftld] 709 root SW [inftld] 710 root SW [rfdd] 742 root SW [kpsmoused] 751 root SW [kmmcd]