字符设备实现内部驱动原理:
应用层:open函数回调到驱动中open操作方法的路线:
open()--->sys_open()--->struct inode结构体--->struct cdev结构体--->struct file_operations结构体--->mycdev_open()
字符设备驱动编写流程:
1、分配对象空间
2、对象空间的初始化
3、对象的注册
4、对象的注销
流程模板(非详细)
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
//定义cdev的结构体指针变量
struct cdev *cdev;
static int __init mycdev_init(void)
{
//1.分配对象
cdev = cdev_alloc();
//2.对象的初始化
cdev_init(cdev,&fops);
//3.申请设备号,动态申请
ret = alloc_chrdev_region(&devno,minor,count,CNAME);
//4.字符设备驱动的注册
ret = cdev_add(cdev,MKDEV(major,minor),count);
//5.自动创建设备节点
cls = class_create(THIS_MODULE,CNAME);
dev = device_create(cls,NULL,MKDEV(major,i),NULL,"mycdev%d",i);
return 0;
}
static void __exit mycdev_exit(void)
{
//1.销毁设备节点
device_destroy(cls,MKDEV(major,i));
class_destroy(cls);
//2.销毁字符设备驱动
cdev_del(cdev);
//3.销毁设备号
unregister_chrdev_region(MKDEV(major,minor),count);
//4.释放动态申请的cdev内存
kfree(cdev);
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");
inode结构体作用:
只要文件存在文件系统中,在内核中就会有一个struct inode类型的空间,用于保存该文件的信息
struct file结构体作用:
open函数参数是路径下的文件名字,根据文件名找到inode号,然后根据inode结构体,回调对应的操作方法
通过文件描述符回调对应操作方法的路径:
fd--->fd_array[fd]--->struct file--->f_op--->操作方法
linux内核中的并发和竟态解决方法:
当多个应用程序访问同一个驱动的临界资源的时候,竞态就会产生。
根本原因:
1.对于单核处理器,如果内核支持抢占就会产生竞态
2.多核处理器,核心与核心之间本身就会产生竞态。
3.中断和进程间也会产生竞态
解决方法:
1、中断屏蔽:
local_irq_disable(); //屏蔽中断 //临界资源 local_irq_enable(); //开启中断
2、自旋锁:
自旋锁:针对多核设计的,当一个进程获取到自旋锁之后,另外一个进程也想获取这把锁,此时后一个进程处于自旋状态(原地打转)(自旋锁又叫盲等锁);
特点:
1.自旋状态是需要消耗cpu资源的
2.自旋锁适合用在临界区比较小地方,临界区不能有延时,耗时,甚至休眠的操作,
临界区也不能够有copy_to_user/copy_from_user的函数。
3.自旋锁会导致死锁(在同一个进程中多次获取同一把未解锁的锁)
4.自旋锁可以在进程上下文中使用,也可以在中断上下文中使用
5.自旋锁在上锁前会关闭抢占
spinlock_t lock; //定义自旋锁
spin_lock_init(&lock); //初始化自旋锁
spin_lock(&lock); //上锁
spin_unlock(&lock); //解锁
信号量:
信号量:当一个进程获取到信号量之后,另外一个进程也想获取信号量,此时后一个进程处于休眠状态。
特点:
1.休眠的进程是不消耗cpu资源的
2.信号量不会导致死锁
3.信号量保护的临界区可以很大,里面可以有延时,耗时,甚至休眠的操作。
4.信号量只能工作在进程上下文。不能工作于中断上下文。
5.信号量也不会关闭抢占
struct semaphore sem; //定义信号量
void sema_init(struct semaphore *sem, int val) //初始化信号量
//注意,这里的val只有初始化为1的时候才有能够解决竞态。当val初始化为0的时候代表同步机制
void down(struct semaphore *sem); //上锁
void up(struct semaphore *sem);//解锁
互斥体:
互斥体:当一个进程获取到互斥体之后,另外一个进程也想获取互斥体,此时后一个进程处于休眠状态。
特点:
1.休眠的进程是不消耗cpu资源的
2.互斥体不会导致死锁
3.互斥体保护的临界区可以很大,里面可以有延时,耗时,甚至休眠的操作。
4.互斥体只能工作在进程上下文。不能工作于中断上下文。
5.互斥体也不会关闭抢占
6.互斥体在进入休眠前会稍微等一会儿在进入休眠状态,但是对于临界区比较小的时候使用互斥体的效率是高于信号量的,优先使用互斥体
struct mutex mutex; //定义互斥体
mutex_init(&mutex); //初始化互斥体
void mutex_lock(struct mutex *lock) //上锁
static int mutex_unlock(unsigned long *lock) //解锁
原子操作:
原子操作:将整个过程看做是一个不可被分割的整体。原子操作结构体内部本身就是一个变量,通过
对这个变量的值的修改来实现防止竞态的效果,对这个变量的值的修改是通过内联汇编完成的。
atomic_t atm = ATOMIC_INIT(1); //定义并初始化原子操作
int atomic_dec_and_test(atomic_t *v) //上锁
功能:让原子变量的值减去1,如果结果为0 表示获取锁成功了
参数:
@v:原子变量的地址
返回值:如果获取成功返回真,失败返回假
void atomic_inc(atomic_t *v) //将原子变量的值加1,解锁
atomic_t atm = ATOMIC_INIT(-1); //定义并初始化原子操作
int atomic_inc_and_test(atomic_t *v) //上锁
功能:让原子变量的值加1,如果结果为0 表示获取锁成功了
参数:
@v:原子变量的地址
返回值:如果获取成功返回真,失败返回假
void atomic_dec(atomic_t *v) //将原子变量的值减去1,解锁