首页 > 系统相关 >Linux 内核等待队列

Linux 内核等待队列

时间:2023-06-04 13:07:43浏览次数:41  
标签:__ 队列 lock list queue flags 内核 Linux wait


Linux内核中的等待队列是一种延时机制,其用于当前进程需要等待某些资源而进入一种sleep状态,当等待条件为真时,


进程被唤醒,继续执行。显然,这里涉及三个方面,即,一是等待时当前进程处理,


二是进程等待时所关注的资源处理,三时进程何时被唤醒继续执行。


所以,我们这里需要几个数据结构,主要描述如下:


typedef int (*wait_queue_func_t)(wait_queue_t *wait, unsigned mode, int flags, void *key);
 
 
 
struct __wait_queue {
 unsigned int  flags;
 void   *private;
 wait_queue_func_t func;
 struct list_head task_list;
};
 
typedef struct __wait_queue wait_queue_t;


此结构用于把当前进程进入睡眠时保持状态信息,


 


struct __wait_queue_head {
 spinlock_t  lock;
 struct list_head task_list;
};
typedef struct __wait_queue_head wait_queue_head_t;


此结构用于定义一个等待队列,task_list是在等待队列上的任务,即等待任务的状态信息,即链表上是


struct __wait_queue 对象。


 


对于等待队列中的数据结构,有时也可以使用下面这个:

struct wait_bit_key {
 void   *flags;
 int   bit_nr;
#define WAIT_ATOMIC_T_BIT_NR -1
 unsigned long  timeout;
};
struct wait_bit_queue {
 struct wait_bit_key key;
 wait_queue_t  wait;
};

对于等待队列头来说,可以通过下面方式初始化:

#define __WAIT_QUEUE_HEAD_INITIALIZER(name) {    \
 .lock  = __SPIN_LOCK_UNLOCKED(name.lock),  \
 .task_list = { &(name).task_list, &(name).task_list } }
#define DECLARE_WAIT_QUEUE_HEAD(name) \
 wait_queue_head_t name = __WAIT_QUEUE_HEAD_INITIALIZER(name)

 

而对于等待队列中的成员来说,其因为使用了struct __wait_queue结构,所以,

一般对等待队列中成员定义如下:

#define __WAITQUEUE_INITIALIZER(name, tsk) {    \
 .private = tsk,      \
 .func  = default_wake_function,   \
 .task_list = { NULL, NULL } }
#define DECLARE_WAITQUEUE(name, tsk)     \
 wait_queue_t name = __WAITQUEUE_INITIALIZER(name, tsk)

或者其他一些形式定义,如下:

#define __WAIT_BIT_KEY_INITIALIZER(word, bit)    \
 { .flags = word, .bit_nr = bit, }
#define __WAIT_ATOMIC_T_KEY_INITIALIZER(p)    \
 { .flags = p, .bit_nr = WAIT_ATOMIC_T_BIT_NR, }

 

#define init_waitqueue_head(q)    \
 do {      \
  static struct lock_class_key __key; \
       \
  __init_waitqueue_head((q), #q, &__key); \
 } while (0)

简单函数形式如下:

static inline void init_waitqueue_entry(wait_queue_t *q, struct task_struct *p)
{
 q->flags = 0;
 q->private = p;
 q->func  = default_wake_function;带唤醒函数
}
static inline void
init_waitqueue_func_entry(wait_queue_t *q, wait_queue_func_t func)
{
 q->flags = 0;
 q->private = NULL;
 q->func  = func;设置唤醒函数,但任务未设置
}

简单的加入队列过程:

static inline void __add_wait_queue(wait_queue_head_t *head, wait_queue_t *new)
{
 list_add(&new->task_list, &head->task_list);
}

 

对外简单封装函数有:

一.

void add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait)
{
 unsigned long flags;
 wait->flags &= ~WQ_FLAG_EXCLUSIVE;
 spin_lock_irqsave(&q->lock, flags);
 __add_wait_queue(q, wait);
 spin_unlock_irqrestore(&q->lock, flags);
}
EXPORT_SYMBOL(add_wait_queue);

二.

void
prepare_to_wait(wait_queue_head_t *q, wait_queue_t *wait, int state)
{
 unsigned long flags;
 wait->flags &= ~WQ_FLAG_EXCLUSIVE;
 spin_lock_irqsave(&q->lock, flags);
 if (list_empty(&wait->task_list))
  __add_wait_queue(q, wait);
 set_current_state(state);
 spin_unlock_irqrestore(&q->lock, flags);
}
EXPORT_SYMBOL(prepare_to_wait);

三.

long prepare_to_wait_event(wait_queue_head_t *q, wait_queue_t *wait, int state)
{
 unsigned long flags;
 long ret = 0;
spin_lock_irqsave(&q->lock, flags);
 if (unlikely(signal_pending_state(state, current))) {
  /*
   * Exclusive waiter must not fail if it was selected by wakeup,
   * it should "consume" the condition we were waiting for.
   *
   * The caller will recheck the condition and return success if
   * we were already woken up, we can not miss the event because
   * wakeup locks/unlocks the same q->lock.
   *
   * But we need to ensure that set-condition + wakeup after that
   * can't see us, it should wake up another exclusive waiter if
   * we fail.
   */
  list_del_init(&wait->task_list);
  ret = -ERESTARTSYS;
 } else {
  if (list_empty(&wait->task_list)) {
   if (wait->flags & WQ_FLAG_EXCLUSIVE)
    __add_wait_queue_tail(q, wait);
   else
    __add_wait_queue(q, wait);
  }
  set_current_state(state);
 }
 spin_unlock_irqrestore(&q->lock, flags);
 return ret;
}
四. 
static inline void
__add_wait_queue_exclusive(wait_queue_head_t *q, wait_queue_t *wait)
{
 wait->flags |= WQ_FLAG_EXCLUSIVE;
 __add_wait_queue(q, wait);
}

等待队列的简单使用:

一, 定义等待队列头对象

如static DECLARE_WAIT_QUEUE_HEAD(nl_table_wait);

二. 等待资源时,把当前进程加入等待队列,队列成员

void netlink_table_grab(void) __acquires(nl_table_lock)
{
 might_sleep();
 write_lock_irq(&nl_table_lock);
 if (atomic_read(&nl_table_users)) {
  DECLARE_WAITQUEUE(wait, current);自定义队列成员,并简单初始化
  add_wait_queue_exclusive(&nl_table_wait, &wait);只是进入队列,但进程还可以运行
  for (;;) {
   set_current_state(TASK_UNINTERRUPTIBLE);//改变进程状态
   if (atomic_read(&nl_table_users) == 0)必须满足这个条件,否则不会被唤醒
    break;//唤醒后,跳出
   write_unlock_irq(&nl_table_lock);
   schedule();这一步放弃CPU,真正调度出去,回来也是从这里开始
   write_lock_irq(&nl_table_lock);//再次回来时,获取锁
  }

__set_current_state(TASK_RUNNING);//设置可运行
  remove_wait_queue(&nl_table_wait, &wait);//把之前设置释放。
 }
}

三. 唤醒路径

void netlink_table_ungrab(void) __releases(nl_table_lock)
{
 write_unlock_irq(&nl_table_lock);
 wake_up(&nl_table_wait);
}

 

 

 

 


 


 


 

标签:__,队列,lock,list,queue,flags,内核,Linux,wait
From: https://blog.51cto.com/u_11860992/6410365

相关文章

  • Linux下安装jdk和Tomcat
    一、下载jdk1.5从http://www.sun.com/网站下载jdk1.5 下载:jdk-1_5_0_15-linux-i586-rpm.bin 二、安装jdk  1.#chmod+xjdk-1_5_0_15-linux-i586-rpm.bin#./jdk-1_5_0_15-linux-i586-rpm.bin按提示按输入yes,在同一目录会得到一个jdk-1_5_0_15-linux-i586-rpm通过#rpm–iv......
  • Linux操作系统安装及服务控制
    Linux操作系统安装及服务控制一、设置Linux操作系统每次开机后自动进入字符模式界面。二、使用ntsysv工具同时调整2,3,4,5运行级别中的服务状态,关闭下列服务:atd,bluetooth,mdmonitor,rhnsd,rpcgssd,postfix.关闭下列服务:atd,bluetooth,mdmonitor,rhnsd,rpcgssd,postfix.atdbluetoothmdmo......
  • 5、半虚拟化驱动--virtio(linux和windows)和安装Windows server虚拟机
    KVM的功能主要体现在利用KVM的硬件辅助性虚拟化可以提高处理速度。但在虚拟机中,有些硬件不是KVM来实现加速的,如磁盘、内存、网络的性能,如果更好的提升速度,需要安装半虚拟化驱动半虚拟化驱动在软件层通过修改源代码让硬件的操作被虚拟机和宿主机所识别半虚拟化驱动--virtio红帽RHE......
  • 串口(PL011)在Linux启动运行过程中扮演的角色
    关键词:PL011、earlyprintk、AMBA、UART、tty、console等等。串口虽然是一种简单的工具,但是在Linux启动、运行、调试中扮演了重要角色。其稳定、易用、高效(某些场景)。串口依赖的模块少,在FPGA初期调试中扮演重要角色。往往是CPU基本功能可用后,即可使能串口进行功能调试。下面记......
  • Linux常用的shell命令
    shell linux命令行就是由shell提供的,shell其实是所有命令行程序的统称,而CentOS系统中默认使用的shell程序就是bash,他是linux系统中运行的一种特殊程序,其文件位于/bin/bash,用户在登录linux系统时,系统就会自动加载一个shell程序,在用户和内核之间充当“翻译官”。 这里要跟shel......
  • 2.交换机与特殊队列
    2.交换机2.1.类型1.FanoutExchange(扇形)2.DirectExchange(直连)3.TopicExchange(主题)4.HeadersExchange(头部)以下类型的交换机使用都会使用到这两个步骤①选择依赖②修改启动类2.2.FanoutExchange2.2.1.介绍FanoutExchange:扇形交换机投递到所有绑定的队列,不需要路由键,不......
  • 查看linux中某个端口(port)是否被占用(netstat,lsof)
    netstat-tunlp会显示所有端口和所有对应的程序,用grep管道可以过滤出想要的关键字段.列一下22端口占用的程序[root@leiwantmp]#netstat-tunlp|grep22tcp000.0.0.0:429570.0.0.0:*LISTEN2230/rpc.statdtc......
  • linux 性能自我学习 ———— cpu 快速定位问题 [六]
    前言主要介绍一下cpu如何快速定位问题。正文cpu的一些性能指标:1.cpu使用率cpu使用率描述了非空闲时间占总cpu时间的百分比,根据cpu上运行任务的不同,又被分为用户cpu、系统cpu、i/o等待cpu、软中断、硬中断。用户cpu使用率,包括用户态cpu使用率,和低优先级用户态cpu使用......
  • Linux会替代Windows吗?
    Windows用户们,去还是留?Windows依然是高居榜首的桌面操作系统,占据90%以上的市场份额,远超macOS和 Linux 。从数据来看,尽管linux并不是Windows的头号接班人,但近几年越来越多用户转向Ubuntu、LinuxMint等发行版,的确为Linux带来了不小的增长。面对Windows10发布......
  • Linux会替代Windows吗?
    Windows用户们,去还是留?Windows依然是高居榜首的桌面操作系统,占据90%以上的市场份额,远超macOS和 Linux 。从数据来看,尽管linux并不是Windows的头号接班人,但近几年越来越多用户转向Ubuntu、LinuxMint等发行版,的确为Linux带来了不小的增长。面对Windows10发布......