news 2026/6/16 2:30:41

访问设备文件与调用驱动函数是怎样关联的?

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
访问设备文件与调用驱动函数是怎样关联的?

author: hjjdebug
date: 2026年 06月 15日 星期一 10:28:30 CST
descrip: 访问设备文件与调用驱动函数是怎样关联的?


文章目录

  • 1. 应用层
  • 2. 内核层(提供虚拟文件系统, 实现函数转发)
  • 3. 驱动层(实现回调函数).
  • 4. 执行结果:

我对于打开一个设备文件,对其read,write 就能调用到驱动中的read,write函数,这一点一直理解不太深刻,
今天就给一个实例,演示内核通过怎样的机制实现的函数回调.
是的,你注册了什么函数,我就回调什么函数. 但如何做到的呢?
真正的内核实现太过复杂, 我们这里是一个纯应用层模拟,但这不影响对转发机制的理解.

架构分三层. 应用层,内核层(实现函数转发),驱动层

1. 应用层

main 函数.应用层先向内核注册文件名和FileOps对象
然后调用内核层提供的函数 sys_open, sys_read, sys_write, sys_close

2. 内核层(提供虚拟文件系统, 实现函数转发)

关键: 维护着一张文件名到设备操作集的映射表.
对外提供系统接口,
根据文件名称,找到操作集指针,
对内提供函数转发,根据注册的对象, 调用驱动层函数.
内核层是红娘.

3. 驱动层(实现回调函数).

每个驱动都需要提供一张函数表地址,叫FileOps 对象,
这个FileOps 对象和驱动的名字需要注册给系统(这叫初始化, 例子是在应用层注册的)
实现了myopen,myread,mywrite,myclose.函数
是sys_open,sys_read,sys_write,sys_close的下半部实现.
这里都是回调函数,等着应用来调用.

源代码:

$cat simulate_drv.c#include<stdio.h>#include<string.h>#include<stdlib.h>// ===================== 1. 模拟驱动层:文件操作函数集(对应内核 file_operations) =====================// 模拟驱动回调函数原型typedefint(*dev_open_t)(constchar*path);typedefssize_t(*dev_read_t)(char*buf,size_t len);typedefssize_t(*dev_write_t)(constchar*buf,size_t len);typedefint(*dev_release_t)(void);// 模拟内核 struct file_operationstypedefstruct{dev_open_t open;dev_read_t read;dev_write_t write;dev_release_t release;}FileOps;// -------- 模拟一个自定义设备驱动 /dev/mydev 回调实现 --------intmydev_open(constchar*path){printf("[驱动 /dev/mydev] 设备被打开: %s\n",path);return0;}ssize_tmydev_read(char*buf,size_t len){constchar*str="data from driver";size_t copy_len=strlen(str);if(copy_len>len)copy_len=len;memcpy(buf,str,copy_len);printf("[驱动 /dev/mydev] 执行 read\n");returncopy_len;}ssize_tmydev_write(constchar*buf,size_t len){printf("[驱动 /dev/mydev] 执行 write, 收到数据: %.*s\n",(int)len,buf);returnlen;}intmydev_release(void){printf("[驱动 /dev/mydev] 设备被关闭\n");return0;}// 该设备绑定的操作函数集,每个驱动都需要提供一张函数表地址,叫FileOps 对象FileOps mydev_fops={.open=mydev_open,.read=mydev_read,.write=mydev_write,.release=mydev_release};// ===================== 2. 模拟 VFS 层(内核转发核心) =====================// 设备节点映射表:路径名 -> 对应的驱动操作集typedefstruct{charpath[64];FileOps*ops;}DevMap;#defineMAX_DEV8DevMap sys_map[MAX_DEV];//核心架构intdev_cnt=0;// VFS:注册设备节点(对应内核 cdev_add / device_create)voidsys_register_dev(constchar*path,FileOps*ops){if(dev_cnt>=MAX_DEV)return;strncpy(sys_map[dev_cnt].path,path,63);sys_map[dev_cnt].ops=ops;dev_cnt++;printf("[VFS] 注册设备节点: %s\n",path);}// VFS:根据路径查找对应驱动操作集FileOps*sys_lookup(constchar*path){for(inti=0;i<dev_cnt;i++){if(strcmp(sys_map[i].path,path)==0){returnsys_map[i].ops;}}returnNULL;}// -------- VFS 对外提供接口(模拟系统调用) 对内提供转发调用 --------intsys_open(constchar*path){FileOps*ops=sys_lookup(path);if(!ops){printf("[VFS] 找不到设备: %s\n",path);return-1;}printf("[VFS] 转发 open 请求到驱动\n");returnops->open(path);}ssize_tsys_read(constchar*path,char*buf,size_t len){FileOps*ops=sys_lookup(path);if(!ops)return-1;printf("[VFS] 转发 read 请求到驱动\n");returnops->read(buf,len);}ssize_tsys_write(constchar*path,constchar*buf,size_t len){FileOps*ops=sys_lookup(path);if(!ops)return-1;printf("[VFS] 转发 write 请求到驱动\n");returnops->write(buf,len);}intsys_close(constchar*path){FileOps*ops=sys_lookup(path);if(!ops)return-1;printf("[VFS] 转发 close 请求到驱动\n");returnops->release();}// ===================== 3. 应用层(用户进程) =====================intmain(void){charrbuf[128]={0};constchar*wbuf="hello driver";// 1. 驱动向 VFS 注册设备节点(insmod 时或者probe时,驱动向内核注册时调用)sys_register_dev("/dev/mydev",&mydev_fops);printf("------------------------\n");// 2. 应用层调用文件接口(模拟 open/read/write/close 系统调用)sys_open("/dev/mydev");sys_read("/dev/mydev",rbuf,sizeof(rbuf));printf("[应用] 读到内容: %s\n",rbuf);sys_write("/dev/mydev",wbuf,strlen(wbuf));sys_close("/dev/mydev");return0;}

4. 执行结果:

$ ./simulate_drv
[VFS] 注册设备节点: /dev/mydev
------------------------
[VFS] 转发 open 请求到驱动
[驱动 /dev/mydev] 设备被打开: /dev/mydev
[VFS] 转发 read 请求到驱动
[驱动 /dev/mydev] 执行 read
[应用] 读到内容: data from driver
[VFS] 转发 write 请求到驱动
[驱动 /dev/mydev] 执行 write, 收到数据: hello driver
[VFS] 转发 close 请求到驱动
[驱动 /dev/mydev] 设备被关闭

阅读,调试完这个程序,
我们就深刻的知道,内核层其实没有干什么,它只是根据驱动名称找到操作对象.
根据调用入口转去调用注册的函数.
而函数要实现什么功能,那是驱动中函数的任务.

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

如何突破OBS分辨率限制:Spout2插件完整解决方案

如何突破OBS分辨率限制&#xff1a;Spout2插件完整解决方案 【免费下载链接】obs-spout2-plugin A Plugin for OBS Studio to enable Spout2 (https://github.com/leadedge/Spout2) input / output 项目地址: https://gitcode.com/gh_mirrors/ob/obs-spout2-plugin 你是…

作者头像 李华
网站建设 2026/6/16 2:24:55

XUnity自动翻译器:打破游戏语言壁垒的智能翻译插件

XUnity自动翻译器&#xff1a;打破游戏语言壁垒的智能翻译插件 【免费下载链接】XUnity.AutoTranslator 项目地址: https://gitcode.com/gh_mirrors/xu/XUnity.AutoTranslator 你是否曾因为语言障碍而错过那些精彩的海外游戏&#xff1f;面对满屏的外文剧情和菜单&…

作者头像 李华
网站建设 2026/6/16 2:20:49

Liouville CFT中的缺陷算子与边界态研究

1. Liouville CFT中的缺陷算子与边界态概述Liouville共形场论作为一类重要的非紧致二维共形场论&#xff0c;其独特的数学结构和物理内涵在理论物理研究中占据着核心地位。这个理论描述了一个具有指数相互作用的标量场在二维曲面上的量子行为&#xff0c;其作用量可以表示为&am…

作者头像 李华
网站建设 2026/6/16 2:17:49

CodeX使用技巧6-调试

CodeX使用技巧6-调试 打断点&#xff0c;找到具体报错的代码行&#xff0c;看报什么错 不要一直问下去

作者头像 李华
网站建设 2026/6/16 2:09:20

蓝绿部署与金丝雀部署的区别

蓝绿部署与金丝雀部署的区别 一、核心概念对比维度蓝绿部署金丝雀部署核心思想两套完全独立的环境&#xff0c;一键切换灰度放量&#xff0c;逐步替换切换方式路由切换&#xff08;瞬间完成&#xff09;流量百分比逐步调整回滚速度极快&#xff08;秒级&#xff0c;切回即可&am…

作者头像 李华