[TOC]
驱动编写
注册驱动模块
1 2 3
| module_init(fun_init); module_exit(fun_exit);
|
1 2 3 4 5 6 7
|
MODULE_LICENSE("GPL");
MODULE_AUTHOR("cxcc");
MODULE_DESCRIPTION("read and write test module");
|
输出操作
输出等级
1 2 3 4 5 6 7 8
| #define KERN_EMERG KERN_SOH "0" #define KERN_ALERT KERN_SOH "1" #define KERN_CRIT KERN_SOH "2" #define KERN_ERR KERN_SOH "3" #define KERN_WARNING KERN_SOH "4" #define KERN_NOTICE KERN_SOH "5" #define KERN_INFO KERN_SOH "6" #define KERN_DEBUG KERN_SOH "7"
|
如果输出等级高于控制台等级,就会在控制台输出该消息,否则只能在dmesg中查看
cdev操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
int cdev_add(struct cdev *p, dev_t dev,unsigned count);
void cdev_del(struct cdev *p);
void cdev_init(struct cdev *cdev, const struct file_operations *fops);
struct cdev *cdev_alloc(void);
|
创建设备
手动
1 2 3 4 5
|
sudo mknod [设备路径] [设备类型(c/b)] [主设备号] [次设备号]
sudo mknod /dev/rw_dev0 c 504 0
|
自动
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
|
struct class * class_create(struct module *owner,const char* name);
void class_destroy(struct class *cls);
struct device *device_create( struct class *class, struct device *parent, dev_t devt, void *drvdata, const char *fmt, ...);
void device_destroy(struct class *class, dev_t devt)
|
设备号操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
|
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 unregister_chrdev_region(dev_t from, unsigned count);
|
数据拷贝
1 2 3 4 5 6 7
|
static inline long copy_from_user(void *to, const void __user * from, unsigned long n);
static inline long copy_to_user(void __user *to, const void *from, unsigned long n);
|
符号表导出
1 2 3
| EXPORT_SYMBOL(name) EXPORT_SYMBOL_GPL(name)
|
参数传递
1 2
| module_param(name, type, perm)
|
地址映射
1 2
| void __iomem* imremap(phys_addr_t offset,unsigned long size); void iounmap(void __iomem* addr)
|
读写
1 2 3 4 5
| readb(c) writeb(v,c) readl(c) writel(v,c)
|
GPIO子系统
1 2 3 4 5 6 7 8 9 10 11
| ```
## IRQ子系统
```C
request_irq(unsigned int irq,irq_handler_t handler,unsigned long flags,const char*name,void *dev) free_irq
|
中断底半部
软件中断
处于中断状态,独占CPU,不参与调度,能被IRQ打断。没有内核接口不可用(除非修改内核,导出接口)
1 2 3 4
| open_softirq
raise_softirq
|
tasklet
基于软中断实现,特性和软中断相同。
1 2 3 4
| tasklet_init
tasklet_schedule
|
工作队列
进程模式,参与进程调度
<linux/workqueue.h>
方法一:
1 2 3 4
| INIT_WORK(_work,_func)
schedule_work()
|
方法二:
1 2 3 4 5 6
| create_workqueue()
destory_workqueue()
queue_work
|
内核定时器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| struct timer_list { unsigned long expires; void(*function)(unsigned long ); }
DEFINE_TIMER(_name,_function,expires,_data)
init_timer()
add_timer
del_timer
mod_timer
|
Kfifo
<linux/kfifo.h>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| struct kfifo{ unsigned char*buffer; unsigned int size; unsigned int in; unsigned int out; }
kfifo_alloc(fifo,size,gfp_mask) kfifo_free() kfifo_in kfifo_out
kfifo_len
kfifo_size
kfifo_is_empty
kfifo_is_full
|
内存申请
1 2 3 4 5 6 7 8 9 10
|
kmalloc(size,gfp_mask) kfree()
vmalloc(size) vfree()
|
并发竟态
同步:程序执行的顺序性
互斥:程序执行的排他性
互斥锁
1 2 3 4 5 6 7 8 9
|
mutex_init(mutex)
mutex_lock()
mutex_trylock
mutex_unlock()
|
自旋锁
上锁成功:停止进程调度器
上锁失败:进程自旋,不休眠,100%占用CPU(其他进程在临界区内主动释放CPU)
1 2 3 4 5 6 7
|
spin_lock_init()
spin_lock spin_trylock spin_unlock
|
原子操作
不会被打断的操作(ATOMIC)一般是由汇编编写,方法一般在体系架构相关的代码中
(arch/<架构>/include)
信号量
semaphore
1 2 3 4 5 6 7 8 9
| sema_init
down
up down_trylock
down_interruptible
|
IO模型
阻塞
注意,在等待队列的一般是由于缓冲区无数据导致的读取等待。因此应该在有进程向缓冲区写数据的时候唤醒,或者从硬件读取到数据时唤醒
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
|
wait_event(wq,condition)
wait_event_interrupttible(wq,condition)
init_waitqueue_head()
init_waitqueue_entry(q,task)
add_wait_queue()
set_current_state(state_value)
schedule
|
- 初始化等待队列头
- 初始化等待队列项
- 将等待队列项添加到等待队列上
- 切换进程的状态
- 调度执行其他进行
1 2 3 4 5 6
| wake_up wake_up_interruptible
remove_wait_queue
|
- 切换进程状态(TASK_RUNNING)
- 从等待队列移除 remove_wait_queue
非阻塞
return -EAGAIN;
判断应用层的打开方式是阻塞还是非阻塞
file结构体:f_flags O_NONBLOCK 00004000 非阻塞
应用层打开时,第二个参数中或上O_NONBLOCK
或者使用fcntl F_GETL F_SETL
IO多路复用(select/poll/epoll)
应用层
(153条消息) c语言select函数作用,linux c语言 select函数使用方法_謝晓东的博客-CSDN博客
驱动层实现file_optional->poll
异步通知
驱动层通知应用层(采用信号机制)
实现支持异步通知功能,应用层需要开启异步通知,内核中的驱动需要支持异步通知
- 写FASYNC标志到fd中,通过fcntl设置;设置F_SETOWN,绑定应用层进程
- fcntl(fd,F_SETOWN,getpid())//绑定进程
- fcntl(fd,SETFL,fcntl(fd,GETFL)|FASYNC)//开启异步通知
- 驱动中支持,file_optional->fasync(int fd,struct file* filep,int on)
- 一般会调用,fasync_helper()
- 条件满足,发送信号给应用层进程(SIGIO)
文件操作
open
1 2 3
|
int open(struct inode * in, struct file * fp)
|
ioctl
1 2 3 4 5
|
int ioctl(int fd,unsigned long request,...)
|
1 2 3 4 5
|
long (*unlocked_ioctl) (struct file *, unsigned int cmd, unsigned long args); long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
|
cmd的构造方法
cmd中提取参数
1 2 3 4
| _IOC_DIR _IOC_TYPE _IOC_NR _IOC_SIZE
|