现在的位置: 首页 > 操作系统 > 正文

linux一些机制的总结

2017年04月12日 操作系统 ⁄ 共 5234字 ⁄ 字号 评论关闭

1.  Work

 

将任务添加到系统的工作队列中

Struct work_struct  cd_wq;

INIT_WORK(&cd_wq,work_func);

Schedule_work(&cd_wq);

实际上工作队列就是一个进程,添加到工作队列中就是调度的时候运行

 

Struct delayed_work otg_event;

#define DELAY_TIME  100

INIT_DELAYED_WORK(&otg_event,work_delayed_func);

Schedule_delayed_work(&otg_event,DELAY_TIME);

Cancel_delay_work(&otg_event);

 

       Flush_scheduled_work(void)

       刷新共享队列,因为不知道谁可能会用到这个队列,所以不能知道flush_schedule_work返回需要多长时间

 

创建自己的工作队列,并且将工作添加到此工作队列中(一个进程)

用PS能看到此工作队列名

Struct workqueue_struct *workstruct;

Struct work_struct  work;

Workqueue=create_singlethread_workqueue(dev_name(kobject));

INIT_WORK(&work,work_func);

Queue_work(workqueue,&work);

Destroy_workqueue(workqueue);

 

2.  Tasklet

Struct tasklet_struct finish_tasklet;

Tasklet_init(&finish_tasklet,tasklet_func,(unsigned long)xxx);

Tasklet_func(unsigned long param)

{    

       Struct XXX *xxx ;

       Xxx=(struct XXX *)param;

       …

}

Tasklet_schedule(&finish_tasklet);

Tasklet_kill(&finish_tasklet);

这里,tasklet与work类似

不过在硬件中断服务程式中,同时调度tasklet和work,tasklet会被先调用,也就是说tasklet在那些需要快速调用的中断下半部中可能会有较大的应用

比如

static irqreturn_t hw_irq(…)

{

       Schedule_work(&work1);

       Taklet_schedule(&tasklet1);

}

虽然tasklet激活得晚,但会先调度

同时在tasklet_handle中一般不做休眠的操作,如果在handle中msleep(10),编译的会有提示

 

3.  Completion

有点类似信号量,保证按照某种顺序执行,实现同步

#include<linux/completion>

Struct completion done;

Init_completion(&done);

Wait_for_completion(&done); //死锁

Wait_for_completion_timeout(&done,timeout)://不死锁 超时则处理

Wait_for_completion_interruptible(&done);//可以被信号打断,如果当前进程受到TIF_SIGPENDING信号,则等待该完成量的进程会被从等待队列中删除

Wait_for_completion_interruptible_timeout(&done,timeout);

Complete(&done);

Complete_all(&done);

 

完成量机制是基于等待队列的,内核使用该机制等待某一操作的完成。其有两个参与者:一是等待某操作完成;另一是在操作完成时发出声明。当然可以有“任意数目”个进程等待操作完成。

        完成量的数据描述如下:

  1. struct completion {  
  2.     unsigned int done;       /* 用于处理“在进程开始等待之前,事件或操作已经完成” */  
  3.     wait_queue_head_t wait;  /* 地等待队列 */  
  4. };  

        一、完成量的初始化

        初始化一个动态分配的completion完成量结构体,

  1. static inline void init_completion(struct completion *x)  
  2. {  
  3.     x->done = 0;  
  4.     init_waitqueue_head(&x->wait);  
  5. }  

        初始化一个静态completion完成量结构体。

  1. #define DECLARE_COMPLETION(work) \  
  2.     struct completion work = COMPLETION_INITIALIZER(work)  

        二、添加到等待队列

        进程可以通用一下函数添加到等待队列

  1. void __sched wait_for_completion(struct completion *x)  
  2. unsigned long __sched wait_for_completion_timeout(struct completion *x, unsigned long timeout)  
  3. int __sched wait_for_completion_interruptible(struct completion *x)  
  4. long __sched wait_for_completion_interruptible_timeout(struct completion *x, unsigned long timeout)  
  5. int __sched wait_for_completion_killable(struct completion *x)  
  6. long __sched wait_for_completion_killable_timeout(struct completion *x, unsigned long timeout)  

        (1) 进程在等待完成时处于不可中断状态,若使用wait_for_completion_interruptible表示可中断,如果进程被中断,则返回-ERESTARTSYS,否则返回0;

        (2) wait_for_completion_timeout表示等待事件的发生,并且提供超时设置,如果超过了这一设置,则取消等待,可防止无限等待;如果在超时之前完成则返回剩余时间,否则返回0。

        (3) wait_for_completion_killable表示可以由kill信号中断。

        其他函数均是这三者的变种。

       三、唤醒进程

        进程唤醒,可以通过以下函数实现:

  1. extern void complete(struct completion *);  
  2. extern void complete_all(struct completion *);  

        (1) complete调用每次只能从等待队列中移除一个进程。如果等待队列有N个进程,则需要执行N次、
        (2) complete_all唤醒所有的等待线程。

                

        done的解释:

        每次调用complete,done计数器都会+1,仅当done=0时,wait_for系列函数才会使得调用进程睡眠。

4.  Time.c

调试时可以查看当前时间 ns单位

       struct timespec tv3;

       getnstimeofday(&tv3);

      

       //定时启动的操作相关

       #define timer_req 20

       struct timer_list timer;

       setup_timer(&timer,timer_func,(u32)param);

       mod_timer(&timer,jiffies+HZ/1000*timer_req);

       del_timer(&timer);

 

5.    semaphore

       Struct semaphore mutex;

       Init_MUTEX(&mutex); //这里将信号量初始为互斥锁,即设置信号量的值为1

       Down(&mutex); //--

       Up(&mutex); //++

       //两个信号量可以用于同步 1,0

       //一个信号量可以用于互斥 1

       //一个信号量初始值大于1 表明可以被多个调用

 

6.        mutex

struct mutex lock;

       mutex_init(&lock);

       mutex_lock(&lock);

       mutex_unlock(&lock);

 

7.        spin_lock

由于自旋锁使用者一般保持锁时间非常短,所以选择自旋锁而调用者不会进入睡眠,所以自旋锁的效率远高于互斥锁。

信号量和读写信号量适合于保持时间较长的情况,它们会导致调用者睡眠,因此只能在进程上下文中使用(_trylock的变种能在中断上下文使用);而自旋锁非常适合保持时间非常短的情况,因为一个被争用的自旋锁使用请求它的线程在等待重新可用时自旋,特别浪费处理时间,这也是自旋锁的要害之处,所以自旋锁不应该被长时间持有。在实际应用中,自旋锁代码只能有几行,而持有自旋锁的时间也一般不会超过两次上下文切换,因为线程一旦进行切换,至少花费切出切入两次,自旋锁的时间如果远远长于两次上下文切换,就会让线程睡眠,这也失去了设计自旋锁的意义

如果被保护的共享资源只能在进程上下文(也就是多个线程共享的资源)访问,使用信号量保护该共享资源非常合适,如果对于共享资源的访问时间非常短,自旋锁也可以。但是,如果被保护的共享资源需要在中断上下文访问(包括底半部即中断处理句柄和顶半部即软中断,就必须使用自旋锁。

 

Spinlock_t lock;

Spin_lock_init(&lock);

Spin_lock(&lock);

Spin_unlock(&lock);

 

Spin_lock_irq(&lock);

Spin_unlock_irq(&lock);

//在某些情况下需要访问共享资源时必须中断失效,然后访问后才能使能中断,这种时候会用到spin_lock_irqspin_unlock_irq。考虑mxc_spi.c的驱动,在中断处理程序中有对队列是否为空进行判断,而队列的操作在中断服务程式和其他函数中均有涉及,这个时候就要保证访问共享资源时是否使中断失效

中断处理程序中,使用spin_lock spin_unlock 判断队列是否为空

外部函数中,使用spin_lock_irq spin_unlock_irq 判断队列是否为空

 

 

Spin_lock_irqsave(&lock); //保护访问共享资源前的中断标志,然后失效中断;

Spin_lock_irqrestore(&lock);//恢复访问共享资源前的中断标志,然后使能中断

 

8. Kref

       对于U盘这种支持热插拔的设备,如果用户程序正在访问的时候设备突然被拔掉,驱动程序中的设备对象是否立刻释放呢?如果立刻释放,系统调用一定会发生内存非法访问,如果需要等到用户程序close之后在释放设备对象,可以通过计数kref来实现。

       Struct kref ref;

       Kref_init(&ref);

       Kref_get(&ref);

       Kref_put(&ref,release_func);

设想,在一个字符设备的probe中,初始化计数为1,在open函数中kref_get,计数又加一,

如果出现热插拔事件,在device_disconnect函数,调用kref_put 减一,此时还不执行release_func,不释放设备对象,只有在close函数调用时,再次调用kref_put,这样计数为0,则会调用release_func,从而正确退出,保证内存管理(USB中用到) 

抱歉!评论已关闭.