天天看点

Linux内核之内核锁

 1.内核定时器:

 未来的某个时间点执行提前设置的某个任务函数。

 涉及到的核心头文件:

 #include <linux/timer.h>

 涉及到的结构体:

 struct timer_list {

  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;

 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();

继续阅读