1.内核定时器:
未来的某个时间点执行提前设置的某个任务函数。
涉及到的核心头文件:
#include <linux/timer.h>
涉及到的结构体:
struct timer_list {
/*
* All fields that change during normal runtime grouped to the
* same cacheline
*/
struct list_head entry;
unsigned long expires; //未来的定时时间点,参考时间是jiffies
struct tvec_base *base;
void (*function)(unsigned long); //定时到达时要执行的处理函数
unsigned long data; //定时处理函数需要的参数
int slack;
#ifdef CONFIG_TIMER_STATS
int start_pid;
void *start_site;
char start_comm[16];
#endif
#ifdef CONFIG_LOCKDEP
struct lockdep_map lockdep_map;
#endif
};
使用步骤:
1. 实例化定时器对象
方法1,
struct timer_list timer;
init_timer(&timer);
timer.expires = jiffies + xxx;
timer.function = service_timer;
timer.data = (unsigned long)dev;
方法2,
struct timer_list timer;
setup_timer(&timer, service_timer, (unsigned long)dev);
timer.expires = jiffies + xxx;
方法3,
DEFINE_TIMER(timer, service_timer, jiffies + xxx, (unsigned long)dev);
2. 向内核注册定时器并启动
add_timer(&timer);
3. 如果需要(周期性的来定时)
mod_timer(&timer, jiffies + xx);
4. 在驱动模块的出口移除定时器
//在对称多处理器对应的系统中使用
del_timer_sync(&timer);
//单核使用
del_timer(&timer);
2.杂项设备驱动(misc)的方法注册字符设备驱动
本质上misc驱动也是一个字符设备驱动,可能相对特殊一点而已。在drivers/char/misc.c的misc驱动初始化函数
misc_init()中实际上使用了MISC_MAJOR(主设备号为10)并调用register_chrdev()去注册了一个字符设备驱动。
同时也创建了一个misc_class,使得最后可自动在/dev下自动生成一个主设备号为10的字符设备。总的来讲,如果
使用misc驱动可以满足要求的话,那么这可以为开发人员剩下不少麻烦。
使用杂项设备的优点:
第一,节省主设备号:
使用普通字符设备,不管该驱动的主设备号是静态还是动态分配,都会消耗一个主设备号.而且如果你的这个驱动最
终会提交到内核主线版本上的话,需要申请一个专门主设备号。如用misc驱动的话就好多了。因为内核中已经为misc
驱动分配了一个主设备号。当系统中拥有多个misc设备驱动时,那么它们的主设备号相同,而用子设备号来区分
第二,使用简单:
有时候需要一个功能较简单的字符设备驱动,导出接口让用户空间程序方便地控制硬件,只需要使用misc子系统提供
的接口即可快速地创建一个misc设备驱动。当使用普通的字符设备驱动时,如果开发人员需要导出操作接口给用户空
间的话,需要自己去注册字符驱动,并创建字符设备class以自动在/dev下生成设备节点,相对麻烦一点。而misc驱动
则无需考虑这些,基本上只需要把一些基本信息通过struct miscdevice交给misc_register()去处理即可。
涉及头文件:
#include <linux/miscdevice.h>
一个杂项设备对应的结构类型:
struct miscdevice {
int minor; //次设备号
const char *name; //设备名称
const struct file_operations *fops; //驱动函数集
struct list_head list;
struct device *parent;
struct device *this_device;
const char *nodename;
umode_t mode;
};
注册步骤:
1. 实现驱动函数集
struct file_operations fops = {
xxx;
...
};
2. 实例化杂项设备对象
struct miscdevice misc = {
.minor = MISC_DYNAMIC_MINOR,
.name = "hehe",
.fops = &fops;
};
3. 注册
misc_register(&misc);
current 是全局的一个宏,用其可以获得当前进程的信息.在内核,一个进程用一个struct task_struct
的结构体对象描述。
内核会为每个进程分配8K的栈,在8K栈的低地址出放有一个struct
thread_info的一个对象, 而这个对象内部又有一个struct task_struct 的
一个指针变量task,task指向当前的进程结构体对象。
=======================================================================================================
2015年11月16日
内容:
互斥量(线程锁):
pthread_mutex_t mutex;
pthread_mutex_init(&mutex, NULL);
pthread_mutex_lock(&mutex);
pthread_mutex_trylock(&mutex);
pthread_mutex_unlock(&mutex);
自旋锁:
pthread_spin_t spin;
pthread_spin_init(&spin, 0);
pthread_spin_lock(&spin);
pthread_spin_trylock(&spin);
pthread_spin_unlock(&spin);
加锁一定要避免死锁:
常见的死锁有:
1. ABBA
2. 进程自己递归加锁
加锁的力度:
不能太大,太大有可能起不到保护作用;会降低程序的执行效率。
加锁是一种工程师的自愿工作,如果要加锁,一开始就要考虑。
kernel:
解决竞争实现同步的机制:
1. 原子操作:
int i = 2; /*global*/
A process B process
//i++; i++;
ldr r0,[r1] ldr r0,[r1]
add r0,#1 add r0,#1
str r0,[r1] str r0,[r1]
i = 3; i = 4;
原子变量替换整形变量。
原子变量:
//int i = 2;
atomic_t v = ATOMIC_INIT(2);
原子操作函数接口:
//i += 3;
atomic_add(3, &v);
atomic_add_return();
atomic_add_negative();
atomic_inc();
atomic_inc_and_test();
atomic_inc_return();
2. 信号量
信号量属于睡眠锁,可以指定锁的持有者的个数。
实例化:
struct semaphore sema;
初始化:
sema_init(&sema, 1);
加锁解锁:
down()/up();
如果当前进程获得信号量暂时拿不到,睡。
3. 互斥量
睡眠锁
struct mutex;
使用互斥量锁的步骤:
实例化:
struct mutex mutex;
初始化:
mutex_init(&mutex);
加锁/解锁
mutex_lock(&mutex);/mutex_unlock(&mutex);
4. 自旋锁
非睡眠锁
实例化:
spinlock_t spin;
初始化:
spin_lock_init(&spin);
加锁:
//进程和进程
spin_lock(&spin);
spin_unlock(&spin);
//进程和进程,进程和中断处理函数的竞争
spin_lock_irqsave();
spin_unlock_irqrestore();
spin_lock_bh();
spin_unlock_bh();