Linux 设备驱动开发 —— Tasklets 机制浅析

一 、Tasklets 机制基础知识点

1、Taklets 机制概念

      Tasklets 机制是linux中断处理机制中的软中断延迟机制。通常用于减少中断处理的时间,将本应该是在中断服务程序中完成的任务转化成软中断完成

      为了最大程度的避免中断处理时间过长而导致中断丢失,有时候我们需要把一些在中断处理中不是非常紧急的任务放在后面执行,而让中断处理程序尽快返回。在老版本的 linux 中通常将中断处理分为 top half handler 、 bottom half handler 。利用 top half handler 处理中断必须处理的任务,而 bottom half handler 处理不是太紧急的任务。

      但是 linux2.6 以后的 linux 采取了另外一种机制,就是软中断来代替 bottom half handler 的处理。而 tasklet 机制正是利用软中断来完成对驱动 bottom half 的处理。 Linux2.6 中软中断通常只有固定的几种: HI_SOFTIRQ( 高优先级的 tasklet ,一种特殊的 tasklet) 、 TIMER_SOFTIRQ (定时器)、 NET_TX_SOFTIRQ (网口发送)、 NET_RX_SOFTIRQ (网口接收) 、 BLOCK_SOFTIRQ (块设备)、 TASKLET_SOFTIRQ (普通 tasklet )。当然也可以通过直接修改内核自己加入自己的软中断,但是一般来说这是不合理的,软中断的优先级比较高,如果不是在内核处理频繁的任务不建议使用。通常驱动用户使用 tasklet 足够了。

      机制流程:当linux接收到硬件中断之后,通过 tasklet 函数来设定软中断被执行的优先程度从而导致软中断处理函数被优先执行的差异性

      特点:tasklet的优先级别较低,而且中断处理过程中可以被打断。但被打断之后,还能进行自我恢复,断点续运行。


2、Tasklets 解决什么问题?

a -- tasklet是I/O驱动程序中实现可延迟函数的首选方法

b -- tasklet和工作队列是延期执行工作的机制,其实现基于软中断,但他们更易于使用,因而更适合与设备驱动程序...tasklet是“小进程”,执行一些迷你任务,对这些人物使用全功能进程可能比较浪费。

c -- tasklet是并行可执行(但是是锁密集型的)软件中断和旧下半区的一种混合体,这里既谈不上并行性,也谈不上性能。引入tasklet是为了替代原来的下半区。


      软中断是将操作推迟到未来时刻执行的最有效的方法。但该延期机制处理起来非常复杂。因为多个处理器可以同时且独立的处理软中断,同一个软中断的处理程序可以在几个CPU上同时运行。对软中断的效率来说,这是一个关键,多处理器系统上的网络实现显然受惠于此。但处理程序的设计必须是完全可重入且线程安全的。另外,临界区必须用自旋锁保护(或其他IPC机制),而这需要大量审慎的考虑。

     我自己的理解,由于软中断以ksoftirqd的形式与用户进程共同调度,这将关系到OS整体的性能,因此软中断在Linux内核中也仅仅就几个(网络、时钟、调度以及Tasklet等),在内核编译时确定。软中断这种方法显然不是面向硬件驱动的,而是驱动更上一层:不关心如何从具体的网卡接收数据包,但是从所有的网卡接收的数据包都要经过内核协议栈的处理。而且软中断比较“硬”——数量固定、编译时确定、操作函数必须可重入、需要慎重考虑锁的问题,不适合驱动直接调用,因此Linux内核为驱动直接提供了一种使用软中断的方法,就是tasklet。

软中断和 tasklet 的关系如下图:



       上图可以看出, ksoftirqd 是一个后台运行的内核线程,它会周期的遍历软中断的向量列表,如果发现哪个软中断向量被挂起了( pend ),就执行对应的处理函数,对于 tasklet 来说,此处理函数就是 tasklet_action ,这个处理函数在系统启动时初始化软中断的就挂接了。Tasklet_action 函数,遍历一个全局的 tasklet_vec 链表(此链表对于 SMP 系统是每个 CPU 都有一个),此链表中的元素为 tasklet_struct 。下面将介绍各个函数


二、tasklet数据结构

        tasklet通过软中断实现,软中断中有两种类型属于tasklet,分别是级别最高的HI_SOFTIRQ和TASKLET_SOFTIRQ。

       Linux内核采用两个PER_CPU的数组tasklet_vec[]和tasklet_hi_vec[]维护系统种的所有tasklet(kernel/softirq.c),分别维护TASKLET_SOFTIRQ级别和HI_SOFTIRQ级别的tasklet:

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. struct tasklet_head  
  2. {  
  3.     struct tasklet_struct *head;  
  4.     struct tasklet_struct *tail;  
  5. };  
  6.   
  7. static DEFINE_PER_CPU(struct tasklet_head, tasklet_vec);  
  8. static DEFINE_PER_CPU(struct tasklet_head, tasklet_hi_vec);  


tasklet的核心结构体如下(include/linux/interrupt.h):

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. struct tasklet_struct  
  2. {  
  3.     struct tasklet_struct *next;  
  4.     unsigned long state;  
  5.     atomic_t count;  
  6.     void (*func)(unsigned long);  
  7.     unsigned long data;  
  8. };  

各成员的含义如下:
a -- next指针:指向下一个tasklet的指针。

b -- state:定义了这个tasklet的当前状态。这一个32位的无符号长整数,当前只使用了bit[1]和bit[0]两个状态位。其中,bit[1]=1表示这个tasklet当前正在某个CPU上被执行,它仅对SMP系统才有意义,其作用就是为了防止多个CPU同时执行一个tasklet的情形出现;bit[0]=1表示这个tasklet已经被调度去等待执行了。对这两个状态位的宏定义如下所示(interrupt.h)

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. enum  
  2. {  
  3.     TASKLET_STATE_SCHED,  
  4.     TASKLET_STATE_RUN  
  5. };  

TASKLET_STATE_SCHED置位表示已经被调度(挂起),也意味着tasklet描述符被插入到了tasklet_vec和tasklet_hi_vec数组的其中一个链表中,可以被执行。TASKLET_STATE_RUN置位表示该tasklet正在某个CPU上执行,单个处理器系统上并不校验该标志,因为没必要检查特定的tasklet是否正在运行。

c -- 原子计数count:对这个tasklet的引用计数值。NOTE!只有当count等于0时,tasklet代码段才能执行,也即此时tasklet是被使能的;如果count非零,则这个tasklet是被禁止的。任何想要执行一个tasklet代码段的人都首先必须先检查其count成员是否为0。

d -- 函数指针func:指向以函数形式表现的可执行tasklet代码段。

e -- data:函数func的参数。这是一个32位的无符号整数,其具体含义可供func函数自行解释,比如将其解释成一个指向某个用户自定义数据结构的地址值。



三、tasklet操作接口

       tasklet对驱动开放的常用操作包括:

a -- 初始化,tasklet_init(),初始化一个tasklet描述符。

b -- 调度,tasklet_schedule()和tasklet_hi_schedule(),将taslet置位TASKLET_STATE_SCHED,并尝试激活所在的软中断。

c -- 禁用/启动,tasklet_disable_nosync()、tasklet_disable()、task_enable(),通过count计数器实现。

d -- 执行,tasklet_action()和tasklet_hi_action(),具体的执行软中断。

e -- 杀死,tasklet_kill()

       即驱动程序在初始化时,通过函数task_init建立一个tasklet,然后调用函数tasklet_schedule将这个tasklet放在 tasklet_vec链表的头部,并唤醒后台线程ksoftirqd。当后台线程ksoftirqd运行调用__do_softirq时,会执行在中断向量表softirq_vec里中断号TASKLET_SOFTIRQ对应的tasklet_action函数,然后tasklet_action遍历 tasklet_vec链表,调用每个tasklet的函数完成软中断操作。

1、tasklet_int()函数实现如下(kernel/softirq.c)

     用来初始化一个指定的tasklet描述符

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. void tasklet_init(struct tasklet_struct *t,void (*func)(unsigned long), unsigned long data)  
  2. {  
  3.     t->next = NULL;  
  4.     t->state = 0;  
  5.     atomic_set(&t->count, 0);  
  6.     t->func = func;  
  7.     t->data = data;  
  8. }  

2、tasklet_schedule()函数

      与tasklet_hi_schedule()函数的实现很类似,这里只列tasklet_schedule()函数的实现(kernel/softirq.c),都挺明白就不描述了:

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. static inline void tasklet_schedule(struct tasklet_struct *t)  
  2. {  
  3.     if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state))  
  4.     __tasklet_schedule(t);  
  5. }  
  6.   
  7. void __tasklet_schedule(struct tasklet_struct *t)  
  8. {  
  9.     unsigned long flags;  
  10.     local_irq_save(flags);  
  11.     t->next = NULL;  
  12.     *__this_cpu_read(tasklet_vec.tail) = t;  
  13.     __this_cpu_write(tasklet_vec.tail, &(t->next));  
  14.     raise_softirq_irqoff(TASKLET_SOFTIRQ);  
  15.     local_irq_restore(flags);  
  16. }  

该函数的参数t指向要在当前CPU上被执行的tasklet。对该函数的NOTE如下:

a -- 调用test_and_set_bit()函数将待调度的tasklet的state成员变量的bit[0]位(也即TASKLET_STATE_SCHED位)设置为1,该函数同时还返回TASKLET_STATE_SCHED位的原有值。因此如果bit[0]为的原有值已经为1,那就说明这个tasklet已经被调度到另一个CPU上去等待执行了。由于一个tasklet在某一个时刻只能由一个CPU来执行,因此tasklet_schedule()函数什么也不做就直接返回了。否则,就继续下面的调度操作。

b -- 首先,调用local_irq_save()函数来关闭当前CPU的中断,以保证下面的步骤在当前CPU上原子地被执行。

c -- 然后,将待调度的tasklet添加到当前CPU对应的tasklet队列的首部。

d -- 接着,调用__cpu_raise_softirq()函数在当前CPU上触发软中断请求TASKLET_SOFTIRQ。

e -- 最后,调用local_irq_restore()函数来开当前CPU的中断。


3、tasklet_disable()函数、task_enable()函数以及tasklet_disable_nosync()函数(include/linux/interrupt.h)

      使能与禁止操作往往总是成对地被调用的

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. static inline void tasklet_disable_nosync(struct tasklet_struct *t)  
  2. {  
  3.     atomic_inc(&t->count);  
  4.     smp_mb__after_atomic_inc();  
  5. }  
  6.   
  7. static inline void tasklet_disable(struct tasklet_struct *t)  
  8. {  
  9.     tasklet_disable_nosync(t);  
  10.     tasklet_unlock_wait(t);  
  11.     smp_mb();  
  12. }  
  13.   
  14. static inline void tasklet_enable(struct tasklet_struct *t)  
  15. {  
  16.     smp_mb__before_atomic_dec();  
  17.     atomic_dec(&t->count);  
  18. }  

4、tasklet_action()函数在softirq_init()函数中被调用:

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. void __init softirq_init(void)  
  2. {  
  3.     ...  
  4.   
  5.     open_softirq(TASKLET_SOFTIRQ, tasklet_action);  
  6.     open_softirq(HI_SOFTIRQ, tasklet_hi_action);  
  7. }  

tasklet_action()函数

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. static void tasklet_action(struct softirq_action *a)  
  2. {  
  3.     struct tasklet_struct *list;  
  4.     local_irq_disable();  
  5.     list = __this_cpu_read(tasklet_vec.head);  
  6.     __this_cpu_write(tasklet_vec.head, NULL);  
  7.     __this_cpu_write(tasklet_vec.tail, &__get_cpu_var(tasklet_vec).head);  
  8.     local_irq_enable();  
  9.   
  10.     while (list)   
  11.     {  
  12.         struct tasklet_struct *t = list;  
  13.         list = list->next;  
  14.         if (tasklet_trylock(t))   
  15.         {  
  16.             if (!atomic_read(&t->count))   
  17.             {  
  18.                 if (!test_and_clear_bit(TASKLET_STATE_SCHED, &t->state))  
  19.                 BUG();  
  20.                 t->func(t->data);  
  21.                 tasklet_unlock(t);  
  22.                 continue;  
  23.             }  
  24.               
  25.             tasklet_unlock(t);  
  26.         }  
  27.           
  28.         local_irq_disable();  
  29.         t->next = NULL;  
  30.         *__this_cpu_read(tasklet_vec.tail) = t;  
  31.         __this_cpu_write(tasklet_vec.tail, &(t->next));  
  32.         __raise_softirq_irqoff(TASKLET_SOFTIRQ);  
  33.         local_irq_enable();  
  34.     }  
  35. }  

注释如下:

①首先,在当前CPU关中断的情况下,“原子”地读取当前CPU的tasklet队列头部指针,将其保存到局部变量list指针中,然后将当前CPU的tasklet队列头部指针设置为NULL,以表示理论上当前CPU将不再有tasklet需要执行(但最后的实际结果却并不一定如此,下面将会看到)。

②然后,用一个while{}循环来遍历由list所指向的tasklet队列,队列中的各个元素就是将在当前CPU上执行的tasklet。循环体的执行步骤如下:

a -- 用指针t来表示当前队列元素,即当前需要执行的tasklet。

b -- 更新list指针为list->next,使它指向下一个要执行的tasklet。

c -- 用tasklet_trylock()宏试图对当前要执行的tasklet(由指针t所指向)进行加锁

      如果加锁成功(当前没有任何其他CPU正在执行这个tasklet),则用原子读函atomic_read()进一步判断count成员的值。如果count为0,说明这个tasklet是允许执行的,于是:

   (1)先清除TASKLET_STATE_SCHED位;

   (2)然后,调用这个tasklet的可执行函数func;

   (3)执行barrier()操作;

   (4)调用宏tasklet_unlock()来清除TASKLET_STATE_RUN位。

  (5)最后,执行continue语句跳过下面的步骤,回到while循环继续遍历队列中的下一个元素。如果count不为0,说明这个tasklet是禁止运行的,于是调用tasklet_unlock()清除前面用tasklet_trylock()设置的TASKLET_STATE_RUN位。

    如果tasklet_trylock()加锁不成功,或者因为当前tasklet的count值非0而不允许执行时,我们必须将这个tasklet重新放回到当前CPU的tasklet队列中,以留待这个CPU下次服务软中断向量TASKLET_SOFTIRQ时再执行。为此进行这样几步操作:

  (1)先关CPU中断,以保证下面操作的原子性。

  (2)把这个tasklet重新放回到当前CPU的tasklet队列的首部;

  (3)调用__cpu_raise_softirq()函数在当前CPU上再触发一次软中断请求TASKLET_SOFTIRQ;

  (4)开中断。

c -- 最后,回到while循环继续遍历队列。


5、tasklet_kill()实现

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. void tasklet_kill(struct tasklet_struct *t)  
  2. {  
  3.     if (in_interrupt())  
  4.     printk("Attempt to kill tasklet from interruptn");  
  5.     while (test_and_set_bit(TASKLET_STATE_SCHED, &t->state))   
  6.     {  
  7.         do {  
  8.             yield();  
  9.         } while (test_bit(TASKLET_STATE_SCHED, &t->state));  
  10.     }  
  11.       
  12.     tasklet_unlock_wait(t);  
  13.     clear_bit(TASKLET_STATE_SCHED, &t->state);  
  14. }  

四、一个tasklet调用例子

       找了一个tasklet的例子看一下(drivers/usb/atm,usb摄像头),在其自举函数usbatm_usb_probe()中调用了tasklet_init()初始化了两个tasklet描述符用于接收和发送的“可延迟操作处理”,但此是并没有将其加入到tasklet_vec[]或tasklet_hi_vec[]中:

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. tasklet_init(&instance->rx_channel.tasklet,  
  2. usbatm_rx_process, (unsigned long)instance);  
  3. tasklet_init(&instance->tx_channel.tasklet,  
  4. usbatm_tx_process, (unsigned long)instance);  
      在其发送接口usbatm_atm_send()函数调用tasklet_schedule()函数将所初始化的tasklet加入到当前cpu的tasklet_vec链表尾部,并尝试调用do_softirq_irqoff()执行软中断TASKLET_SOFTIRQ:
[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. static int usbatm_atm_send(struct atm_vcc *vcc, struct sk_buff *skb)  
  2. {  
  3.     ...  
  4.   
  5.     tasklet_schedule(&instance->tx_channel.tasklet);  
  6.   
  7.     ...  
  8. }  
      在其断开设备的接口usbatm_usb_disconnect()中调用tasklet_disable()函数和tasklet_enable()函数重新启动其收发tasklet(具体原因不详,这个地方可能就是由这个需要,暂时重启收发tasklet):
[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. void usbatm_usb_disconnect(struct usb_interface *intf)  
  2. {  
  3.     ...  
  4.   
  5.     tasklet_disable(&instance->rx_channel.tasklet);  
  6.     tasklet_disable(&instance->tx_channel.tasklet);  
  7.   
  8.     ...  
  9.   
  10.     tasklet_enable(&instance->rx_channel.tasklet);  
  11.     tasklet_enable(&instance->tx_channel.tasklet);  
  12.   
  13.     ...  
  14. }  

      在其销毁接口usbatm_destroy_instance()中调用tasklet_kill()函数,强行将该tasklet踢出调度队列。


     从上述过程以及tasklet的设计可以看出,tasklet整体是这么运行的:驱动应该在其硬中断处理函数的末尾调用tasklet_schedule()接口激活该tasklet;内核经常调用do_softirq()执行软中断,通过softirq执行tasket,如下图所示。图中灰色部分为禁止硬中断部分,为保护软中断pending位图和tasklet_vec链表数组,count的改变均为原子操作,count确保SMP架构下同时只有一个CPU在执行该tasklet:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/402153.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

验证码(一)

需要验证码的地方还真不少,这主要是为了确保用户信息的安全。这里我做了一个纯字母的验证码。Random rnew Random ();string all "";private void btnCreatAuthCode_Click(object sender, EventArgs e){GetAuthCodes();}private void GetAuthCodes(){//定…

Linux 设备驱动开发 —— platform 设备驱动

一、platform总线、设备与驱动 在Linux 2.6 的设备驱动模型中,关心总线、设备和驱动3个实体,总线将设备和驱动绑定。在系统每注册一个设备的时候,会寻找与之匹配的驱动;相反的,在系统每注册一个驱动的时候,…

HTML5本地存储——IndexedDB(二:索引)

在HTML5本地存储——IndexedDB(一:基本使用)中介绍了关于IndexedDB的基本使用方法,很不过瘾,这篇我们来看看indexedDB的杀器——索引。 熟悉数据库的同学都知道索引的一个好处就是可以迅速定位数据,提高搜索…

Linux 字符设备驱动开发基础(五)—— ioremap() 函数解析

一、 ioremap() 函数基础概念 几乎每一种外设都是通过读写设备上的寄存器来进行的,通常包括控制寄存器、状态寄存器和数据寄存器三大类,外设的寄存器通常被连续地编址。根据CPU体系结构的不同,CPU对IO端口的编址方式有两种: a -- …

Linux 字符设备驱动开发基础(三)—— read()、write() 相关函数解析

我们在前面讲到了file_operations,其是一个函数指针的集合,用于存放我们定义的用于操作设备的函数的指针,如果我们不定义,它默认保留为NULL。其中有最重要的几个函数,分别是open()、read()、write()、ioctl()&#xff…

机电传动控制第一周学习笔记

机电传动控制第一周学习笔记: 1 这一周主要讲述了概论和机电传动控制系统动力学基础两个章节内容。 2 绪论中说明了《机电传动控制》课程主要内容为下图所示: 3机电传动控制系统动力学基础章节主要内容分为: (1)a&…

opengl 配置

OpenGL(Open Graphics Library)是一个跨编程语言、跨平台的专业图形程序接口。 OpenGL是SGI公司开发的一套计算机图形处理系统,是图形硬件的软件接口,任何一个OpenGL应用程序无须考虑其运行环境所在平台与操作系统,在任何一个遵循OpenG…

Linux 字符设备驱动开发基础(二)—— 编写简单 PWM 设备驱动

编写驱动的第一步仍是看原理图: 可以看到,该蜂鸣器由 GPD0_0 来控制 ,查手册可知该I/O口由Time0 来控制,找到相应的寄存器: a -- I/O口寄存器及地址 GPD0CON 0x114000a0 b -- Time0 寄存器及地址 基地址为&#xff1a…

专访:混合云的发展趋势

近日,业界享有盛誉的vForum2013大会在京召开,此次大会云集了近百家国内外知名的云计算、数据存储、大数据及信息安全厂商,共同讨论了虚拟化、云计算及未来IT模式的发展趋势。笔者也有幸在大会期间采访到了VMware 大中华区技术总监张振伦先生&…

Tomcat7性能优化

用了很久的Tomcat,没怎么看过它的优化,今天抽出时间研究了下,将内容记录下。 首先,是客户端访问tomcat的一个过程,如图所示: 图中间虚线框部分是 Apache基金下的服务器来做静态资源处理的,而这部…

Fast Image Cache – iOS 应用程序高性能图片缓存

Fast Image Cache 是一种在 iOS 应用程序中高效、持续、超快速的存储和检索图像的解决方案。任何良好的 iOS 应用程序的用户体验都应该是快速,平滑滚动的,Fast Image Cache 提供图像高速缓存有助于使这更容易实现。 对于图片丰富的应用程序,图…

Linux 字符设备驱动开发基础(四)—— ioctl() 函数解析

解析完 open、close、read、write 四个函数后,终于到我们的 ioctl() 函数了 一、 什么是ioctl ioctl是设备驱动程序中对设备的I/O通道进行管理的函数。所谓对I/O通道进行管理,就是对设备的一些特性进行控制,例如串口的传输波特率、马达的转速…

android自动化框架简要剖析(一):运行原理+基本框架

android自动化测试原理: 1、将测试apk和被测试apk,运行在一个进程中;通过instrumentation进行线程间的通信 2、通过android.test.AndroidTestCase及其子类,控制android系统对象 3、通过android.test.InstrumentationTestCase 及其…

Linux 字符设备驱动开发基础(一)—— 编写简单 LED 设备驱动

现在,我们来编写自己第一个字符设备驱动 —— 点亮LED。(不完善,后面再完善) 硬件平台:Exynos4412(FS4412) 编写驱动分下面几步: a -- 查看原理图、数据手册,了解设备的操…

Linux 字符设备驱动结构(四)—— file_operations 结构体知识解析

前面在 Linux 字符设备驱动开发基础 (三)—— 字符设备驱动结构(中) ,我们已经介绍了两种重要的数据结构 struct inode{...}与 struct file{...} ,下面来介绍另一个比较重要数据结构 struct _file_operatio…

Android开发群

为什么80%的码农都做不了架构师?>>> 我的自建Android应用开发群,欢迎大家来聊聊呀!201427584 转载于:https://my.oschina.net/catia/blog/176384

Linux 字符设备驱动结构(三)—— file、inode结构体及chardevs数组等相关知识解析

前面我们学习了字符设备结构体cdev Linux 字符设备驱动开发 (一)—— 字符设备驱动结构(上) 下面继续学习字符设备另外几个重要的数据结构。 先看下面这张图,这是Linux 中虚拟文件系统、一般的设备文件与设备驱动程序…

技术人生:三亚之行

人生收获 此次是公司组团去的三亚,地接的导游非常热情,如同大多数人一样,导游也会在这短短的几天内,尽可能的表现自己,此文聊聊导游说的三句话。 旅游三不“较”: 不比较不计较不睡觉人生何尝不是如此&…

Linux 字符设备驱动结构(二)—— 自动创建设备节点

上一篇我们介绍到创建设备文件的方法,利用cat /proc/devices查看申请到的设备名,设备号。 第一种是使用mknod手工创建:mknod filename type major minor 第二种是自动创建设备节点:利用udev(mdev)来实现设备…

Linux 字符设备驱动结构(一)—— cdev 结构体、设备号相关知识解析

一、字符设备基础知识 1、设备驱动分类 linux系统将设备分为3类:字符设备、块设备、网络设备。使用驱动程序: 字符设备:是指只能一个字节一个字节读写的设备,不能随机读取设备内存中的某一数据,读取数据需要按照先后数…