驱动开发-模块

来源:互联网 发布:怎么成为算法工程师 编辑:程序博客网 时间:2024/05/01 20:08

驱动开发-


atexit – 程序终止时释放空间

void function(void){    printf("1\n");}void function1(void){    printf("2\n");}int main(int argc,char \*argv[]){    printf("duang\n");    atexit(function);    printf("fang\n");    atexit(function1);    return 0;}

输出结果
duang
fang
2
1

exit比return 0多一个刷新缓冲区功能

exit–>释放空间

_exit–>不释放空间


应用程序和模块区别

  • 入口函数
应用程序 模块函数 main module_init

- 运行空间

应用程序 模块 用户空间 内核空间

- 调用函数

应用程序 模块函数 c库或者系统调用 内核提供的函数

- 资源释放

应用程序 模块函数 自动释放或者手动释放 手动释放

什么程序可以编译生成模块

  • 在linux下,有三大设备驱动
    1. 字符设备 –> led, beep,key,adc,mpu6050,usb,lcd
      • 一般按字节的形式访问,顺序访问 char
    2. 块设备 –> emmc,nand flash,nor flash,sd
      • 固定大小随机访问,linux系统下block 512byte
    3. 网络设备驱动 –> 网卡
      • 有自己的协议

如何编写模块

如果想在内核中编译模块,必须在make menuconfig 中配置驱动程序才可以编译生成模块:make modules .o –> .ko

1. 模块分成内部模块和外部模块2. 编译模块的条件    - 必须是已经 编译过的内核 --> make    - 必须是已经 配置过的内核 --> make menuconfig3. 在内核的源码目录外编译4. 采用模块化编程
  1. 模块的入口函数 –> 申请资源

    module_init() --> 模块的入口函数// 函数的返回值typedef init (\*initcall_t)(void);
  2. 模块的出口函数 –> 释放资源

    module_exit() --> 模块的出口函数// 函数的返回值typedef void (\*exitcall_t)(void);

C程序编程

    //编写入口函数和出口函数    int hello_init(void)    {        //资源的申请    }    void hello_exit(void)    {        //资源的释放    }    module_init();    module_exit();    ```-------------------------### 查看内核和安装模块- 查看内核  <br/>    1. 添加文件索引    `make tags`  <br/>    2. 搜索函数名    `vi -t module_init`  <br/>- 模块的命令    1. 安装模块  <br/>        `insmod 文件名`    2. 卸载模块  <br/>        `rmmod 模块名`    2. 查看模块  <br/>        `lsmod `    3. 查看内核后台的打印信息 <br/>        `dmesg`    4. 清空内核后台的打印信息  <br/>        `dmesg -c`------------------------------------### 字符设备驱动> 应用程序是通过设备号找到驱动程序,在linux系统设备号是唯一的。- 内核定义设备号通过设备号可以找到对应的驱动程序,驱动程序可以绑定设备号- 设备号 是32bit的无符号整数    - 其中高12bit表示的是主设备号,低20位表示的是次设备号    - 主设备号:表示哪一类设备    - 次设备号:表示这一类设备的第几个设备- 申请设备号 -- 静态申请和动态申请    - 静态申请        int marjor = 250;        int minor = 0;        int decno = major << 20 | minor        内核申请设备号的函数        dev_t devno = MAKDEV(marjor,minor);        MAKDEV    - 注册字符设备号    ```c        register_chrdev_region();        int register_chrdev_region(dev_t from, unsigned count,const char \*name);        参数        dev_t from 设备号        unsigned count 申请设备号的个数        const char * name 设备或者驱动的名字        返回值        成功返回0,失败返回错误码        int ret;        ret  register_chrdev_region("devno",1,"hello");        if(0 != ret)        {            return -EBUSY;        }    ```    - 释放设备号    ```c        unregister_chrdev_region();        void unregister_chrdev_region(dev_t from,unsigned count);        参数        dev_t from 设备号        unsigned count 设备号的个数        unregister_chrdev_region(devno,1);    ```    - 查看系统中注册的设备号  <br/>    `cat /proc/devices`----------------------------------------### 初始化cdev结构体- 查看系统中已经注册的设备号  <br/>`cat /proc/devices`--------------------------------------1. 初始化cdev结构体    - **cdev_init()函数**```c555  * @fops: the file_operations for this device556  *557  * Initializes @cdev, remembering @fops, making it ready to add to the558  * system with cdev_add();559  *560 void cdev_init(struct cdev \*cdev, const struct file_operations \*fops);参数:      struct  cdev *cdev:cdev结构体      struct  file_operations  *        ops:操作函数的接口<div class="se-preview-section-delimiter"></div>
- **struct cdev \*cdev**
    struct cdev{    struct kobject kobj;   // 内嵌的kobject对象    struct module \*owner;  // 所属模块    const struct file_operations \*ops; // 文件操作结构体    struct list_head list;        //linux内核所维护的链表指针    dev_t dev;               //设备号    unsigned int count;};<div class="se-preview-section-delimiter"></div>

- **struct file_operations**

struct file_operations {
struct module *owner;//拥有该结构的模块的指针,一般为THIS_MODULES
loff_t (*llseek) (struct file *, loff_t, int);//用来修改文件当前的读写位置
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);//从设备中同步读取数据
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);//向设备发送数据

ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);//初始化一个异步的读取操作
ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);//初始化一个异步的写入操作
int (*readdir) (struct file *, void *, filldir_t);//仅用于读取目录,对于设备文件,该字段为NULL
unsigned int (*poll) (struct file *, struct poll_table_struct *); //轮询函数,判断目前是否可以进行非阻塞的读写或写入
int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long); //执行设备I/O控制命令
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long); //不使用BLK文件系统,将使用此种函数指针代替ioctl
long (*compat_ioctl) (struct file *, unsigned int, unsigned long); //在64位系统上,32位的ioctl调用将使用此函数指针代替
int (*mmap) (struct file *, struct vm_area_struct *); //用于请求将设备内存映射到进程地址空间
int (*open) (struct inode *, struct file *); //打开
int (*flush) (struct file *, fl_owner_t id);
int (*release) (struct inode *, struct file *); //关闭
int (*fsync) (struct file *, struct dentry *, int datasync); //刷新待处理的数据
int (*aio_fsync) (struct kiocb *, int datasync); //异步刷新待处理的数据
int (*fasync) (int, struct file *, int); //通知设备FASYNC标志发生变化
int (*lock) (struct file *, int, struct file_lock *);
ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
int (*check_flags)(int);
int (*flock) (struct file *, int, struct file_lock *);
ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
int (*setlease)(struct file *, long, struct file_lock **);
};

Linux使用file_operations结构访问驱动程序的函数,这个结构的每一个成员的名字都对应着一个调用。用户进程利用在对设备文件进行诸如read/write操作的时候,系统调用通过设备文件的主设备号找到相应的设备驱动程序,然后读取这个数据结构相应的函数指针,接着把控制权交给该函数,这是Linux的设备驱动程序工作的基本原理。


- **cdev_add()函数**

c
464 **
465 * cdev_add(); - add a char device to the system
466 * @p: the cdev structure for the device
467 * @dev: the first device number for which this device is responsible
468 * @count: the number of consecutive minor numbers corresponding to this
469 * device
470 *
471 * cdev_add(); adds the device represented by @p to the system, making it
472 * live immediately. A negative error code is returned on failure.
473 *
474 int cdev_add(struct cdev \*p, dev_t dev, unsigned count)
参数: struct cdev * p:字符设备
dev_t devno;设备号
unsigned count :次设备号的个数


- **删除cdev结构体**
496 **497  * cdev_del(); - remove a cdev from the system498  * @p: the cdev structure to be removed499  *                                                                                                                               500  * cdev_del(); removes @p from the system, possibly freeing the structure501  * itself.502  *503 void cdev_del(struct cdev \*p)504 {505     cdev_unmap(p->dev, p->count);506     kobject_put(&p->kobj);507 }
0 0
原创粉丝点击