Linux 内核辅助工具函数盘点:container_of 宏的解析与应用-LINUX

首页 2024-07-07 18:26:45

扫描关注一起学嵌入式,一起学习,一起成长

Linux内核是独立的软件,他没有使用任何C语言库,他自己实现了好多工具和辅助工具。

本系列文章将盘点一些内核提供的辅助工具函数。在编撰驱动程序时linux 驱动 内核,我们可以借助内核提供的工具函数,便捷实现目标功能。

宏container_of

这个宏定义十分出名,很多文章对齐进行了解析,但是这个宏在内核和驱动中常常看见。

该宏的作用是通过结构体成员的地址和结构体类型推导入结构体的地址。

在linux源码的toolsincludelinuxkernel.h文件下linux 驱动 内核,container_of()的定义如下:

<span style="color: #5c6370;font-style: italic;line-height: 26px">#define?offsetof(TYPE,?MEMBER)?((size_t)?&amp;((TYPE?*)0)-&gt;MEMBER)</span><br><br><span style="color: #5c6370;font-style: italic;line-height: 26px">#define?container_of(ptr,?type,?member)?({???</span><br>??const?typeof(((<span style="color: #e6c07b;line-height: 26px">type</span>?*)0)-&gt;member)?*?__mptr?=?(ptr);?<br>??(<span style="color: #e6c07b;line-height: 26px">type</span>?*)((char?*)__mptr?-?offsetof(<span style="color: #e6c07b;line-height: 26px">type</span>,?member));?})<br>

宏的参数分别为:type是指结构体的类型,member是成员在结构体中的名子,ptr是该成员在type结构体中的地址。

宏container_of主要用在内核的通用容器中。

该宏的详尽介绍可以参考:

数组

数组有两种类型:

内核中实现了循环单向数组,这个结构就能实现FIFO和LIFO。假如要使用内核提供的数组操作函数,代码中须要添加头文件。

内核中数组的实现核心部份数据结构structlist_head定义为:

struct?list_head<br>{<br>?struct?list_head?*next,?*prev;<br>}<br>

structlist_head数据结构不包含数组节点的数据区,一般是用在数组头或则嵌入到其他数据结构中。

创建和初始化数组有两种方式:动态创建和静态创建。

动态方式创建并初始化数组方式如下:

struct?list_head?mylist;<br>INIT_LIST_HEAD(&amp;mylist);<br>

INIT_LIST_HEAD()展开如下:

static?inline?void?INIT_LIST_HEAD(struct?list_head?*list)<br>{<br>????list-&gt;next?=?list;<br>????list-&gt;prev?=?list;<br>}<br>

静态创建数组通过LIST_HEAD宏完成:

LIST_HEAD(mylist)<br>

LIST_HEAD的定义如下:

<span style="color: #5c6370;font-style: italic;line-height: 26px">#define?LIST_HEAD(name)?</span><br>???struct?list_head?name?=?LIST_HEAD_INIT(name)<br>

其中LIST_HEAD_INIT展开为:

<span style="color: #5c6370;font-style: italic;line-height: 26px">#define?LIST_HEAD_INIT(name)?{?&amp;(name),?&amp;(name)?}</span><br>

把next和prev表针都初始化并指向自己,这样便初始化了一个带头节点的空数组。

添加节点到数组中,内核提供了几个插口函数,如list_add()是把一个节点添加到表头,list_add_tail()是插入表尾。

void?list_add(struct?list_head?*new,?struct?list_head?*head)<br>list_add_tail(struct?list_head?*new,?struct?list_head?*head)<br>

内核提供的list_add用于向数组添加新项linux命令行,它是内部函数__list_add的包装。

static?inline?void?__list_add(struct?list_head?*new,?struct?list_head?*prev,struct?list_head?*next)<br>{<br>??next-&gt;prev?=?new;<br>??new-&gt;next?=?next;<br>??new-&gt;prev?=?prev;<br>??prev-&gt;next?=?new;<br>}<br>

删掉节点很简单:

void?list_del(struct?list_head?*entry);<br>

数组遍历

使用宏list_for_each_entry(pos,head,member)进行数组遍历。

参数解释head:数组的头节点;member:数据结构中数组structlist_head的名称;pos:用于迭代。它是一个循环游标,如同for(i=0;i中的i。

<span style="color: #5c6370;font-style: italic;line-height: 26px">#define?list_for_each_entry(pos,?head,?member)??</span><br><span style="color: #c678dd;line-height: 26px">for</span>?(pos?=?list_entry((head)-&gt;next,typeof(*pos),?member);?<br>??&amp;pos-&gt;member?!=?(head);?<br>???pos?=?list_entry(pos-&gt;member.next,typeof(*pos),?member))<br><br><span style="color: #5c6370;font-style: italic;line-height: 26px">#define?list_entry(ptr,?type,?member)??container_of(ptr,?type,?member)</span><br>

内核的睡眠机制

内核调度器管理要运行的任务列表,这被叫做运行队列。睡眠进程不再被调度,由于已将它们从运行队列中移除。除非其状态改变(唤起),否则睡眠进程将永远不会被执行。

进程一旦步入等待状态,就可以释放处理器,一定要确保有条件或其他进程会唤起它。Linux内核通过提供一组函数和数据结构来简化睡眠机制的实现。

等待队列

Linux内核提供了一个数据结构,拿来记录等待执行的任务,那就是等待队列,主要用于处理被阻塞的I/O操作。其结构定义在include/linux/wait.h文件中:

struct?wait_queue_entry?{<br>?unsigned?int??flags;<br>?void???*private;<br>?wait_queue_func_t?func;<br>?struct?list_head?entry;<br>};<br>

其中,entry数组是一个数组,将步入睡眠的进程加入到这个数组中(在数组中排队),并步入睡眠状态。

处理等待队列也有两种形式:静态申明、动态申明,常用到的函数如下:

DECLARE_WAIT_QUEUE_HEAD(name)<br>

wait_queue_head_t?my_wait_queue;<br><br>init_waitqueue_head(&amp;my_wait_queue);<br>

/*?如果条件condition为真,则唤醒任务并执行。若为假,则阻塞?*/<br>wait_event_interruptible(wq_head,?condition)<br>

void?wake_up_interruptible(wait_queue_head_ts?*q)<br>

wait_event_interruptible不会持续协程,而只是在被调用时评估条件。若果条件为假,则进程将步入TASK_INTERRUPTIBLE状态并从运行队列中删掉。

当每次在等待队列中调用wake_up_interruptible时,就会重新复查条件。假如wake_up_interruptible运行时发觉条件为真,则等待队列中的进程将被唤起,并将其状态设置为TASK_RUNNING。

进程根据它们步入睡眠的次序唤起。要唤起在队列中等待的所有进程,应当使用wake_up_interruptible_all。

假如调用了wake_up或wake_up_interruptible,而且条件依然是FALSE,则哪些都不会发生。若果没有调用wake_up(或wake_up_interuptible),进程将永远不会被唤起。

工作队列

等待队列有了,Linux内核提供了工作队列,其中的work结构定义如下

struct?work_struct?{<br>?atomic_long_t?data;<br>?struct?list_head?entry;<br>?work_func_t?func;<br><span style="color: #5c6370;font-style: italic;line-height: 26px">#ifdef?CONFIG_LOCKDEP</span><br>?struct?lockdep_map?lockdep_map;<br><span style="color: #5c6370;font-style: italic;line-height: 26px">#endif</span><br>};<br>

其中,func为工作work的处理函数,其类型定义为:

<a style="color:#f60; text-decoration:underline;" href="https://www.php.cn/zt/58423.html" target="_blank">typedef</a>?void?(*work_func_t)(struct?work_struct?*work);<br>

Linux内核仍然运行着worker线程,他会对工作队列中的work进行处理。

定义并初始化一个work操作如下:

struct?work_struct?wrk;<br><br>INIT_WORK(_work,?_func)<br>

最新文章

  • Linux 内核辅助工具函数盘点:container_of 宏的解析与应用-LINUX

  • 常用命令:
    clear 清屏
    pwd Print Working Directory的缩写可以显示当前工作目录的全路径
    whoami 当前用户名称
    file -name- 显示详细信息
    
    文件相关命令:
    touch 创建文件
    cp 复制文件
    mv 移动文件、cp和mv可以在移动复制过程中修改文件名,mv可以完成重命名的功能
    rm 删除文件后,不通过回收站直接删除,基本找不到
    find 查找文件,-name 以文件名搜索
    chmod 修改文件权限
    r4 w2 x1
    例如:chmod 644 文件名
    显示:类型 

  • 家用服务器主机:隐患重重,慎选慎用!

  • 楼月备份管理,iTunes数据无忧掌控!

  • 深中跨市公交专线售票系统升级:按班次购票、新增班次查询功能-IT行业

  • 第一批阿里巴巴 50 湖南灾区救灾物资1万元,华容洞庭湖-IT产业

  • 虎牙直播怎么看直播间有多少人?常见问题

  • 相关文章

  • 常用命令:
    clear 清屏
    pwd Print Working Directory的缩写可以显示当前工作目录的全路径
    whoami 当前用户名称
    file -name- 显示详细信息
    
    文件相关命令:
    touch 创建文件
    cp 复制文件
    mv 移动文件、cp和mv可以在移动复制过程中修改文件名,mv可以完成重命名的功能
    rm 删除文件后,不通过回收站直接删除,基本找不到
    find 查

  • 深中跨市公交专线售票系统升级:按班次购票、新增班次查询功能-IT行业

  • 第一批阿里巴巴 50 湖南灾区救灾物资1万元,华容洞庭湖-IT产业

  • 虎牙直播怎么看直播间有多少人?常见问题

  • 虎牙直播怎么赚钱?常见问题

  • 如何直播虎牙直播游戏?常见问题

  • 虎牙直播如何投影电视-常见问题

  • wps如何设置行距很小-计算机软件

  • wps如何获得在线文档多人编辑-计算机软件

  • wps如何分隔线-计算机软件

  • wps如何在纸上缩放2页-计算机软件

  • wps如何将两页内容变成一页-计算机软件