嵌入式驱动day02

回顾day01
1.搭建环境
    编译器
    内核编译
    tftp     nfs
2.内核模块的编译
    驱动程序工作于内核态
    2.1直接编译进内核
    2.2编译成内核模块
         1).c放到内核源码目录中修改对应的Kconfig Makefile
         2).c放到自己创建的目录,完成简单的Makefile
              obj-m      += xxx.o
              make -C /opt/kernel M=$(pwd) modules
         3).完成Makefile
              make 就可以完成

day02
    
  
  
  1. #include <linux/init.h>
  2. #include <linux/module.h>
  3. MODULE_LICENSE("GPL")
  4. int __init helloworld_init(void){
  5. printk("hello world\n");
  6. return 0;
  7. void __exit helloworld_exit(void){
  8. printk("bye bye\n");
  9. }
  10. moudle_init(helloworld_init);
  11. moudle_exit(helloworld_exit);
1.__init       ->      #define __init     __section(.init.text)_cold notrac...
    vmlinux.lds链接文件
    vi     arch/arm/kernel/vmlinux.lds
    __init 定义的函数放到了.init.text段中去
    __exit定义的函数放到了.exit.text段中去
    被以上两个宏修饰的代码段执行一次后释放对应的内存资源
    被module修饰的函数在加载模块时会调用到
    被module修饰的函数在卸载模块时会调用到
----------------------------------------------------------------------------------
1.内核导出符号
    a.c     int max(int,int)
    b.c     main()      {      max(...);      }
    第一种方式
         在b.c extern int max(int,int);
    第二种方式
         a.h extern int max(int,int);
         b.c     #include      "a.h"
内核中导出符号 EXPORT_SYMBOL
esdexp.c
   
   
  1. #include<linux/init.h>
  2. #include<linux/module.h>
  3. MODULE_LICENSE("GPL")
  4. int esdexp_mult(int x,int y){
  5. printk("enter esdexp_mult!\n");
  6. return x*y;
  7. }
  8. int esdexp_divd(int x ,int y){
  9. printk("enter esdexp_divd");
  10. return x/y;
  11. }
  12. /*导出符号*/
  13. EXPROT_SYMBOL(esdexp_mult);
  14. EXPROT_SYMBOL(esdexp_divd);
esdimp.c
   
   
  1. #include<linux/init.h>
  2. #include<linux/module.h>
  3. MODULE_LICENSE("GPL");
  4. extern int esdexp_mult(int,int);
  5. extern int esdexp_divd(int,int);
  6. int __init esdimp_init(void){
  7. int result=0
  8. printk("enter esdimp_init!\n");
  9. //int result=0; 局部变量
  10. result=esdexp_mult(10,17);
  11. printk("result %d\n",result);
  12. return 0;
  13. }
  14. void __exit esdimp_exit(void){
  15. int result=0;
  16. esdexp_divd(10,2);
  17. printk("result %d\n",result);
  18. }
  19. module_init(esdimp_init);
  20. module_exit(esdimp_exit);
@注意局部变量如此编译那么会编译不过,ISO90局部变量要放在可执行代码之前
编译后顺序安装
insmod esdexp.ko
insmod esdimp.ko
lsmod //查看
rmmod esdimp.ko
rmmod esdexp.ko

2.模块参数
    内核提供了一种机制:在用户空间可以修改内核模块中全局变量的值.
   
   
  1. #include<linux/init.h>
  2. #include<linux/module.h>
  3. MODULE_LICENSE("GPL");
  4. static short mpshort=1;
  5. static int mpint=10;
  6. static char* mpstring="hello";
  7. static int mparry[2]={100,200};
  8. /*模块参数的声明*/
  9. //module_param(name,type,perm);
  10. //name参数名 type 变量类型名 perm 用户炒作的权限
  11. module_param(mpshort,short,S_IRWXU);
  12. module_param(mpint,int,S_IRWXU);
  13. module_param(mpstring,charp,S_IRWXU);
  14. //module_param_array(name.type,nump,perm);
  15. //nump 如果数组在参数加载时设置,该值为加载时设置的数据个数,
  16. 不允许传递闭模块允许个数更多的值
  17. module_param_array(mparray,int,NULL,00700);
  18. init __init modparam_init(void){
  19. printk("mpshort=%d\n",mpshort);
  20. printk("mpint=%d\n",mpint);
  21. printk("mpstring=%s\n",mpstring);
  22. printk("mparray=%d %d\n",mparray[0],mparray[2]);
  23. return 0;
  24. }
  25. void __exit modparam_exit(void){
  26. printk("mpshort=%d\n",mpshort);
  27. printk("mpint=%d\n",mpint);
  28. printk("mpstring=%s\n",mpstring);
  29. printk("mparray=%d %d\n",mparray[0],mparray[2]);
  30. }
  31. module_init(modparam_init);
  32. module_exit(modparam_exit);

编译代码
insmod modparam.ko mpshort=100 mpint=200 mpstring="world" mparray=300,400
安装上后查看文件
ls /sys/module/moduleparam/parameters/ -l
echo 55>mpshort
cat mpshort
rmmod modparam.ko(注意:在用户空间可以修改内核模块中全局变量的值)


注意模块参数所支持的数据类型如下

                                  
 

3.内存管理
    逻辑地址:汇编文件中使用的偏移地址
    虚拟地址(线性地址):UC编程时使用的地址为虚拟地址
                                            每个进程都0-4G独立空间 0-3G用户 3-4G内核地址
                                            内核驱动开发时使用的也是虚拟地址。
    物理地址:出现在地址总线上的值
 段式管理单元:
    16位cpu,内部地址总线是20bits,寻址能力1M,内部寄存器是16bits
      段基址寄存器 CS DS SS ES
     段内偏移寄存器     IP SP BX 
     虚拟地址=段基址<<4+段内偏移
    32cpu,内部总线32bits,内部寄存器是32bits
         实模式:类似于以上16位CPU的段式管理单元
         保护模式:绝大多数工作时间内工作处于保护模式

 
 虚拟地址和物理内存的关系(页式管理单元)
 #linux内核中没有(有限的)使用了段式管理机制,充分使用了页式管理机制
     linux始终认为基地址寄存器值为零,逻辑地址=虚拟地址
    充分使用了页式管理机制:四级页表
    linux内存管理的最小单元:页。通常32位CPU一页为4K
            struct page
内核中内存的分配:
    用户空间
         malloc/free
         new/delete
         valloc
   内核空间
         kmalloc/kfree
         vmalloc/vfree(申请到的物理空间可能不连续)
         __get_free_pages/free_pages      内存页  


   
   
  1. #inlcude<linux/init.h>
  2. #include<linux/module.h>
  3. #include<linux/fs.h>
  4. #include<linux/slab.h>
  5. #include<linux/vmalloc.h>
  6. MODULE_LICENSE("GPL");
  7. unsigned char * kernelkmalloc=NULL;
  8. unsigned char * kernelpagemem=NULL;
  9. unsigned char * kernelvmalloc=NULL;
  10. #define PAGE_NUM 4
  11. init __init kernelspace_init(void){
  12. int ret=-ENOMEM;
  13. kernelkmalloc=(unsigned char*)kmalloc(x,GFP_KERNEL);
  14. /*
  15. void * kmalloc(size_t size,gfp_t flags)
  16. 参数一是空间大小,
  17. 参数二 GFP_KERNEL分配内存,分配过程中可能导致睡眠(中断上下文中是不允许睡眠的)
  18. GFP_ATOMIC分配过程中不会导致睡眠
  19. GFP_DMA 申请到的内存位于0~16M之间
  20. __GFP_HIGHMEM 申请高端内存(896M以上的物理内存)
  21. */
  22. if(IS_ERR(kernelkmalloc)){
  23. printk("kmalloc failed!\n");
  24. ret=PTR_ERR(kernelkmalloc);
  25. goto failure_kmalloc;
  26. }
  27. printk("kmalloc space:0x%lx!\n",(unsigned long)kernelkmalloc);
  28. kernelpagemem=(unsigned char*)__get_free_pages(GFP_KERNEL,PAGE_NUM);
  29. if(IS_ERR(kernelpagemem)){
  30. printk("get free page failed!\n");
  31. ret = PTR_ERR(kernelpagemem);
  32. goto failure_get_free_pages;
  33. }
  34. kernelvmalloc=(unsigned char*)vmalloc(1024*10);
  35. if(IS_ERR(kernelvmalloc)){
  36. printk("Vmalloc failed\n");
  37. ret = PTR_ERR(kernelvmalloc);
  38. goto failure_vmalloc;
  39. }
  40. return 0;
  41. failure_vmalloc:
  42. free_page((unsigned long)kernelpagemem,PAGE_NUM);
  43. failure_get_free_pages:
  44. kfree(kernelkmalloc);
  45. failure_kmalloc:
  46. return ret;
  47. }
  48. void __exit kernelspace_exit(void){
  49. vfree(kernelvmalloc);
  50. free_page((unsigned long)kernelpagemem,PAGE_NUM);
  51. kfree(kernelkmalloc);
  52. }
  53. module_init(kernel_init);
  54. module_exit(kernel_exit);

注意:这里需要注意内存泄漏的问题

4内核链表
    struct student{
         char name[20];
         int age;
         int sex;
          ...
           struct student *next
     }
    链表的增删改查
    struct employee{
         char name[20];
         int age;
         int sex;
         int id;
         ...
         struct employ *next;
    }
    include/linux/list.h
    struct list_head{
         struct list_head *next,*prev;
    } ;
 
  
  
  1. #include<linux/init.h>
  2. #include<linux/module.h>
  3. #include<linux/slab.h> //kmalloc
  4. #include<linux/list.h> //list
  5. #include<linux/error.h>
  6. MODULE_LICENSE("GPL");
  7. #define EMPLOYEE_NUM 10
  8. struct employee{
  9. char name[20];
  10. int id;
  11. int salary;
  12. int age;
  13. struct list_head list;
  14. }
  15. /*定义链表头节点*/
  16. struct list_head employee_list;
  17. struct employee *employeep=NULL;
  18. struct list_head *pos=NULL;
  19. struct employee *employee_temp=NULL;
  20. int __init listtest_init(void){
  21. int i=0;
  22. /*初始化链表头节点*/
  23. INIT_LIST_HEAD(&employee_list);
  24. /*申请employee的空间*/
  25. employeep=kmalloc(sizeof(struct employee)*EMPLOYEE_NUM,GFP_KERNEL);
  26. if(IS_ERR(employeep)){
  27. printk("kmalloc failed!\n");
  28. return -ENOMEM;
  29. }
  30. //动态申请完一定要重新初始化
  31. memset(kmalloc,0,sizeof((struct employee)*EMPLOYEE_NUM));
  32. /*初始化每个struct*/
  33. for(;i<EMPLOYEE_NUM;i++){
  34. sprintf(employee[i].name,"employee%d",i);
  35. /*添加节点到链表中去*/
  36. list_add(&(employee[i].list),&employee_list));
  37. }
  38. /*链表节点的遍历*/
  39. list_for_each(pos,&employee_list){
  40. employee_temp=list_entry(pos,struct employee,list);
  41. printk("employee name:%s\n",employee_temp->name);
  42. }
  43. return 0;
  44. }
  45. void __exit listtest_exit(void){
  46. /*
  47. int i=0;
  48. for(;i<EMPLOYEE_NUM;i++)
  49. list_del(&(employrrp[i].list));
  50. */
  51. kfree(employeep);
  52. }
  53. module_init(listtest_init);
  54. module_exit(listtest_exit);

    INIT_LIST_HEAD
    list_add
    list_del
    list_for_each
    list_entery(?)
    1)list_for_each(结束条件为什么是pos!=(head))
    2)增加节点的0,1,2,3..... 
         遍历时9,8,7,...

























  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值