| 
 
高级会员 
 
	积分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)的结果是不一样的。
 ---------------------------------------------------------------------------------------------------------------------
 初学者很生硬的理解,必定有很多不严谨的地方,欢迎大家批评指正!
 
 
 
 
 
 
 
 
 
 
 
 
 | 
 |