Driver中使用的内核机制

1. 互斥与同步

自旋锁

三种主要自旋锁函数

spin_lock

spin_lock_irq/spin_lock_irqsave

spin_lock_bh

使用spin_lock时要明确知道该锁不会在中断处理程序中使用。

在任何情况下使用spin_lock_irq都是安全的,它关闭当前CPU硬件中断,禁止内核抢占。使用spin_lock_irqsave在于你不期望在离开临界区后,改变中断的开启关闭状态!进入临界区是关闭的,离开后它同样应该是关闭的!

spin_lock_bh()中首先会调用local_bh_disable()禁止当前CPU的软件中断。

如果被保护的共享资源只在进程上下文访问和软中断上下文访问,那么当在进程上下文访问共享资源时,可能被软中断打断,从而可能进入软中断上下文来对被保护的共享资源访问,因此对于这种情况,对共享资源的访问必须使用spin_lock_bh和spin_unlock_bh来保护。

当然使用spin_lock_irq和spin_unlock_irq以及spin_lock_irqsave和 spin_unlock_irqrestore也可以,它们失效了本地硬中断,失效硬中断隐式地也失效了软中断。但是使用spin_lock_bh和 spin_unlock_bh是最恰当的,它比其他两个快。

信号量

信号量和读写信号量适合于保持时间较长的情况,它们会导致调用者睡眠,因此只能在进程上下文使用(_trylock的变种能够在中断上下文使用),当尝试获取某信号量失败时CPU会来回切换到其他进程

互斥锁

互斥锁上特殊的信号量,将该特殊semaphore的count值初始化为1,获取锁和释放锁对应信号量的DOWN和UP操作

struct  mutex cmd_lock;

struct mutex cmd_lock;

mutex_init(&cmd->cmd_lock);

mutex_destroy(&cmd->cmd_lock);

mutex_lock(&cmd->cmd_lock);

mutex_unlock(&cmd->cmd_lock);

原子变量与位操作

所谓的原子操作即是保证指令以原子的方式执行,它在执行过程中不被打断。它包括了原子整数操作和原子位操作,在内核中分别定义于include\linux\types.h和arch\x86\include\asm\bitops.h

原子位操作

2.阻塞操作

在linux驱动程序中,可使用等待队列(wait queue)来实现阻塞进程的唤醒,以队列为基础数据结构,与进程调度机制紧密结合,用于视线内核的异步事件通知机制,也可用于同步对系统资源的访问。(信号量在内核中也依赖等待队列来实现)

在软件开发中任务经常由于某种条件没有得到满足而不得不进入睡眠状态,然后等待条件得到满足的时候再继续运行,进入运行状态。这种需求需要等待队列机制的支持。Linux中提供了等待队列的机制,该机制在内核中应用很广泛。

等待队列在linux内核中有着举足轻重的作用,很多linux驱动都或多或少涉及到了等待队列。因此,对于linux内核及驱动开发者来说,掌握等待队列是必须课之

Linux内核的等待队列是以双循环链表为基础数据结构,与进程调度机制紧密结合,能够用于实现核心的异步事件通知机制。它有两种数据结构:等待队列头 (wait_queue_head_t)和等待队列项(wait_queue_t)。等待队列头和等待队列项中都包含一个list_head类型的域作为”连接件”。它通过一个双链表和把等待task的头,和等待的进程列表链接起来。下面具体介绍。

wait_queue_head_twaitq;

init_waitqueue_head(&cmd->waitq);

wait_event_timeout(wq,condition, timeout)

也与wait_event()类似.不过如果所给的睡眠时间为负数则立即返回.如果在睡眠期间被唤醒,且condition为真则返回剩余的睡眠时间,否则继续睡眠直到到达或超过给定的睡眠时间,然后返回0.

在我们驱动中,向fireware发送命令时,要求命令在timeout之内返回,否则认为命令没有成功返回;发送命令后会wait_event_timeout阻塞线程,直到收到了fireware的命令返回,这时wake_up阻塞的线程,且能够判断condition为真,wait_event_timeout返回为剩余时间,如果超过了timeout时间还未收到返回命令,则wait_event_timeout直接返回0.

3.延迟操作

除了中断处理进程用到的底半部机制,用在进程上下文的延迟操作是work queue

(一)利用系统共享的工作队列添加工作:

第一步:声明或编写一个工作处理函数

void my_func();

第二步:创建一个工作结构体变量,并将处理函数和参数的入口地址赋给这个工作结构体变量

DECLARE_WORK(my_work,my_func,&data);//编译时创建名为my_work的结构体变量并把函数入口地址和参数地址赋给它;如果不想要在编译时就用DECLARE_WORK()创建并初始化工作结构体变量,也可以在程序运行时再用INIT_WORK()创建

struct work_structmy_work; //创建一个名为my_work的结构体变量,创建后才能使用INIT_WORK()

INIT_WORK(&my_work,my_func,&data);//初始化已经创建的my_work,其实就是往这个结构体变量中添加处理函数的入口地址和data的地址,通常在驱动的open函数中完成

第三步:将工作结构体变量添加入系统的共享工作队列

schedule_work(&my_work);//添加入队列的工作完成后会自动从队列中删除schedule_delayed_work(&my_work,tick); //延时tick个滴答后再提交工作

(二)创建自己的工作队列来添加工作

第一步:声明工作处理函数和一个指向工作队列的指针

void my_func();

struct workqueue_struct*p_queue;

第二步:创建自己的工作队列和工作结构体变量(通常在open函数中完成)

p_queue=create_workqueue("my_queue");//创建一个名为my_queue的工作队列并把工作队列的入口地址赋给声明的指针

struct work_structmy_work;

INIT_WORK(&my_work,my_func,&data);//创建一个工作结构体变量并初始化,和第一种情况的方法一样

第三步:将工作添加入自己创建的工作队列等待执行

queue_work(p_queue,&my_work); //作用与schedule_work()类似,不同的是将工作添加入p_queue指针指向的工作队列而不是系统共享的工作队列

第四步:删除自己的工作队列

destroy_workqueue(p_queue);//一般是在close函数中删除

4. Linux内核链表

4.1 链表初始化

struct athwl_priv {

struct wiphy *wiphy;

/* virtual interfacelist */

spinlock_t list_lock;

struct list_headvif_list;

}

INIT_LIST_HEAD(&priv->vif_list);

struct athwl_vif {

struct net_device*ndev;        /* Linux net device */

struct wireless_dev  wdev;       /* Linux wireless device */

struct athwl_priv*priv;

char name[IFNAMSIZ];

enum athwl_mode mode;

struct list_headvif_node;      /* node for virtualinterface list */

}

4.2插入操作

在表头插入:

static inline voidlist_add(struct list_head *new, struct list_head *head);

在表尾插入:

static inline void list_add_tail(structlist_head *new, struct list_head *head);

list_add_tail(&vif->vif_node,&priv->vif_list);

4.3删除

static inline voidlist_del(struct list_head *entry);

4.4遍历

a)由链表节点到数据项

/**

* list_entry - get thestruct for this entry

* @ptr:        the &struct list_head pointer.

* @type:       the type of the struct this is embeddedin.

* @member:     the name of the list_struct within thestruct.

*/

#define list_entry(ptr, type, member) \

container_of(ptr,type, member)

b)遍历宏

/**

* list_for_each        -      iterate over a list

* @pos:       the &struct list_head to use as a loop cursor.

* @head:      the head for your list.

*/

#definelist_for_each(pos, head) \

for (pos = (head)->next; pos !=(head); pos = pos->next)

它实际上是一个for循环,利用传入的pos作为循环变量,从表头head开始,逐项向后(next方向)移动pos,直至又回到head

大多数情况下,遍历链表的时候都需要获得链表节点数据项,也就是说list_for_each()和list_entry()总是同时使用。对此Linux给出了一个list_for_each_entry()宏:

#definelist_for_each_entry(pos, head, member) ……

与list_for_each()不同,这里的pos是数据项结构指针类型,而不是(struct list_head *)。

list_for_each_entry(vif,&priv->vif_list, vif_node);

Driver中使用的内核机制相关推荐

  1. Linux 内核中的 Device Mapper 机制

    本文结合具体代码对 Linux 内核中的 device mapper 映射机制进行了介绍.Device mapper 是 Linux 2.6 内核中提供的一种从逻辑设备到物理设备的映射框架机制,在该机 ...

  2. Linux 2.6内核中新的锁机制--RCU [转]

    2005 年 7 月 01 日 本文详细地介绍了 Linux 2.6 内核中新的锁机制 RCU(Read-Copy Update) 的实现机制,使用要求与典型应用. 一. 引言 众所周知,为了保护共享 ...

  3. Linux 2.6内核中新的锁机制--RCU

    转载自: Linux 2.6内核中新的锁机制--RCU 一. 引言 众所周知,为了保护共享数据,需要一些同步机制,如自旋锁(spinlock),读写锁(rwlock),它们使用起来非常简单,而且是一种 ...

  4. Linux内核中的vfs,解析 Linux 中的 VFS 文件系统机制

    在Linux系统中,每个分区都是一个文件系统,都有自己的目录层次结构.Linux的最重要特征之一就是支持多种文件系统,这样它更加灵 活,并可以和许多其它种操作系统共存.由于系统已将Linux文件系统的 ...

  5. Linux中的中断管理机制

    1.中断相关基础知识介绍 1.1.中断产生背景 假设现在CPU需要去获取一个键盘的时间,如果处理器发出一个请求信号之后一直在轮询键盘的响应,由于键盘响应速度比处理器慢得多并且需要等待用户输入,这对于C ...

  6. 浅析操作系统和Netty中的零拷贝机制

    点击关注公众号,Java干货及时送达 零拷贝机制(Zero-Copy)是在操作数据时不需要将数据从一块内存区域复制到另一块内存区域的技术,这样就避免了内存的拷贝,使得可以提高CPU的.零拷贝机制是一种 ...

  7. 想读读PyTorch底层代码?这份内核机制简介送给你

    学习 PyTorch 比较简单,但你能学习 PyTorch 内部机制吗?最近,有 14 年 ML 经验的大神 Christian 介绍了 PyTorch 的内核机制.虽然在实际使用中并不需要这些知识, ...

  8. pytorch 使用cpu_想读读PyTorch底层代码?这份内核机制简介送给你

    机器之心报道 参与:思源 学习 PyTorch 比较简单,但你能学习 PyTorch 内部机制吗?最近,有 14 年 ML 经验的大神 Christian 介绍了 PyTorch 的内核机制.虽然在实 ...

  9. 解析 Linux 中的 VFS 文件系统机制

    简介: 本文阐述 Linux 中的文件系统部分,源代码来自基于 IA32 的 2.4.20 内核.总体上说 Linux 下的文件系统主要可分为三大块:一是上层的文件系统的系统调用,二是虚拟文件系统 V ...

最新文章

  1. These dependencies were not found: *!!vue-style-loader!css-loader?
  2. oracle11g 数据库导出报“ EXP-00003: 未找到段 (0,0) 的存储定义”错误的解决方案
  3. 解惑图数据库!你知道什么是图数据库吗?
  4. 使用GZIP和压缩数据
  5. 大王——有趣干货集合
  6. vue怎么根据id获取组件_vue子组件,如何根据父组件传进来的id,去查询详情,并在子组件中显示?...
  7. Linux--信号阻塞与屏蔽
  8. 深圳地图echarts
  9. 单片机---HLK-W801并口驱动ST7789
  10. R语言保存EXCEL小技巧
  11. 【程序员思维】用两个鸡蛋判断鸡蛋在多少楼层扔下不会碎
  12. 如何恢复删除的微信记录?恢复删除记录的方法
  13. 实习第一天——网宿报到日
  14. file_get_contents failed to open stream: HTTP request failed! HTTP/1.1 400 Bad Request
  15. 心流体验:专注的快乐--读书笔记
  16. CGLib中类Enhancer介绍
  17. ZGC-一款为开拓JAVA新疆土的垃圾回收器
  18. springboot、quartz定时任务未触发
  19. python数字排序_python数字排序
  20. 192.168.和10.0.开头的IP、内网IP段、IP简介、分类

热门文章

  1. Botnet趋势典型攻击链
  2. 小练习使用html 中table表格 实现个人简历
  3. 鸡尾酒问题,最小二乘法和范数的思考
  4. MavSDKMavros学习笔记
  5. grub.cfg使用说明
  6. 「Slack」- 安装 @20210303
  7. linux下google浏览器字体不清晰,google浏览器的字体模糊的原因是什么_怎么解决 - 驱动管家...
  8. 最新 955 不加班的公司名单(2022 版)
  9. mysql大写和小写_MySQL大写和小写问题
  10. laydate点击输入框闪一下不见了_ps文字工具打字不显示,应该如何解决?