高级会员
- 积分
- 728
- 金钱
- 728
- 注册时间
- 2015-8-20
- 在线时间
- 58 小时
|
本帖最后由 zuozhongkai 于 2020-4-13 16:58 编辑
在家学习原子哥的linux视频.......学到了第一个字符设备开发(chrdevbase)
左老师对整个字符设备驱动开发的原理讲的很详细。
在他的基础上,有了一些自己的理解,在这里分享给大家。
1.回顾一下从驱动到LED亮灭实验完成,我们做了哪些事情
①在驱动中
#define LED_MAJOR 200 /* 主设备号 */---------以后我们这个设备在系统的设备节点上就是c-200-0(字符-主-次设备)
#define LED_NAME "led" /* 设备名字 */
led_switch----(操作寄存器,控制灯亮灭)
led_open----(空的)
led_read----(空的)
led_write----(操作有点多,回头再说)
led_release---(空的)
struct file_operations led_fops ---(几个赋值)
------(让open=这个模块的led_open)
------(让read=这个模块的led_read)
------(让write=这个模块的led_write)
------(让release=这个模块的led_release)
led_init-----(操作几组寄存器,为LED的亮灭做准备)
led_exit----(断开了几个地址映射)
module_init(led_init)---------模块注册------insmod 模块时调用
module_exit(led_exit)--------模块注销------rmmod 模块时调用
②在测试代码中
if(argc != 3)----判断运行APP时的参数是不是3个----- ./ledAPP /dev/led 1
filename = argv[1]; ----------将/dev/led赋给filename
fd = open(filename, O_RDWR);--------调用open-----和驱动中的结构体赋值对应起来看,它是led_open吗?
if(fd < 0)---------一个判断
databuf[0] = atoi(argv[2]); ----------一个类型转换
retvalue = write(fd, databuf, sizeof(databuf));--------调用write--------和驱动中的结构体赋值对应起来看,它是led_write吗?
if(retvalue < 0)-------一个判断
close(fd);---------关闭文件。
③在开发板中的操作
insmod led.ko
mknod /dev/led c 200 0---在系统中创建设备文件
./ledAPP /dev/led 1 ---------运行ledAPP,并给两个参数,argv[0]=/dev/led,argv[1]='1'
以上三个操作,就实现了LED的亮灭。
针对这个过程我们提出几个值得我们思考的问题:
①执行insmod后,linux做了什么?---------linux加载了我们的驱动模块
②执行mknod 后,linux做了什么?-------linux在设备节点的c-200-0上做了一个命名,命名为/dev/led,以后/dev/led就=该设备
③运行APP,linux做了什么?---------------linux把/dev/led和1传递给APP,并执行了APP这个程序。
--------------------------------------------------------------------------------------------------------------------------------------------
从字面上看没有问题。我又有几个问题:
①linux把设备节点c-200-0命名为/dev/led,那这个和具体的LED是怎么建立对应关系的?
------mknod后,设备节点c-200-0的设备就是/dev/led
------在运行APP时,把/dev/led传递给了APP,相当于给APP传了一个c-200-0的设备号,
------在众多驱动中,c-200-0的驱动就对应led.ko
------APP对/dev/led这个设备的使用系统调用函数open、write等的时候就注定了要和led.ko中的led_open、led_write函数。
②为什么要insmod led.ko去加载模块?
------linux用户空间和内核空间是分开的。
------单纯一个led.ko文件,是磁盘(或者flash)上的一堆数据,要把这对数据加载到内存(ddr-RAM)中(的内核空间)去。
---------------------------------这几个问题搞清楚了,我们再来看一个很有意思的编程思想---------------------------------------
这个问题是这样产生的:
系统调用write函数怎么和我们的led_write函数建立联系的呢?或者说
那么多驱动的XXX_write函数,系统怎么就知道了一个设备号就知道用哪个_write呢?
我们看一个例子
--------------------------------------------------注意这只是说一个编程思想-------------------------------------
#include <stdio.h>
struct _worker{
unsigned int worker_id; //员工ID
unsigned int work_day; //工作天数
unsigned int(*jiesuan)(unsigned int days);//工资结算公式
unsigned int money; //本月的工资
};
typedef struct _worker worker;
unsigned int jiesuan_1(unsigned int days);
unsigned int jiesuan_2(unsigned int days);
unsigned int jiesuan_3(unsigned int days);
int main(int argc,char argv[])
{
worker zhangsan,lisi;
zhangsan.worker_id = 1387;
zhangsan.work_day = 35;
zhangsan.jiesuan = &jiesuan_1;
printf("zhangsan's money=%d\r\n",zhangsan.jiesuan(zhangsan.work_day));
lisi.worker_id = 0105;
lisi.work_day = 29;
lisi.jiesuan = &jiesuan_3;
printf("lisi's money=%d\r\n", lisi.jiesuan(lisi.work_day));getchar();
}
unsigned int jiesuan_1(unsigned int days) { return 100 * days;}
unsigned int jiesuan_2(unsigned int days) { return 200 * days;}
unsigned int jiesuan_3(unsigned int days) { return 300 * days;}
-------------------------------------------------------------------------------------------------
我们在对(有函数指针的结构体)赋值的时候,它的函数指针指其实是指向一类函数。
对于不同的结构体变量a和b,
a.jiesuan和b.jiesuan就是不同的函数,以至于a.jiesuan(20)和b.jiesuan(20)的结果是不一样的。
---------------------------------------------------------------------------------------------------------------------
初学者很生硬的理解,必定有很多不严谨的地方,欢迎大家批评指正!
|
|