内核模块结构体初始化中函数指针相关技术疑问
关于内核模块
file_operations结构体初始化的疑问解答 嘿,这个问题问到点子上了!内核里这种指定初始化器的风格,尤其是函数指针部分,确实容易让刚接触的人产生困惑,我来逐个给你掰明白:
一、函数指针初始化的无缝工作机制
其实本质就是C语言的**指定初始化器(Designated Initializer)**特性,加上函数名本身就是函数地址的特性。
咱们拆解来看:
- 结构体
struct file_operations里的.write成员是一个函数指针类型(比如ssize_t (*write)(struct file *, const char __user *, size_t, loff_t *)); - 而
sample_write这个函数名,在C语言里直接代表该函数代码在内存中的起始地址,完全符合函数指针的类型要求; - 所以
.write = sample_write本质就是把函数的地址赋值给结构体里对应的指针成员,和你给普通指针成员赋值(比如.private_data = some_ptr)的逻辑完全一致,只是这个指针指向的是代码段的函数,而非数据段的变量。
二、结构体的创建位置与生命周期
你定义的struct file_operations sample_fops是在文件作用域(也就是所有函数外面)声明的,属于静态存储期的变量:
- 它会在你的内核模块被加载到内核时,就完成初始化,存储在模块的
.data段(如果你给结构体加了const修饰,比如const struct file_operations sample_fops,就会放在.rodata只读数据段,内核里推荐这么做,因为文件操作结构体通常不会被修改); - 它的生命周期和整个模块绑定:从模块加载成功开始,直到模块被卸载才会被释放。
三、函数参数的访问问题
你完全不用纠结这个!这里的初始化只是把函数的地址存在结构体里,和函数调用时的参数传递是完全独立的两个环节:
- 当内核需要处理写操作时,会调用
sample_fops.write(file, buf, len, pos),这时候内核会把对应的参数(比如缓冲区指针buf、长度len这些)传递给sample_write函数; - 你在
sample_write函数里可以正常访问这些参数,就像你直接调用sample_write(file, buf, len, pos)一样,和结构体的初始化风格没有任何关系。
四、多次调用时结构体是否唯一
是的,整个模块里sample_fops就是同一个结构体实例。因为它是文件作用域的全局变量(如果没加static就是模块内全局,加了static就是当前文件内全局),不管内核多少次触发读写操作、调用这个结构体里的函数指针,都是指向同一个结构体对象。
五、内存位置的细节
sample_fops结构体本身:如果是非const全局,在模块的.data段(可读写数据区);如果是const修饰,在.rodata段(只读数据区);- 结构体里的函数指针:存储的是对应函数在
.text段(代码段)的地址,而sample_read、sample_write这些函数的代码本身就放在.text段,属于只读的内存区域。
内容的提问来源于stack exchange,提问作者Yusuf Gürkan Bor
相关产品推荐
相关产品推荐

