Linux 驱动程序之字符驱动
系统调用、内核、驱动程序的关系
主要驱动类型:
-> 字符设备 ( c)
-> 块设备 (b)
-> 网络设备 (ifconfig)
字符设备和块设备的主要区别是 : 在对字符 设备发出读
Linux 驱动程序之字符驱动 系统调用、内核、驱动程序的关系 主要驱动类型: -> 字符设备 ( c) -> 块设备 (b) -> 网络设备 (ifconfig) 字符设备和块设备的主要区别是 : 在对字符 设备发出读 / 写请求时,实际的硬件 I/O 一 般就紧接着发生了,块设备则不然,它利用 一块系统内存作缓冲区 主设备号 , 从设备号 用 ll 命令可以观察,设备号规范在 docmention/devices.txt 中 模块 对于每一个内核模块来说,必定包含下面两个函数: int init_module () : 这个函数在插入内核时启动,在内核中注册一定的功能函数,或者用 他的代码代替内和中某些函数的内容。 int cleanup_module() :当内核模块卸载时调用,它能将模块从内核中清 除。 (#include ) 编译 :Makefile(-D__KERNEL__ -DMODULE) 在包含 module.h 前定义 NO_VERSION 使用模块: insmod,lsmod,rmmod,depmod 字符型驱动程序 驱动程序的初始化、卸载 设备驱动程序所提供的入口点,在设备驱动程序初始化的时候向系统进行登记,以便系统在适当的时候调用。LINUX系统里,通过调用 register_chrdev向系统注册字符型设备驱动程序。 register_chrdev定义为:
其中,major是为设备驱动程序向系统申请的主设备号,如果为0则系统为此驱动程序动态地分配一个主设备号。name是设备名。fops就是对各个调用的入口点的说明。此函数返回0表示成功。返回-EINVAL表示申请的主设备号非法,一般来说是主设备号大于系统所允许的最大设备号。返回-EBUSY表示所申请的主设备号正在被其它设备驱动程序使用。如果是动态分配主设备号成功,此函数将返回所分配的主设备号。如果 register_chrdev操作成功,设备名就会出现在/proc/devices文件里。 初始化部分一般还负责给设备驱动程序申请系统资源,包括内存、中断、时钟、I/O端口等,这些资源也可以在open子程序或别的地方申请。在这些资源不用的时候,应该释放它们,以利于资源的共享。 驱动程序的功能实现: 两个重要的数据结构
Linux中的I/O子系统向内核中的其他部分提供了一个统一的标准设备接口,这是通过include/linux/fs.h中的struct file_operations来完成的 (1) lseek,移动文件指针的位置,显然只能用于可以随机存取的设备 (2) read,进行读操作,参数buf为存放读取结果的缓冲区,count为所要读取的数据长度。返回值为负表示读取操作发生错误,否则返回实际读取的字节数。对于字符型,要求读取的字节数和返回的实际读取字节数都必须是inode->i_blksize的的倍数 (3)write,进行写操作,与read类似。 (4)readdir,取得下一个目录入口点,只有与文件系统相关的设备驱动程序才使用 (5)select,进行选择操作,如果驱动程序没有提供select入口,select操作将会认为设备已经准备好进行任何的I/O操作 (6)ioctl,进行读、写以外的其它操作,参数cmd为自定义的的命令 (7)mmap,用于把设备的内容映射到地址空间,一般只有块设备驱动程序使用 (8)open,打开设备准备进行I/O操作。返回0表示打开成功,返回负数表示失败。如果驱动程序没有提供open入口,则只要/dev/driver文件存在就认为打开成功 (9)release,即close操作 read: 阻塞I/O模式与非阻塞I/O模式 当执行读写I/O的系统调用时,执行或等待I/O操作的过程中进程阻塞,直到I/O操作完成,调用才结束,唤醒进程继续向下进行。缺省情况下套接口的读写操作就是阻塞I/O方式。阻塞I/O模式的流程图如下: 设备驱动程序通过调用request_irq函数来申请中断,通过free_irq来释放中断。 开关中断:disable_irq(intirq);enable_irq(intirq); 中断处理注意的方面,中断不处于进程上下文,处于中断模式 1.不允许访问用户空间,没有到达与进程关联的用户空间路径 2.current指针在中断模式下是无效的 3.不能执行睡眠。不可以调用schedule或者sleep_on; kmalloc(…,GFP_KERNEL) 4.中断处理函数不能太长 作为系统核心的一部分,设备驱动程序在申请和释放内存时不是调用 malloc和free,而代之以调用kmalloc和kfree 高1G(内核空间)的内存分配 物理区 || 8M隔离 || vmalloc区 || 8K隔离 || 4M的高端映射区 || 固定映射区 || 128K保留区 申请与释放IO:check_region,request_region,release_region 在设备驱动程序里,一般都需要用到计时机制。在LINUX系统中,时钟是由 系统接管,设备驱动程序可以向系统申请时钟。与时钟有关的系统调用有: #include #include void add_timer(struct timer_list * timer); int del_timer(struct timer_list * timer); 时间流: Linux的时间系统 一般PC机中有两个时钟,分别是RTC时钟和OS时钟。OS时钟产生于主板上的定时/计数芯片,其基本单位就是计数芯片的计数周期。在开机时通过RTC来初始化芯片。定时/计数芯片的每一个输出脉冲周期叫做一个“时钟滴答”,计算机中的时间就是以“滴答”为单位的 ,每一次滴答时间会加一。根据当前的滴答数就可以得到秒等其他单位 计时器大概每10ms向CPU送入一个脉冲,就可以触发一个时钟中断。系统利用时钟中断维持系统时间,促使进程和环境发生切换,进行记帐等工作以确定动态优先级等等。 中断: arm 的中断向量表可以放在地址 0 开始linux编码,也可以指定为 0xFFFF0000 开始。 中断发生后的执行过程是: R14_irq = 下一条指令的地址 Spsr_irq = CPSR Cpsr[4:0] = 0b10010 ; 表示进入 irq Cpsr[7] = 1 ; 关闭 irq, 防止中断嵌套 If high vector table then Pc = 0xFFFF0018 Else Pc = 0x00000018 Linux 中的中断向量表是在 void __init trap_init(void) // arch/arm/kernle/trap.c 中安装的,其中调用汇编函数 __trap_init((void *)vectors_base()); 宏 vectors_base() 决定项量表是从 0 开始还是从 0xFFFF0000 开始 保存中断现场用宏 get_irqnr_and_base 取得中断号代码 do_IRQ ,此时 R0 为中断号, R1 指向堆栈中保存的寄存器起始地址 。do { // 逐个处理中断处理 handler 链表中的每个函数 status |= action->flags; action->handler(irq, action->dev_id, regs); action = action->next; } while (action);if (softirq_pending(cpu)) // 执行软中断处理过程 do_softirq(); 软中断概况: 软中断是利用硬件中断的概念,用软件方式进行模拟,实现宏观上的异步 执行效果。 -> bottom half 在 Linux 内核中, bottom half 通常用“ bh” 表示,最初用于在特权级较 低的上下文中完成中断服务的非关键耗时动作,现在也用于一切可在低优 先级的上下文中执行的异步动作。 -> task queue 原始的 bottom half 机制有几个很大的局限,最重要的一个就 是个数限制在 32 个以内,随着系统硬件越来越多,软中断的应用范围越来 越大,这个数目显然是不够用的,而且,每个 bottom half 上只能挂接一 个函数,也是不够用的。因此,在 2.0.x 内核里,已经在用 task queue ( 任务队列)的办法对其进行了扩充 -> tasklet 之所以引入 tasklet ,最主要的考虑是为了更好的支持 SMP ,提高 SMP 多个 CPU 的利用率:不同的 tasklet 可以同时运行于不同的 CPU 上。 (编辑:通辽站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |