zoukankan      html  css  js  c++  java
  • 模块参数,系统调用,字符设备编程重要数据结构,设备号的申请与注册,关于cdev的API

    1、模块参数
      应用编程:
          int main(int argc, char *argv[])
          {
             
          }
          ./a.out xxx yyy zzz
      内核编程:
          
          insmod xxx.ko  参数信息    
          
      希望在安装内核模块时也可以给其传递参数,需要使用模块参数
     
     
      模块参数的实现步骤:
         1)在模块中定义全局变量
         2)使用
            module_param(name,type,perm);或者
            module_param_array(name,type,nump,perm);
            将该变量声明为模块参数
            
      module_param(name,type,perm);
         name, 全局变量的名称
         type, 变量的类型
               内核支持声明为模块参数的变量类型 int short long charp
         perm, 访问权限
      module_param_array(name,type,nump,perm);  
         作用:将一个数组声明为模块参数
         name, 数组名称
         type, 数组成员变量的数据类型
         nump, 数组元素个数指针
         perm, 访问权限     
       
      实验步骤:
        insmod moduleparam.ko
        rmmod  moduleparam
        
        insmod moduleparam.ko irq=100  str="hello" fish=1,2,3,4,5   
       
        ls /sys/module/moduleparam/parameters/       无显示
        cd /sys/module/moduleparam/parameters/   
        cat fish
        cat irq
        echo 1234 >irq
        echo 11,22,33,44,55,66 >fish
        cd /
        rmmod moduleparam
      模块参数的使用场景:
        调试代码时使用
        
        REG1=irq;
        
        insmod xxx.ko irq= 100
        rmmod xxx.ko
        insmod xxx.ko irq=101;  

    2、系统调用
       谈谈对系统调用的理解?
       
       什么是系统调用?
           是操作系统提供应用编程者调用的一组特殊函数
       系统调用的作用?
           保护内核的安全
           保证用户空间以安全的方式调用内核中的函数
       系统调用是如何实现的
           
           在用户空间指定系统调用号
           产生软中断异常 陷入内核态执行
           执行异常向量表 跳转到软中断异常处理代码
           在软中断异常处理代码中根据系统调用号
           找到内核中的对应函数
           并且执行该函数
           执行完毕后将结果原路返回给用户空间
           
           
           系统调用号:arch/arm/include/asm/unistd.h
           软中断指令:
                       arm, swi/svc
                       intel, int
           异常向量表:arch/arm/kernel/entry-armv.S
           软中断异常处理代码:arch/arm/kernel/entry-common.S
           系统调用表:calls.S
      增加一个新的系统调用
         cd /home/tarena/driver/kernel/
         vi arch/arm/kernel/sys_arm.c
            asmlinkage int sys_add(int x, int y)
        {
            printk("<1>" "enter %s ", __func__);
            return x+y;
        }
         vi arch/arm/include/asm/unistd.h //增加新的系统调用号
            #define __NR_add   (__NR_SYSCALL_BASE+378)
         vi arch/arm/kernel/calls.S//更新系统调用表
            390                 CALL(sys_add)   
       make uImage
       让开发板使用新内核
       
       vi test.c
           
          open=("a.txt", O_RDWR) 等价于
          syscall(5, "a.txt", O_RDWR)
          
          read(fd, buf,len) 等价于
          syscall(3,fd,buf,len)
       arm-cortex_a9-linux-gnueabi-gcc test.c -o test
       cp test ../../rootfs
       
       ./test //注意使用新旧内核的区别
     

    3、字符设备驱动编程

      linux是使用C编程实现的
      但是内核实现过程中大量运用了面向对象的编程思想
     
     
      在linux中实现一个字符设备(例如 按键 鼠标 触摸屏 LCD 。。。)
      驱动程序,其实质就是实例化一个struct cdev类型的对象
     
      struct cdev
      {
         //设备号
         dev_t dev;
         const struct file_operations *ops;   
         ...
      }
     3.1 设备号
         本质是一个32bit的无符号数据
         
         设备号=主设备号(高12bit) + 次设备号(低20bit)
         
         主设备号,用于区分不同类型的设备
         次设备号,用于区分同一类设备中的不同个体
         
         ls /dev/ttySAC* -l
         
         设备号的申请与注册有两种方式 :
             1)静态方式
             2)动态方式
       3.1.1 静态方式
            查看已经有哪些主设备号被占用
                cat /proc/devices
            
            从没被占用的主设备号挑一个为我所用      
             
            int register_chrdev_region(dev_t from, unsigned count,
                                       const char *name)
                作用:注册连续的多个设备号
                from, 要注册的起始设备号
                count, 要注册的个数
                name, 名称
                返回值,0 成功
                        <0 失败
            void unregister_chrdev_region(dev_t from, unsigned count)            
                        
            实验步骤:
                insmod led_drv.ko
                cat /proc/devices
                rmmod led_drv.ko
                cat /proc/devices
        3.1.2 动态方式
             由内核动态分配一个未被使用主设备号
             
             int alloc_chrdev_region(dev_t *dev,
                      unsigned baseminor, unsigned count,
                const char *name)       
                dev,传出参数   
                    用于返回分配得到的第一个设备号
                baseminor,起始次设备号
                count,申请的连续设备号的个数
                name, 名称
                
             注销:unregister_chrdev_region       
      3.2 操作(驱动)函数集合
          实现一个字符设备硬件的驱动程序
          实则就是实例化一个struct cdev对象
          在实例化struct cdev对象过程中主要的编码工作
          集中在了struct file_operations   
          
          struct file_operations
          {
              owner
              open
              read
              write
              mmap
              release
              unlocked_ioctl
              poll
              ...
          }
          具体某个特定的字符设备硬件,只需要实现其中的部分函数即可
      3.3 内核中提供的关于cdev的API  
          void cdev_init(struct cdev *cdev,
                   const struct file_operations *fops)
          {
              cdev->ops = fops;
              ...
              
          }
          
          //注册cdev 将一条学生记录插入到数据库中
          cdev_add(struct cdev *p, dev_t dev, unsigned count)
          {
              p->dev = dev;
              ...
          }
          //注销cdev 将一条学生的记录从数据库中删除
          void cdev_del(struct cdev *p)    
              
         实验步骤:
             1)insmod led_drv.ko
                找到该驱动使用的主设备号 和 次设备号
             2)在开发板上创建设备文件
                mknod /dev/myleds c 244  5
             3)vi test.c
                
                fd = open("/dev/myleds", O_RDWR);  
             4) arm-cortex_a9-linux-gnueabi-gcc test.c -o test
             5) 板子上执行test
                cp test ../../rootfs
                
                ./test
         总结:
             linux下一切皆文件  除了socket

    //模块传参
    #include "../../global.h" int irq = 0; char *str = "tarena"; int fish[10] = {0}; /*保存fish元素个数*/ int num_fish = 10; module_param(irq, int, 0644); module_param(str, charp, 0); module_param_array(fish, int, &num_fish,0600); int __init moduleparam_init(void) { int i = 0; printk("<1>" "irq=%d ", irq); printk("<1>" "str=%s ", str); for(; i<num_fish; i++) { printk("<1>" "fish[%d]=%d ", i, fish[i]); } return 0; } void __exit moduleparam_exit(void) { int i = 0; printk("<1>" "irq=%d ", irq); printk("<1>" "str=%s ", str); for(; i<num_fish; i++) { printk("<1>" "fish[%d]=%d ", i, fish[i]); } } module_init(moduleparam_init); module_exit(moduleparam_exit);
    //设备号的注册。
    #include "../../global.h" #include <linux/fs.h> unsigned int major = 200; unsigned int minor = 5; dev_t dev ; //设备号 int __init led_drv_init(void) { //dev = major<<20|minor; dev = MKDEV(major, minor); register_chrdev_region(dev, 1, "myleds"); return 0; } void __exit led_drv_exit(void) { unregister_chrdev_region(dev, 1); } module_init(led_drv_init); module_exit(led_drv_exit);
    //设备号的动态注册
    1
    #include "../../global.h" 2 #include <linux/fs.h> 3 4 unsigned int major = 0; 5 unsigned int minor = 5; 6 dev_t dev ; //设备号 7 8 9 int __init led_drv_init(void) 10 { 11 if(major) //静态 12 { 13 //dev = major<<20|minor; 14 dev = MKDEV(major, minor); 15 16 register_chrdev_region(dev, 1, "myleds"); 17 } 18 else //动态注册 19 { 20 alloc_chrdev_region(&dev, minor, 1, "myleds"); 21 printk("<1>" "major=%d minor=%d ", 22 MAJOR(dev), MINOR(dev)); 23 } 24 return 0; 25 } 26 void __exit led_drv_exit(void) 27 { 28 unregister_chrdev_region(dev, 1); 29 } 30 module_init(led_drv_init); 31 module_exit(led_drv_exit);
    #include "../../global.h"
    #include <linux/fs.h>
    #include <linux/cdev.h>
    
    unsigned int major = 0;
    unsigned int minor = 5;
    dev_t dev ; //设备号
    
    /*1 定义一个struct cdev变量*/
    struct cdev led_cdev;
    
    
    static int led_open(struct inode *inode, 
                        struct file *filp)
    {
        printk("<1>" "enter %s
    ", __func__);
        return 0;
    }
    static int led_close(struct inode *inode, 
                        struct file *filp)
    {
        printk("<1>" "enter %s
    ", __func__);
        return 0;
    }
    
    struct file_operations led_fops =
    {
        .owner = THIS_MODULE,
        .open  = led_open,
        .release = led_close,
    };
    int __init led_drv_init(void)
    {
        if(major) //静态
        {
            //dev = major<<20|minor;
            dev = MKDEV(major, minor);
    
            register_chrdev_region(dev, 1, "myleds");
        }
        else //动态注册
        {
            alloc_chrdev_region(&dev, minor, 1, "myleds");
            printk("<1>" "major=%d minor=%d
    ",
                    MAJOR(dev), MINOR(dev));
        }
        /*2 初始化cdev变量*/
        cdev_init(&led_cdev, &led_fops);
        /*3 注册cdev变量*/
        cdev_add(&led_cdev, dev, 1);
    
        return 0;
    }
    void __exit led_drv_exit(void)
    {
        /*4 注销cdev*/
        cdev_del(&led_cdev);
    
        unregister_chrdev_region(dev, 1);
    }
    module_init(led_drv_init);
    module_exit(led_drv_exit);
  • 相关阅读:
    八、总结
    第5章、Kafka监控
    十一、总结
    十、图形化的客户端和监控工具
    九、zookeeper四字监控命令
    八、zookeeper 开源客户端curator介绍
    七、Zookeeper原理
    六、zookeeper 事件监听机制
    五、zookeeper的javaApi
    四、zookeeper的Acl权限控制
  • 原文地址:https://www.cnblogs.com/DXGG-Bond/p/11844642.html
Copyright © 2011-2022 走看看