驱动开发-
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
1exit比return 0多一个刷新缓冲区功能
exit–>释放空间
_exit–>不释放空间
应用程序和模块区别
- 入口函数
应用程序 | 模块函数 |
---|---|
main | module_init |
- 运行空间
应用程序 | 模块 |
---|---|
用户空间 | 内核空间 |
- 调用函数
应用程序 | 模块函数 |
---|---|
c库或者系统调用 | 内核提供的函数 |
- 资源释放
应用程序 | 模块函数 |
---|---|
自动释放或者手动释放 | 手动释放 |
什么程序可以编译生成模块
- 在linux下,有三大设备驱动
- 字符设备 –> led, beep,key,adc,mpu6050,usb,lcd
- 一般按字节的形式访问,顺序访问 char
- 一般按字节的形式访问,顺序访问 char
- 块设备 –> emmc,nand flash,nor flash,sd
- 固定大小随机访问,linux系统下block 512byte
- 固定大小随机访问,linux系统下block 512byte
- 网络设备驱动 –> 网卡
- 有自己的协议
- 有自己的协议
- 字符设备 –> led, beep,key,adc,mpu6050,usb,lcd
如何编写模块
如果想在内核中编译模块,必须在make menuconfig 中配置驱动程序才可以编译生成模块:make modules .o –> .ko
1. 模块分成内部模块和外部模块
2. 编译模块的条件
- 必须是已经 编译过的内核 --> make
- 必须是已经 配置过的内核 --> make menuconfig
3. 在内核的源码目录外编译
4. 采用模块化编程
模块的入口函数 –> 申请资源
module_init() --> 模块的入口函数 // 函数的返回值 typedef init (\*initcall_t)(void);
模块的出口函数 –> 释放资源
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()函数**
```c
555 * @fops: the file_operations for this device
556 *
557 * Initializes @cdev, remembering @fops, making it ready to add to the
558 * 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 system
498 * @p: the cdev structure to be removed
499 *
500 * cdev_del(); removes @p from the system, possibly freeing the structure
501 * itself.
502 *
503 void cdev_del(struct cdev \*p)
504 {
505 cdev_unmap(p->dev, p->count);
506 kobject_put(&p->kobj);
507 }