"Linux设备驱动开发详解" 笔记

《Linux设备驱动开发详解》第2版 宋宝华 编著

Bought on Dec 1, 2010, Noted on 2015.6

【声明】本文大部分内容摘自《Linux设备驱动开发详解》第2版,或者网上搜索,故不单独注明内容出处

第一篇 Linux设备驱动入门

 

设备的分类

字符设备;块设备;网络设备

其中网络设备不会映射到文件系统中的文件和目录,而是面向数据包的接受和发送

 

处理器分类

CPU的体系结构:冯诺依曼机构和哈弗结构(程序和数据分开存储,包括独立的总线)

从指令集角度分:RISC(ARM,MIPS,PowerPC)和CISC(IA x86)

按应用领域区分:通用处理器(GPP:general-purpose preprocessor),DSP,ASP/ASIC(Application specific integriated circuit)

 

Linux 2.6内核特点

1. 新的调度器,在高负载情况下执行极其出色
2. 内核任务可抢占,提高系统的实时性,使得鼠标和键盘事件得到更快的响应
3. 改进的线程模型,线程操作速度得以提高,可以处理任意数量的线程
4. 虚拟内存,增加r-map(方向映射),显著改善虚拟内存在一定程度负载下的性能
5. 音频。弃用OSS,改用ALSA
 

Linux Kernel's components

SCHED(进程调度)MM(内存管理)VFS(虚拟文件系统)NET(网络接口)IPC(进程间通信)
其中网络接口分为网络协议和网络驱动程序
 

Coding style

TAB 8 characters
if/for one line, without { }
switch and case aligning
for (i = 0; i < 10; i++) {
.......................
}
 

GNU C & ANSI C

GNU 是ANSI C的扩展语法
1. 零长度和变量长度数组
char data[0]; ....... then data[i]=......
int main(int argc, char *argv[])
{
int i, n = argc;
double x[n];
}
2. case的范围
like
switch (ch) {
case '0' ... '9': xxx
case 'a' ... 'f': xxx
}
3. typeof 获取变量的type
e.g. const typeof(x) _x = (x);
4. 可变参数宏
e.g. #define pr_debug(fmt, arg...) \
printk(fmt, ##arg)
其中##是为了处理参数为零的情况,去除掉fmt后面的逗号
5. 标号元素
通过指定索引或者结构体成员名,允许初始化值以任意顺序出现
struct file_operations ext2_file_operations = {
llseek: genernic_file_llseek,
ioctl: ext2_ioctl,
.....
}
6. built-in function in GCC
For example, __builtin_return_address(LEVEL)
Checking out GNU GCC manual

第二篇 Linux设备驱动核心理论

Linux File System

System calls of FS

int create(const char *filename, mode_t mode);
int umask(int newmask);
int open(const char *filename, int flags, mode_t mode);
int read(int fd, const void *buf, size_t length);
int write(int fd, const void *buf, size_t length);
int lseek(int fd, offset_t offset, int whence);
int close(int fd);
 

File operation API of C library

dependence on different OS

FILE *fopen(const char *path, const char *mode);
int fgetc(FILE *stream);
int fputc(int c, FILE *stream);
char *fgets(char *s, int n, FILE *stream);
int fprintf(FILE *stream, const char *format, ... );
int fscanf(FILE *steam, const char *format, ... );
size_t fread(void *ptr, size_t size, size_t n, FILE *stream);
size_t fwrite(const void *ptr, size_t size, size_t n, FILE *stream);
int fseek(FILE *stream, long offset, int whence);
int fclose(FILE *stream);
 

struct file & inode

file结构体代表一个打开的文件或者设备。
file->f_mode标识文件的读写权限,file->f_flags标识可反映阻塞和非阻塞

inode结构体是linux管理文件系统的最基本单位,记录文件各类属性信息,其中i_rdev字段包含设备编号,包含major and minor number。一般major对应驱动,minor对应该驱动的设备序号
 

sysfs file system

这个VFS提供了包含所有系统硬件的层级视图,展示设备驱动模型各组件的层次关系,大致分三类:bus,devices,class。
三个重要结构体分别描述bus,driver,device: bus_type,device_driver,device.
device and driver's registration is separately. when any one registration, it will try to match another one via match() routine of bus_type();

Linux Character device

Main function of cdev

cdev struct describes character device information.

MAJOR(dev_t dev)
MINOR(dev_t dev)
MKDEV(int major, int minor)

void cdev_init(struct cdev *, struct file_operations *);
struct cdev *cdev_alloc(void);

int register_chrdev_region(dev_t from, unsigned count, const char *name);
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name);

void cdev_put(strcut cdev *p);
int cdev_add(struct cdev *, dev_t, unsigned);
void cdev_del(struct cdev*);
 

procedure of character device initialization

1. application device number
2. registration cdev
 

data transmit

copy_from_user()
copy_to_user()
put_user()
get_user()
 

ioctl command format

Device type + no + direction + size
(Device type is magic number, see more in ioctl-number.txt)

_IO()
_IOR()
_IOW()
_IOWR()

 

private_data


struct file {
....
void *private_data;
}
private_data存储驱动的私有数据,一般在驱动probe时动态开辟内存,以便多个设备共用一个驱动使用;不过随着device——tree的应用,越来越多的驱动数据放在device_tree中。

 

Concurrency & race condition

并发和竞态


critical sections临界资源包含:HW,static/global variables
竞态发生的情况:SMP,多线程,包括可抢占式内核,中断
避免竞态的手段:中断屏蔽,原子操作,自旋锁,信号量
 

中断屏蔽

可以避免新的中断到来,也可以避免内核抢占的发生(进程调度依赖于中断来实现)

local_irq_disable()
local_irq_enable()
local_irq_save(flags)
local_irq_restore(flags)
local_bh_disable() //disable 中断底半部
local_bh_enable()
 

原子操作

For integer operand

atomic_set
atomic_read
atomic_add
atomic_sub
atomic_inc_and_test
atomic_dec_and_test
atomic_sub_and_test
atomic_inc_return
atomic_sub_return
atomic_dec_return
atomic_add_return
 

For bit operand

set_bit
clear_bit
change_bit
test_bit
test_and_set_bit
test_and_clear_bit
test_and_change_bit
 

自旋锁

Spin lock

spinlock_t lock;
spin_lock_init(lock);
spin_lock(lock)
spin_trylock(lock) return immediately even lock failure
spin_unlock(lock)

NOTE: 在自旋锁持有期间,内核抢占被禁止,主要针对SMP和单CPU可抢占内核,但是依然受到中断和底半部的影响。所以自旋锁往往结合中断使能函数一同使用,如下:
spin_lock_irq
spin_unlock_irq
spin_lock_irqsave
spin_unlock_irqrestore
spin_lock_bh
spin_unlock_bh

NOTE: spin lock实际上是忙等,CPU不做任何事情,非常消耗CPU,所以只能用于很短时间的等待,往往用于等待硬件的场景
spin lock可能导致系统死锁,比如递归使用同一个自旋锁,即拿到锁后,没有释放而再次拿锁
spin lock锁定期间,不能调用可能引起进程调度的函数。如果进程获得自旋锁后再阻塞,如调用了copy_from_user(), copy_to_user(),则可能导致内核的崩溃
 

读写自旋锁

rwlock

允许读的并发,禁止写的同时进行

rwlock_init
read_lock
read_lock_ireqsave
read_lock_irq

read_unlock
read_unlock_irqrestore
read_unlock_irq

write_lock
write_lock_irqsave
write_lock_irq
write_trylock

write_unlock
write_unlock_irqrestore
write_unlock_irq


For example,

rwlock_t lock;
rwlock_init(&lock);

read_lock(&lock);
.....
read_unlock(&lock);

write_lock_irqsave(&lock, flags);
....
write_unlock_irqrestore(&lock, flags);
 

循序锁

seqlock

对读写锁的一种优化,读执行单元不会被写执行单元阻塞,写执行单元也不需要等待读执行单元完成读操作后才进行写操作,即读写可以同时操作,只不过如果读的过程中发生了写操作,需要重新读取数据;写执行单元之间是互斥的。

因为顺序锁允许读写同时进行,大大提高了并发性,对于读写同时进行的概率比较小的情况下,性能非常好。

NOTE:顺序锁有个限制,要求被保护的共享资源不含有指针,因为写执行单元可能使得指针失效,但读执行单元如果正要访问该指针,将导致oops

For example,
//write operation
write_seqlock(&seqlock_a);
...
write_sequnlock(&seqlock_a);

//read operation
read_seqbegin(...); //返回顺序锁的当前顺序号
read_seqretry(...); //检查资源是否被复写,如是,则重读

e.g.
do {
   seqnum = read_seqbegin(&seqlock_a);
   /* execute read operations */
   ....
   
} while(read_seqretry(&seqlock_a, seqnum));
 

读-拷贝-更新

RCU(Read-Copy Update)

原理:写操作需要先拷贝一个副本,先对副本进行修改,然后再适当的时机拷贝(update)回原有数据。这个时机就是所有引用该数据的CPU都退出对共享数据的操作的时候。读执行单元没有任何同步开销,而写操作单元的同步开销则取决于使用的写执行单元间同步机制。

RCU可以看做读写锁的高性能版本,既允许多个读单元并发,又允许多个读执行单元和多个写执行单元同时并发。但是对于写比较多的并发情况,写执行单元之间的同步开销也随之加大,必定需要用某种锁机制来同步并行的多个写操作<

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值