Chinaunix首页 | 论坛 | 博客
  • 博客访问: 206832
  • 博文数量: 33
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 1277
  • 用 户 组: 普通用户
  • 注册时间: 2013-03-03 10:03
个人简介

现于杭州电子科技大学攻读硕士学位

文章分类

全部博文(33)

文章存档

2013年(33)

我的朋友

分类: LINUX

2013-09-07 16:04:53

/****************************************************休眠*********************************************************************/    
/*
    当一个进程被置为休眠时,它会被标记为一种特殊状态并从调度器的运行队列移走,直到某些情况下修改了这个状态,进程才会在任CPU调度,也即运行
该进程,休眠中的进程会被搁置在一边,等待将来的每个事件发生
  我们的驱动程序不能再拥有自旋锁锁时休眠、seqlock或者RCU锁时休眠,如果我们已经禁止了中断也不能休眠,拥有信号量休眠是合法的,但任何拥有信号量
而休眠的代码必须很短,并且还要确保有能唤醒自己,能够找到休眠的进程意味着需要维护一个称为等待队列的数据结构,等待队列就是一个进程链表,其中包含了
等待某个特定事件的所有进程,在linux中,一个等待队列通过一个“等待队列头”来管理
*/
//等待队列:
#include
//初始化一个等待队列节点:
DECLARE_WAIT_QUEUE_HEAD(name);

wait_queue_head_t  xxx_queue; //定义一个等待队列节点
init_waitqueue_head(&xxx_queue);//初始化对待队列节点

DECLARE_WAITQUEUE(name, tsk); //定义等待队列,wait_queue_t name,struct task_struct *tsk

//添加或移除等待队列:
void fastcall add_wait_queue(wait_queue_head_t *q,wait_queue_t *wait );//将等待队列wait添加到等待队列头q指向的等待队列链表中
void fastcall remove_wait_queue(wait_queue_head_t *q,wait_queue_t *wait );//将等待队列wait从等待队列头q指向的等待队列链表中移除


//等待事件(简单休眠):
wait_event(xxx_queue,condition);  /*xxx_queue为等待队列头,是值传递,condition是任意一个布尔表达式,在条件为真前,进程会保持休眠*/
wait_event_interruptible(xxx_queue,condition);  //可中断,返回值为非0表示休眠被某个信号中断,而驱动程序要返回-ERESTARTSYS

wait_event_timeout(xxx_queue,condition,timeout);  //只会等待给定的时间,当给定的时间到期时,这两个宏都会返回0值,不论condition如何求值,如果由其他事件唤醒,则返回剩余的延时时间
wait_event_interruptible_timeout(xxx_queue,condition,timeout);

//唤醒休眠的进程:
void wake_up(wait_queue_head_t *queue);
void wake_up_interruptible(wait_queue_head_t *queue);

//在等待队列上睡眠:
sleep_on(wait_queue_head_t *q);//将目前进程的状态设置为TASK_UNINTERRUPTIBLE,并定义一个等待队列,之后把它附属到等待队列头,直到资源可获取,q引导的等待队列被唤醒,其和wake_up成对使用
interruptible_sleep_on(wait_queue_head_t *q);//与上类似,其将目前进程的状态设置为TASK_INTERRUPTIBLE,其和wake_up_interruptible成对使用

/*
高级休眠:
复杂的锁定以及性能需求会强制驱动程序使用底层的函数来实现休眠(建议首先使用内核提供的函数,如果它们确实无法满足要求,那么再构造自己的睡眠和唤醒函数)
将进程置于休眠的步骤:
(1) 分配并初始化一个等待队列wait_queue_t,然后将其加入到对应的等待队列头指向的等待链表
      DECLARE_WAITQUEUE(wait, current); //定义等待队列,wait_queue_t wait,struct task_struct current--->当前进程
     
(2) 设置进程的状态,将其标记休眠,中定义了多个任务状态,TASK_RUNNING表示进程可运行,尽管进程并不一定在任何给定时间都运行在每个处理器上
      ,TASK_INTERRUPTIBLE和TASK_UNINTERRUPTIBLE表明进程处于休眠状态
      可调用下面函数来设置进程状态:
      void set_current_state(int new_state);  //在老代码中还可以看到current->state=TASK_INTERRUPTIBLE

(3)将等待队列加入到对应的等待队列头指向的等待链表
      add_wait_queue(wait_queue_head_t *q,&wait);//将等待队列wait添加到等待队列头q指向的等待队列链表中
    
(4)通过改变当前进程状态,我们只是改变了调度器处理该进程的方式,但尚未使进程让出处理器,那么最后一步就是放弃处理器
     调用下面函数:
     schedule();  //对schedule的调用将调用调度器,并让出CPU

(5)唤醒以后将等待节点从队列中移除
     remove_wait_queue(wait_queue_head_t *q,&wait );//将等待队列wait从等待队列头q指向的等待队列链表中移除
*/
//手工休眠:
(1)建立并初始化一个等待队列入口
     DEFINE_WAIT(xxx_wait);//name为等待队列入口变量的名称
     或
     wait_queue_t xxx_wait;
     init_wait(&xxx_wait);
(2)将等待队列入口添加到队列中,并设置进程的状态
    void prepare_to_wait(wait_queue_head_t *queue, wait_queue_t *wait, int state);  //queue和wait分别是等待队列头和进程入口,state为进程的新状态,它应该是TASK_INTERRUPTIBLE和TASK_UNINTERRUPTIBLE
(3)让出CPU
    if(条件没有就绪)
      schedule();
(4)一旦 schedule返回,就到了清理时间了
     void finish_wait(wait_queue_head_t *queue,wait_queue_t *wait) ;
(5)之后,代码可测试其状态,并判断是否需要重新等待

//在许多设备驱动中亲自进行进程的状态改变和切换:

 {
  DECLARE_WAITQUEUE(wait, current); //定义等待队列 
  add_wait_queue(&xxx_wait,&wait ); //添加等待队列
 
  do{
   .....
    if(资源未就绪)
     {
      __set_current_state(TASK_INTERRUPTIBLE); //改变进程状态
      schedule();//调度其他进程执行
      }
    .....
    }while(资源未就绪);
   
  ...........
  ...........   
 }


/****************************************************end**********************************************************************/

/***************************************************tasklet(小任务)*********************************************************************/
/*
 *tasklet(小任务):
 *   中断管理中大量使用了这种机制,tasklet在很多方面类似内核定时器:它们始终在中断期间运行,始终会在调度它们的同一CPU上运行,而且都接收一个unsigned long类型的参数,但我们不能要求
 *tasklet在某个给定的时间执行,调度一个tasklet,表明我们希望内核选择某个其后的时间来执行给定的函数
 *   一个tasklet可在稍后被禁止或者重新启动,只有启动的次数和禁止的次数相同时,tasklet才会被执行。tasklet可以注册自己本身。tasklet可被调度以通常的优先级或高优先级执行,如果
 *系统负荷不重,则tasklet会立即得到执行
 *   tasklet以结构体形式的数据结构的形式存在,并在使用前必须初始化,可调用下面函数中的一个进行初始化
 */
#include
struct tasklet_struct   /*小任务*/
{
 struct tasklet_struct *next;
 unsigned long state;
 atomic_t count;
 void (*func)(unsigned long);
 unsigned long data;      //是func函数指针指向的函数的参数
};
void tasklet_init(struct tasklet_struct *t,void (*func)(unsigned long),unsigned long data);  //初始化

DECLARE_TASKLET(name,func,data);         //初始化

DECLARE_TASKLET_DISABLED(name,func,data); //初始化

void tasklet_disable(struct tasklet_struct *t); //禁止指定的tasklet,该tasklet仍然可以用tasklet_schedule调度,但其执行被推迟,直到该tasklet被重新启动。如果终止当前tasklet正在运行,该函数会
                                                //进入忙等待直到tasklet退出为止,因此,在调用tasklet_disable之后,我们可以确信该tasklet不会在系统中的任何地方运行
                                               
void tasklet_disable_nosync(struct tasklet_struct *t); //禁止指定的tasklet,但不会等待任何正在运行的tasklet退出,该函数返回后,tasklet是禁止的,而且在重新启动之前,不会再次被调度,但是
                                                       //当该函数返回时,指定的tasklet可能仍在其他cpu上运行
                                                      
void tasklet_enable(struct tasklet_struct *t);//启动一个先前被禁止的tasklet,如果该tasklet已经被调度,它很快就会运行;对tasklet_enable调用和每个对tasklet_disable的调用匹配

void tasklet_schedule(struct tasklet_struct *t); //调度执行指定的tasklet

void tasklet_hi_schedule(struct tasklet_struct *t); //调度执行指定的tasklet以高优先级执行,理想状态下只有具备低延迟需求的任务才能使用这个函数

void tasklet_kill(struct tasklet_struct *t); //该函数确保指定的tasklet不会被再次调度运行,当设备要被关闭或模块要移除时,我们通常调用这个函数,如果tasklet正被调度执行,该函数会等待其退出

/*
   一:使用高优先级延迟任务软中断的步骤如下:
   1:根据实际需要为struct tasklet_struct结构的tasklet_hi申请内存空间
   2:调用tasklet_init()函数初始化tasklet_hi
   3:调用tasklet_hi_schedule()将tasklet_hi加入到当前处理器的内核高优先级延迟任务软中断链表tasklet_hi_vec的最前面
      激活高优先级延迟任务软中断
   */

/*
  二:使用常规延迟任务软中断的步骤如下:
  1:根据实际需要为struct tasklet_struct结构的tasklet申请内存空间
  2:调用tasklet_init()函数初始化tasklet
  3:调用tasklet_schedule()将tasklet加入到当前处理器的内核常规延迟任务软中断链表tasklet_vec的最前面
     激活常规延迟任务软中断
  */

/*
   三:使用定时器软中断
   1:构造一个struct softirq_action结构的操作函数void (*action)(struct softirq_action *sa)相同类型的函数fun_sa(sa),通常sa
      指向softirq_vec[TIMER_SOFTIRQ],但是操作函数一般不用
   2:在程序开始阶段执行open_soft(nr,fun_sa,NULL)将fun_sa安装到softirq_vec[nr].action中,nr为TIMER_SOFTIRQ
   3:在需要运行定时器软中断操作函数时调用raise_softirq(nr)(需要关中断保护,如定时器中断)或者raise_softirq_irqoff(nr)
      (不需要关中断保护,如网络收发和SCSI设备驱动软中断)
  */

/*
   四:使用其他的网络发送软中断
    网络发送软中断、网络接收软中断、SCSI接口设备软中断的使用方法和使用定时器软中断的方法类似
  */


/****************************************************end**********************************************************************/

/**************************************************工作队列*******************************************************************/

//工作队列类似tasklet,它们都允许内核代码请求某个函数在将来的时间被调用,tasklet代码必须是原子的,而工作队列函数是可以休眠的

#include
struct workqueue_struct {  //工作队列
 struct cpu_workqueue_struct *cpu_wq;
 struct list_head list;
 const char *name;
 int singlethread;
 int freezeable;  /* Freeze threads during suspend */
 int rt;
#ifdef CONFIG_LOCKDEP
 struct lockdep_map lockdep_map;
#endif
};

//在使用之前,必须显示地创建一个工作队列,使用下面函数:
//每个工作队列有一个或多个专用的进程,这些进程运行提交到该队列的函数
struct workqueue_struct *creat_workqueue(const char *name);//使用它,内核会在系统中的每个处理器上为该工作队列创建专用的线程

struct workqueue_struct *creat_singlethread_workqueue(const char *name);  //如果单个工作线程足够使用,那么应该使用creat_singlethread_workqueue创建工作队列

//向一个工作队列提交一个任务,需要构造一个work_struct结构,可通过下面接口完成:
DECLARE_WORK(name,void(*function)(void*),void *data); //name是要声明的结构名称,function是要从工作队列中调用的函数,data是要传递给该函数的值
//或动态运行时
INIT_WORK(struct work_struct *work,void(*function)(void*),void *data );//该函数完成更加彻底的结构初始化,在首次构造该结构时,应该使用它

PREPARE_WORK(struct work_struct *work,void(*function)(void*),void *data ); //PREPARE_WORK完成几乎相同的工作,但它不会初始化用来将work_struct结构链接到工作队列的指针,
                                                                           //如果结构已经被提交到工作队列,而只是修改该结构,则应该使用它而不是INIT_WORK
//将工作提交到工作队列,使用下面两个函数之一:
int queue_work(struct workqueue_struct *wq, struct work_struct *work);
int queue_delayed_work(struct workqueue_struct *wq,struct delayed_work *dwork, unsigned long delay);  //它们都会将dwork->work添加到给定的queue,但queue_delayed_work会延时,如果添加成功,则返回1,在将来的某个时间
                                                                                                      //工作函数会被调用,并传入给定的data值,该函数不能访问用户空间
//如果要取消某个挂起的工作队列入口项,可调用:
int cancel_delayed_work(struct work_struct *work);//如果该入口项在开始执行前被取消,则该函数返回非0,在调用它之后,内核会确保不会初始化给定入口项的执行,但是如果cancel_delayed_work返回0,则说明
                                                  //该入口项已经在其他处理器上运行,因此在cancel_delayed_work返回后可能仍在运行,为了绝对确保在cancel_delayed_work返回0后,工作函数不会在系统中的
                                                  //任何地方运行,则应该随后调用下面函数
                                                 
void flush_workqueue(struct workqueue_struct *queue);  //它被调用返回后,任何在调用之前被提交的工作函数都不会在系统任何地方运行

void destroy_workqueue(struct workqueue_struct *queue); //在结束对工作队列的使用后,可调用该函数释放资源

int schedule_work(struct work_struct *work);//调度工作队列执行
int schedule_delayed_work(struct work_struct *work,unsigned long delay);//调度工作队列在给定的时间后执行

/*
  工作队列的使用
  方法一:
  1:自行声明一个工作结构struct work_struct mywork,并用字定义的工作函数fun()初始化mywork,如DECLARE_WORK(mywork,fun,NULL)
  2:调用schedule_work(mywork)或schedule_delay_work(mywork,delay)函数将mywork通过mywork.entry链接到keventd_wq->struct cpu_workqueue_struct  的
    worklist链表的末尾,将工作mywork添加到队列中去

  方法二:
  1:调用creat_workqueue()或creat_singlethread_workqueue()创建一个名为name的struct workqueue_struct mywq,并在系统每个cpu(调用前一个
     函数)或者只在0号cpu(调用后一个函数)上为工作队列mywq创建一个工作者线程,其执行函数为worker_thread()
  2:自行声明一个工作结构struct work_struct mywork,并用自行定义的工作函数fun()初始化mywork如DECLARE_WORK(mywork,fun,NULL)
  3:调用queue_work(mywork,fun)将工作结构mywork添加到工作队列mywq中去
  */
/****************************************************end**********************************************************************/

阅读(2685) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~