字符设备实现内部驱动原理及分步注册流程

 字符设备实现内部驱动原理:

字符设备实现内部驱动原理及分步注册流程_第1张图片

 应用层:open函数回调到驱动中open操作方法的路线:

open()--->sys_open()--->struct inode结构体--->struct cdev结构体--->struct file_operations结构体--->mycdev_open()

字符设备驱动编写流程:

1、分配对象空间

2、对象空间的初始化

3、对象的注册

4、对象的注销

流程模板(非详细)

#include 
#include 
#include 
#include 

//定义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,解锁

你可能感兴趣的:(linux,驱动开发)