kernel/wait.c

本文介绍了Linux内核中等待队列的基本操作,包括初始化、添加、移除等待队列等函数,并详细解释了如何实现中断式的等待以及如何唤醒特定条件下的等待者。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

/*
 * Generic waiting primitives.
 *
 * (C) 2004 Nadia Yvette Chambers, Oracle
 */
#include <linux/init.h>
#include <linux/export.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/wait.h>
#include <linux/hash.h>

void __init_waitqueue_head(wait_queue_head_t *q, const char *name, struct lock_class_key *key)
{
 spin_lock_init(&q->lock);
 lockdep_set_class_and_name(&q->lock, key, name);
 INIT_LIST_HEAD(&q->task_list);
}

EXPORT_SYMBOL(__init_waitqueue_head);

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 add_wait_queue_exclusive(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_tail(q, wait);
 spin_unlock_irqrestore(&q->lock, flags);
}
EXPORT_SYMBOL(add_wait_queue_exclusive);

void remove_wait_queue(wait_queue_head_t *q, wait_queue_t *wait)
{
 unsigned long flags;

 spin_lock_irqsave(&q->lock, flags);
 __remove_wait_queue(q, wait);
 spin_unlock_irqrestore(&q->lock, flags);
}
EXPORT_SYMBOL(remove_wait_queue);


/*
 * Note: we use "set_current_state()" _after_ the wait-queue add,
 * because we need a memory barrier there on SMP, so that any
 * wake-function that tests for the wait-queue being active
 * will be guaranteed to see waitqueue addition _or_ subsequent
 * tests in this thread will see the wakeup having taken place.
 *
 * The spin_unlock() itself is semi-permeable and only protects
 * one way (it only protects stuff inside the critical region and
 * stops them from bleeding out - it would still allow subsequent
 * loads to move into the critical region).
 */
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);

void
prepare_to_wait_exclusive(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_tail(q, wait);
 set_current_state(state);
 spin_unlock_irqrestore(&q->lock, flags);
}
EXPORT_SYMBOL(prepare_to_wait_exclusive);

/**
 * finish_wait - clean up after waiting in a queue
 * @q: waitqueue waited on
 * @wait: wait descriptor
 *
 * Sets current thread back to running state and removes
 * the wait descriptor from the given waitqueue if still
 * queued.
 */
void finish_wait(wait_queue_head_t *q, wait_queue_t *wait)
{
 unsigned long flags;

 __set_current_state(TASK_RUNNING);
 /*
  * We can check for list emptiness outside the lock
  * IFF:
  *  - we use the "careful" check that verifies both
  *    the next and prev pointers, so that there cannot
  *    be any half-pending updates in progress on other
  *    CPU's that we haven't seen yet (and that might
  *    still change the stack area.
  * and
  *  - all other users take the lock (ie we can only
  *    have _one_ other CPU that looks at or modifies
  *    the list).
  */
 if (!list_empty_careful(&wait->task_list)) {
  spin_lock_irqsave(&q->lock, flags);
  list_del_init(&wait->task_list);
  spin_unlock_irqrestore(&q->lock, flags);
 }
}
EXPORT_SYMBOL(finish_wait);

/**
 * abort_exclusive_wait - abort exclusive waiting in a queue
 * @q: waitqueue waited on
 * @wait: wait descriptor
 * @mode: runstate of the waiter to be woken
 * @key: key to identify a wait bit queue or %NULL
 *
 * Sets current thread back to running state and removes
 * the wait descriptor from the given waitqueue if still
 * queued.
 *
 * Wakes up the next waiter if the caller is concurrently
 * woken up through the queue.
 *
 * This prevents waiter starvation where an exclusive waiter
 * aborts and is woken up concurrently and no one wakes up
 * the next waiter.
 */
void abort_exclusive_wait(wait_queue_head_t *q, wait_queue_t *wait,
   unsigned int mode, void *key)
{
 unsigned long flags;

 __set_current_state(TASK_RUNNING);
 spin_lock_irqsave(&q->lock, flags);
 if (!list_empty(&wait->task_list))
  list_del_init(&wait->task_list);
 else if (waitqueue_active(q))
  __wake_up_locked_key(q, mode, key);
 spin_unlock_irqrestore(&q->lock, flags);
}
EXPORT_SYMBOL(abort_exclusive_wait);

int autoremove_wake_function(wait_queue_t *wait, unsigned mode, int sync, void *key)
{
 int ret = default_wake_function(wait, mode, sync, key);

 if (ret)
  list_del_init(&wait->task_list);
 return ret;
}
EXPORT_SYMBOL(autoremove_wake_function);

int wake_bit_function(wait_queue_t *wait, unsigned mode, int sync, void *arg)
{
 struct wait_bit_key *key = arg;
 struct wait_bit_queue *wait_bit
  = container_of(wait, struct wait_bit_queue, wait);

 if (wait_bit->key.flags != key->flags ||
   wait_bit->key.bit_nr != key->bit_nr ||
   test_bit(key->bit_nr, key->flags))
  return 0;
 else
  return autoremove_wake_function(wait, mode, sync, key);
}
EXPORT_SYMBOL(wake_bit_function);

/*
 * To allow interruptible waiting and asynchronous (i.e. nonblocking)
 * waiting, the actions of __wait_on_bit() and __wait_on_bit_lock() are
 * permitted return codes. Nonzero return codes halt waiting and return.
 */
int __sched
__wait_on_bit(wait_queue_head_t *wq, struct wait_bit_queue *q,
   int (*action)(void *), unsigned mode)
{
 int ret = 0;

 do {
  prepare_to_wait(wq, &q->wait, mode);
  if (test_bit(q->key.bit_nr, q->key.flags))
   ret = (*action)(q->key.flags);
 } while (test_bit(q->key.bit_nr, q->key.flags) && !ret);
 finish_wait(wq, &q->wait);
 return ret;
}
EXPORT_SYMBOL(__wait_on_bit);

int __sched out_of_line_wait_on_bit(void *word, int bit,
     int (*action)(void *), unsigned mode)
{
 wait_queue_head_t *wq = bit_waitqueue(word, bit);
 DEFINE_WAIT_BIT(wait, word, bit);

 return __wait_on_bit(wq, &wait, action, mode);
}
EXPORT_SYMBOL(out_of_line_wait_on_bit);

int __sched
__wait_on_bit_lock(wait_queue_head_t *wq, struct wait_bit_queue *q,
   int (*action)(void *), unsigned mode)
{
 do {
  int ret;

  prepare_to_wait_exclusive(wq, &q->wait, mode);
  if (!test_bit(q->key.bit_nr, q->key.flags))
   continue;
  ret = action(q->key.flags);
  if (!ret)
   continue;
  abort_exclusive_wait(wq, &q->wait, mode, &q->key);
  return ret;
 } while (test_and_set_bit(q->key.bit_nr, q->key.flags));
 finish_wait(wq, &q->wait);
 return 0;
}
EXPORT_SYMBOL(__wait_on_bit_lock);

int __sched out_of_line_wait_on_bit_lock(void *word, int bit,
     int (*action)(void *), unsigned mode)
{
 wait_queue_head_t *wq = bit_waitqueue(word, bit);
 DEFINE_WAIT_BIT(wait, word, bit);

 return __wait_on_bit_lock(wq, &wait, action, mode);
}
EXPORT_SYMBOL(out_of_line_wait_on_bit_lock);

void __wake_up_bit(wait_queue_head_t *wq, void *word, int bit)
{
 struct wait_bit_key key = __WAIT_BIT_KEY_INITIALIZER(word, bit);
 if (waitqueue_active(wq))
  __wake_up(wq, TASK_NORMAL, 1, &key);
}
EXPORT_SYMBOL(__wake_up_bit);

/**
 * wake_up_bit - wake up a waiter on a bit
 * @word: the word being waited on, a kernel virtual address
 * @bit: the bit of the word being waited on
 *
 * There is a standard hashed waitqueue table for generic use. This
 * is the part of the hashtable's accessor API that wakes up waiters
 * on a bit. For instance, if one were to have waiters on a bitflag,
 * one would call wake_up_bit() after clearing the bit.
 *
 * In order for this to function properly, as it uses waitqueue_active()
 * internally, some kind of memory barrier must be done prior to calling
 * this. Typically, this will be smp_mb__after_clear_bit(), but in some
 * cases where bitflags are manipulated non-atomically under a lock, one
 * may need to use a less regular barrier, such fs/inode.c's smp_mb(),
 * because spin_unlock() does not guarantee a memory barrier.
 */
void wake_up_bit(void *word, int bit)
{
 __wake_up_bit(bit_waitqueue(word, bit), word, bit);
}
EXPORT_SYMBOL(wake_up_bit);

wait_queue_head_t *bit_waitqueue(void *word, int bit)
{
 const int shift = BITS_PER_LONG == 32 ? 5 : 6;
 const struct zone *zone = page_zone(virt_to_page(word));
 unsigned long val = (unsigned long)word << shift | bit;

 return &zone->wait_table[hash_long(val, zone->wait_table_bits)];
}
EXPORT_SYMBOL(bit_waitqueue);

<think>我们正在处理一个编译错误:expected ')' before '->' token in asm-generic/current.h when using get_current() macro。 根据错误信息,问题出现在使用get_current()宏时,在asm-generic/current.h文件中出现了expected ')' before '->' token的错误。 首先,我们需要了解get_current()宏的作用。在Linux内核中,get_current()宏用于获取当前正在运行的进程的task_struct指针。这个宏通常是通过当前进程的内核栈指针来计算的。 错误信息提示在宏定义中出现了语法错误,具体是在'->'操作符之前缺少一个')'。这通常意味着宏的展开不符合语法规则,可能是由于宏的参数使用不当或者宏定义本身有问题。 我们参考引用[1]中提到的错误类型,其中包含了“expected expression before ')' token”和“expected ';', ',' or ')' before '=' token”等,这些错误通常与语法错误有关,比如括号不匹配、缺少分号等。 在引用[2]中,错误信息是“expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘zip_flags_t’”,这通常是由于在类型定义之前使用了该类型,或者类型定义有误。 但是,我们的错误是“expected ')' before '->' token”,这更可能是宏展开后产生了不正确的表达式。 在Linux内核中,get_current()宏的定义可能因体系结构而异。在x86体系结构中,通常通过current_thread_info()来获取当前线程的信息,然后通过current_thread_info()->task来获取当前进程的task_struct指针。而在其他体系结构中,可能有不同的实现。 错误信息指向了asm-generic/current.h文件。这个文件是一个通用实现,但可能并不适用于所有体系结构。通常,每个体系结构会有自己的current.h头文件。 可能的原因和解决方法: 1. **体系结构特定的头文件未正确包含**:可能是由于配置问题,导致编译器使用了通用头文件(asm-generic/current.h)而不是体系结构特定的头文件。检查你的内核配置,确保你为正确的体系结构编译,并且该体系结构的头文件在include路径中正确设置。 2. **宏定义冲突**:检查是否在包含current.h之前定义了与宏中使用的标识符冲突的宏。例如,current.h中的宏可能使用了某个标识符,而该标识符被其他头文件定义为宏,导致展开异常。 3. **语法错误**:检查current.h文件中的宏定义。通常,get_current()宏可能定义为类似这样的形式: ```c #define get_current() (current_thread_info()->task) ``` 如果current_thread_info()宏定义有问题,或者在该上下文中未定义,则可能导致错误。 4. **头文件包含顺序**:头文件的包含顺序可能导致宏定义的不同。确保先包含必要的体系结构特定头文件,再包含current.h。 5. **内核版本问题**:不同内核版本中,current.h的实现可能不同。检查你使用的内核版本,并确认该版本中asm-generic/current.h的内容是否正确。 由于我们无法直接查看你的代码和头文件内容,以下是一个通用的解决方案: 步骤1:检查体系结构配置 确保你的内核配置(.config)中指定了正确的体系结构(如x86、ARM等)。在编译时,编译器应该使用arch/your_arch/include/asm/current.h而不是asm-generic/current.h。 步骤2:检查头文件包含顺序 在包含current.h之前,确保包含了必要的体系结构头文件。例如,在x86上,你可能需要包含arch/x86/include/asm/thread_info.h。 步骤3:查看current.h文件内容 打开asm-generic/current.h,查看get_current()宏的定义。它应该类似于: ```c #ifndef _ASM_GENERIC_CURRENT_H #define _ASM_GENERIC_CURRENT_H #include <linux/thread_info.h> #define get_current() (current_thread_info()->task) #define current get_current() #endif /* _ASM_GENERIC_CURRENT_H */ ``` 如果这个文件中的定义有误,或者current_thread_info()没有正确返回指向thread_info结构的指针,则可能导致错误。 步骤4:检查current_thread_info()的定义 current_thread_info()宏应该在thread_info.h中定义。例如,在x86上,它可能定义为: ```c #define current_thread_info() ((struct thread_info *)(sp & ~(THREAD_SIZE - 1))) ``` 其中sp是当前栈指针。如果这个宏定义不正确,或者sp未定义,则会导致问题。 步骤5:考虑使用标准方式 在较新的内核版本中,通常直接使用current宏,而不是get_current()。如果你在编写内核模块,建议使用current宏。 步骤6:检查编译环境 确保你使用的交叉编译工具链(如果是交叉编译)和内核头文件版本匹配。 如果以上步骤无法解决问题,请提供更详细的错误上下文和你的代码片段,以便进一步分析。 由于错误信息指向了'->'操作符之前缺少')',我们可以推测在宏展开后,可能形成了类似这样的代码: ... (某个表达式 -> task ... 但是在这个表达式之前,可能缺少了一个右括号,导致语法错误。 例如,如果current_thread_info()宏展开后是一个函数调用,而该函数调用返回一个指向结构体的指针,那么展开后应该是: (current_thread_info()->task) 如果current_thread_info()宏定义不正确,比如少了一个括号,就可能出现错误。 因此,请仔细检查current_thread_info()宏的定义。 最后,如果问题仍然存在,请考虑在Linux内核邮件列表或相关论坛上寻求帮助,提供完整的错误信息和你的环境配置。 相关问题: 1. 如何在内核模块中获取当前进程的task_struct结构? 2. Linux内核中体系结构特定的头文件是如何组织的? 3. 在编译Linux内核时,如何配置目标体系结构? 4. 如何解决C语言宏展开导致的语法错误?</think>### 编译错误分析:`expected ')' before '->' token in asm-generic/current.h` #### 错误原因分析 该错误通常发生在使用 Linux 内核的 `get_current()` 宏时,由以下原因导致: 1. **宏展开冲突** 当自定义头文件或代码中定义了与内核头文件冲突的宏(如 `current`),会导致宏展开异常。例如: ```c // 自定义头文件中的冲突定义 #define current some_other_expression // 与内核宏冲突 ``` 2. **头文件包含顺序错误** 如果先包含自定义头文件再包含内核头文件,可能导致内核宏被覆盖: ```c #include "custom.h" // 可能定义了冲突宏 #include <asm-generic/current.h> // 内核宏被覆盖 ``` 3. **宏依赖缺失** `get_current()` 依赖 `current_thread_info()` 宏(定义在 `<asm/thread_info.h>` 中)。若未正确包含该头文件,会导致展开失败: ```c // asm-generic/current.h 中的定义 #define get_current() (current_thread_info()->task) // 依赖 thread_info.h ``` --- #### 解决方法 ##### 方法 1:检查并修复宏冲突 1. 检查所有自定义头文件和代码,避免定义与内核冲突的标识符(如 `current`、`task` 等) 2. 重命名冲突的宏: ```c // 修改前(冲突) #define current my_current // 修改后(修复) #define MY_CURRENT my_current // 使用唯一前缀 ``` ##### 方法 2:调整头文件包含顺序 确保**先包含内核头文件**,再包含自定义头文件: ```c // 正确顺序 #include <linux/sched.h> // 内核调度头文件 #include <asm/thread_info.h> // 依赖宏定义 #include <asm-generic/current.h> // get_current() 宏 #include "custom.h" // 最后包含自定义头文件 ``` ##### 方法 3:显式包含依赖 在调用 `get_current()` 前,显式包含所有依赖: ```c #include <asm/thread_info.h> // 提供 current_thread_info() #include <asm-generic/current.h> // 提供 get_current() void example() { struct task_struct *task = get_current(); // 正确展开 } ``` ##### 方法 4:直接使用内核标准宏 多数情况下推荐使用内核提供的标准宏 `current`(而非 `get_current()`): ```c #include <linux/sched.h> // 包含 current 宏 void example() { struct task_struct *task = current; // 直接获取当前任务 } ``` --- #### 验证示例 ```c // 正确示例 #include <linux/sched.h> #include <asm/thread_info.h> void demo() { // 方法 1:使用标准 current 宏 struct task_struct *t1 = current; // 方法 2:显式调用 get_current() struct task_struct *t2 = get_current(); } ``` > **关键提示**:内核版本差异可能导致宏定义路径不同。若问题持续: > 1. 检查内核源码中 `current` 的实际定义路径:`grep -r "define current" /path/to/kernel/sources` > 2. 确认交叉编译工具链与内核版本匹配 --- ### 相关问题 1. 如何安全地在 Linux 内核模块中定义与内核不冲突的宏? 2. `current_thread_info()` 宏在不同 CPU 架构(x86/ARM)中的实现差异? 3. 解决头文件包含冲突有哪些通用调试技巧? 4. 如何为特定内核版本验证宏定义的有效性? [^1]: 编译错误分析参考 [^2]: 头文件冲突案例 [^3]: 依赖解析问题 [^4]: Linux 内核宏文档
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值