news 2026/6/6 20:09:39

linux内核源码中module_init()定义的函数指针变量解引用

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
linux内核源码中module_init()定义的函数指针变量解引用

最近,我在看linux内核源码,读到了模块tty_io.c文件,其中有一个函数console_init()用于遍历访问加解引用访问了module_init()含参宏定义的函数指针变量。具体过程在这里捋一下。

首先,module_init()含参宏

module_init(x)-->__initcall(x)--->device_initcall(fn)--->__define_initcall("6", fn, 6)--->

#define __define_initcall(level, fn, id) \ static initcall_t __initcall_##fn##id __used \ __attribute__((__section__(".initcall" level ".init"))) = fn

定义了一个静态全局变量,并且与fn函数名表示的函数地址进行了绑定。

其次,再看看initcall_t的类型定义:

typedef int (*initcall_t)(void);

定义了一个函数指针类型,指向的函数返回值为int,参数列表为void.

因此模块源文件中使用module_init(x),最终定义了static修饰的全局变量,而且是一个占4字节的函数指针类型的变量,并且进行了初始化,绑定了函数x。

值得注意的事这个函数指针变量分配了字段属性,如果再定义一个链接脚本用来指导链接器将具有相同字段属性的代码链接放在一起,也就形成了好多个函数指针变量是“连续排布的”,这就为使用函数指针变量而非函数名的方式间接实现函数调用创造了条件

我在这里有一个错误理解:这里只是给变量分配了一个属性,至于链接器将相关属性的变量摆放在一起是由链接脚本指导完成的,而非连接器自动完成的。

另外就是,static修饰的静态全局函数,如果追踪查看发现没有内部函数通过使用变量名的方式实现“直接调用”,当然源文件之外的函数更加无法直接调用了(static的特性),表面上看是只完成了定义工作,没有访问动作。后面深层看,采用了一种高超不常用方式实现间接访问。(也说明了c语言不是绝对的而是相对的,c语言是灵活的,底层的,有时甚至是从内存角度直接考虑问题)。

再次,看看函数console_init():

void __init console_init(void) { initcall_t *call; /**函数指针变量 */ /* Setup the default TTY line discipline. */ tty_ldisc_begin(); /* * set up the console device so that later boot sequences can * inform about problems etc.. */ call = __con_initcall_start; /**这个就是initcall属性函数调用,而且是使用函数只恨间接调用的 */ while (call < __con_initcall_end) { (*call)(); call++; } }

这个函数体内部,定义了一个函数指针类型变量,并且使用了while循环对从__con_initcall_start~__con_initcall_end的每一个函数指针变量进行解引用后访问(*call()),最终实现了函数指针变量绑定的函数的调用。

最后,追踪看看__con_initcall_start、__con_initcall_end这两个函数指针变量。

使用,vscode的搜索功能(clangd可以非常快速找到),找到了如下内容:

源文件int.h中的:

extern initcall_t __con_initcall_start[], __con_initcall_end[];

首先c语言是一种强类型语言,无论是变量或者常量都要指定一个类型进行限制约束才能使用,也就说变量或者常量必须要有类型进行修饰/限制/约束才有意义,才能使用。这其实是函数指针常量声明(类型既可以修饰变量也可以修饰常量),声明了两个函数指针常量__con_initcall_start、__con_initcall_end,之所以使用数组方式盛行函数指针常量,是因为在这里数组这个概念比指针这个概念更贴切,暗含了对函数指针常量调用实际上是要进行数组相关操作。

链接脚本:vmlinux.lds中:

其中"."表示的就是代码段当前地址常量,这就话的含义就是链接脚本vmlinux.lds指导链接器将具有段属性为.con_initcall.init的变量放在一起。

看到这里基本上就捋清楚了整个过程,其实从宏观上也是面向对象的编写方法:各个各的,各完成各自的工作,然后进行打通链接(函数指针变量定义并初始化),这样整个过程就是通的了。

可以再看看我的另外一篇文章:

面向对象:linux内核中函数转数据的用法-CSDN博客文章浏览阅读199次,点赞4次,收藏5次。函数转数据”是我自己起的名称,可能不太准确,具体含义是:在一个源文件(模块)内部定义了好多static修饰的静态函数,一般用法源文件中另外一个函数去直接调用这个函数,但是“函数转数据”的用法是这样的:用到了函数指针变量,例如定义了一个结构体类型,结构体成员包含各种函数指针类型的变量,然后另一一个结构体类型的全局变量,全局变量初始化的时候被赋绑定的函数名,最后通过访问这个全局变量里面的成员,来间接实现对函数的访问。整个过程的好处就是各个各自的,互不干涉,另外就是实现了“隔离”,互补干涉。https://blog.csdn.net/duanjianbo3330/article/details/161655402?spm=1011.2124.3001.6209

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/6 20:08:31

芜湖Geo优化亲测品牌分享

行业痛点分析在当前的数字营销领域&#xff0c;Geo优化正逐渐成为企业竞争力的关键。然而&#xff0c;这一领域面临着诸多技术挑战。首先&#xff0c;AI搜索的算法不断更新&#xff0c;企业难以及时调整优化策略。其次&#xff0c;跨平台的矩阵化内容建设需要大量的资源和专业知…

作者头像 李华
网站建设 2026/6/6 20:04:01

5分钟搞定ESP32蓝牙音频库:打造你的专属蓝牙音箱

5分钟搞定ESP32蓝牙音频库&#xff1a;打造你的专属蓝牙音箱 【免费下载链接】ESP32-A2DP A Simple ESP32 Bluetooth A2DP Library (to implement a Music Receiver or Sender) that supports Arduino, PlatformIO and Espressif IDF 项目地址: https://gitcode.com/gh_mirro…

作者头像 李华
网站建设 2026/6/6 19:56:39

img2img

智慧衣橱虚拟试穿实践&#xff1a;从占位 UI到 Sophnet 双图图生图的落地思考一、写在前面&#xff1a;做智慧衣橱时&#xff0c;产品页面上最早写的是AI 试穿 / 图生图 接入中。很长一段时间里&#xff0c;我把试穿和小衣对话助手混在一起想——以为在聊天框里发一张衣服图&a…

作者头像 李华
网站建设 2026/6/6 19:55:33

企业法务部搭建诉讼管理看板的完整指南:从数据收集到可视化监控

&#x1f4cc; 摘要 企业法务部门每天面对大量诉讼案件&#xff0c;但案件信息分散在 Excel、邮件和微信群中&#xff0c;总部看不清全貌&#xff0c;子公司的案件上报总是迟缓缺漏。本文从企业法务的实际场景出发&#xff0c;详细讲解如何搭建一套诉讼案件管理看板——从数据采…

作者头像 李华
网站建设 2026/6/6 19:54:20

如何在3分钟内免费获得苹果专业字体?PingFangSC跨平台终极指南

如何在3分钟内免费获得苹果专业字体&#xff1f;PingFangSC跨平台终极指南 【免费下载链接】PingFangSC PingFangSC字体包文件、苹果平方字体文件&#xff0c;包含ttf和woff2格式 项目地址: https://gitcode.com/gh_mirrors/pi/PingFangSC 你是否曾羡慕Mac电脑上那些清晰…

作者头像 李华