唤醒 -- try_to_wake_up()

    唤醒操作通过函数wake_up进行,它会唤醒指定的等待队列上的所有进程。它调用函数try_to_wake_up,该函数负责将进程设置为 TASK_RUNNING状态,调用activate_task将此进程放入可执行队列,如果被唤醒的进程优先级比当前正在运行的进程的优先级高,还有设 置need_resched标志。 通常哪段代码促使等待条件达成,它就负责随后调用wake_up()函数。
    The try_to_wake_up( ) function awakes a sleeping or stopped process by setting its state to TASK_RUNNING and inserting it into the runqueue of the local CPU.
    The function receives as its parameters:
1. The descriptor pointer (p) of the process to be awakened
2. A mask of the process states (state) that can be awakened(要唤醒的进程状态集)
3. A flag (sync) that forbids the awakened process to preempt the process currently running on the local CPU
(如果sync为1则表示禁止唤醒进程p抢占当前进程)

static int try_to_wake_up(task_t * p, unsigned int state, int sync)
{
    int cpu, this_cpu, success = 0;
    unsigned long flags;
    long old_state;
    runqueue_t *rq;
#ifdef CONFIG_SMP
    unsigned long load, this_load;
    struct sched_domain *sd;
    int new_cpu;
#endif

1. 关闭本地中断并给本地可执行队列rq加锁
Invokes the task_rq_lock( ) function to disable local interrupts and to acquire the lock of the runqueue rq owned by the CPU that was last executing the process (it could be different from the local CPU). The logical number of that CPU is stored in the p->thread_info->cpu field.
|----------------------------------|
|   rq = task_rq_lock(p, &flags);  |
|----------------------------------|

2. 如果当前进程状态p->state不在要唤醒的进程状态集中,则不能唤醒该进程
Checks if the state of the process p->state belongs to the mask of states state passed as argument to the function; if this is not the case, it jumps to step 9 to terminate the function.
|----------------------------------|
|   old_state = p->state;          |
|   if (!(old_state & state))      |
|       goto out;                  |
|----------------------------------|

3. 如果当前进程本身就在可执行队列中,则无需唤醒本进程
If the p->array field is not NULL, the process already belongs to a runqueue; therefore, it jumps to step 8.
|----------------------------------|
|   if (p->array)                  |
|       goto out_running;          |
|----------------------------------|

task_cpu(p)返回当前进程p所使用的CPU编号(p所归属的runqueue所在的CPU编号)
|----------------------------------|
|   cpu = task_cpu(p);             |
|   this_cpu = smp_processor_id(); |
|----------------------------------|

4. 多处理器系统:

#ifdef CONFIG_SMP
    if (unlikely(task_running(rq, p)))
        goto out_activate;
#ifdef CONFIG_SCHEDSTATS
    schedstat_inc(rq, ttwu_cnt);
    if (cpu == this_cpu) {
        schedstat_inc(rq, ttwu_local);
    } else {
        for_each_domain(this_cpu, sd) {
            if (cpu_isset(cpu, sd->span)) {
                schedstat_inc(sd, ttwu_wake_remote);
                break;
            }
        }
    }
#endif

    new_cpu = cpu;
    if (cpu == this_cpu || unlikely(!cpu_isset(this_cpu, p->cpus_allowed)))
        goto out_set_cpu;

    load = source_load(cpu);
    this_load = target_load(this_cpu);

    if (sync)
        this_load -= SCHED_LOAD_SCALE;

    if (load < SCHED_LOAD_SCALE/2 && this_load > SCHED_LOAD_SCALE/2)
        goto out_set_cpu;

    new_cpu = this_cpu;

    for_each_domain(this_cpu, sd) {
        unsigned int imbalance;
        imbalance = sd->imbalance_pct + (sd->imbalance_pct - 100) / 2;

        if ((sd->flags & SD_WAKE_AFFINE) &&
                !task_hot(p, rq->timestamp_last_tick, sd)) {
            if (cpu_isset(cpu, sd->span)) {
                schedstat_inc(sd, ttwu_move_affine);
                goto out_set_cpu;
            }
        } else if ((sd->flags & SD_WAKE_BALANCE) &&
                imbalance*this_load <= 100*load) {
             if (cpu_isset(cpu, sd->span)) {
                schedstat_inc(sd, ttwu_move_balance);
                goto out_set_cpu;
            }
        }
    }

    new_cpu = cpu;
out_set_cpu:
    new_cpu = wake_idle(new_cpu, p);
    if (new_cpu != cpu) {
        set_task_cpu(p, new_cpu);
        task_rq_unlock(rq, &flags);
        rq = task_rq_lock(p, &flags);
        old_state = p->state;
        if (!(old_state & state))
            goto out;
        if (p->array)
            goto out_running;

        this_cpu = smp_processor_id();
        cpu = task_cpu(p);
    }

out_activate:
#endif


5.If the process is in the TASK_UNINTERRUPTIBLE state, it decreases the nr_uninterruptible field of the target runqueue, and sets the p->activated field of the process descriptor to -1.
|----------------------------------------------|
|   if (old_state == TASK_UNINTERRUPTIBLE) {   |
|       rq->nr_uninterruptible--;              |
|       p->activated = -1;                     |
|   }                                          |
|----------------------------------------------|

6. 更新唤醒进程p的平均睡眠时间sleep_avg和动态优先级prio;记录该进程唤醒前的睡眠状态;将该进程插入活跃优先级数组
|----------------------------------------------|
|   activate_task(p, rq, cpu == this_cpu);     |
|----------------------------------------------|

7. 如果唤醒进程p的动态优先级prio比当前进程current的动态优先级高则当前进程的 TIF_NEED_RESCHED就需要设置
If either the target CPU is not the local CPU or if the sync flag is not set, it checks whether the new runnable process has a dynamic priority higher than that of the current process of the rq runqueue (p->prio < rq->curr->prio); if so, invokes resched_task( ) to preempt rq->curr.
|----------------------------------------------|
|   if (!sync || cpu != this_cpu) {            |
|       if (TASK_PREEMPTS_CURR(p, rq))         |
|           resched_task(rq->curr);            |
|   }                                          |
|   success = 1;                               |
|----------------------------------------------|

8. Sets the p->state field of the process to TASK_RUNNING.
|---------------------------------|
|out_running:                     |
|    p->state = TASK_RUNNING;     |
|---------------------------------|

9. Invokes task_rq_unlock( ) to unlock the rq runqueue and reenable the local interrupts.
|---------------------------------|
|out:                             |
|    task_rq_unlock(rq, &flags);  |
|---------------------------------|
    return success;
}







 
### FLUSHER_THREADS 线程的具体代码实现 在 Linux 内核中,`flusher_threads` 是一组专门用于处理脏页写回磁盘的内核线程。以下是其具体实现的关键部分解析: --- #### 1. **FLUSHER_THREADS 的初始化** `flusher_threads` 的启动和初始化通常发生在文件系统初始化过程中。核心函数为 `bdi_register()` 和 `bdi_start_background_thread()`。以下是相关代码片段: ```c // bdi_start_background_thread 函数负责创建 flusher 线程 struct backing_dev_info *bdi; bdi->thread = kthread_run(bdi_fork_fn, bdi, "bd-%s", bdi->name); if (IS_ERR(bdi->thread)) { pr_err("Failed to start background thread for BDI %s\n", bdi->name); return PTR_ERR(bdi->thread); } ``` 此处通过 `kthread_run()` 创建了一个名为 `bd-*` 的内核线程,该线程的主要入口函数为 `bdi_fork_fn`[^4]。 --- #### 2. **FLUSHER_THREADS 的主循环** `flusher_threads` 的核心逻辑位于 `bdi_fork_fn` 中,这是一个无限循环,等待条件触发后执行脏页写回操作。以下是其实现概览: ```c static int bdi_fork_fn(void *data) { struct backing_dev_info *bdi = data; set_freezable(); while (!kthread_should_stop()) { wait_event_interruptible(bdi->wb_waitq, wb_has_dirty_io(bdi) || freezing(current)); try_to_wake_up_flushers(bdi); if (try_to_freeze()) continue; balance_dirty_pages(&bdi->wb); } return 0; } ``` - **`wait_event_interruptible`**: 等待队列上的事件通知,只有当存在需要写回的脏页时才会被唤醒- **`balance_dirty_pages`**: 实际执行脏页写回的核心函数,控制写回速度以避免过度占用 I/O 带宽[^1]。 --- #### 3. ** Dirty Page Writeback 的实现细节** `balance_dirty_pages` 是 `flusher_threads` 的核心功能之一,它决定了何时以及如何将脏页写回磁盘。以下是其实现要点: ##### a. **判断是否需要唤醒 Flush Threads** ```c void wake_flusher_threads(long nr_pages) { struct backing_dev_info *bdi; rcu_read_lock(); list_for_each_entry_rcu(bdi, &bdi_list, bdi_list) { if (test_bit(BDI_writeback_running, &bdi->state)) continue; spin_lock_irq(&bdi->wb_lock); if (!list_empty(&bdi->wb.b_dirty) && !bdi->wb.thread) { bdi_start_background_thread(bdi); } spin_unlock_irq(&bdi->wb_lock); } rcu_read_unlock(); } ``` 此函数遍历所有注册的 `backing_dev_info` 对象,并根据条件决定是否需要启动新的 `flusher_threads`[^2]。 ##### b. **实际写回过程** ```c void balance_dirty_pages(struct bdi_writeback *wb) { unsigned long pages_written = 0; unsigned long write_chunk = dirty_background_ratio / 100 * totalram_pages(); while (pages_written < write_chunk && get_nr_dirty_pages(wb) > 0) { writeout_one_page(wb); pages_written++; } if (get_nr_dirty_pages(wb) == 0) { clear_bdi_congested(wb->bdi, WB_CONGESTED_WRITE); } } ``` - **`writeout_one_page`**: 每次尝试写回单个脏页。 - **`clear_bdi_congested`**: 当所有脏页都被成功写回时,清除阻塞标志位[^1]。 --- #### 4. **Wake-Up Mechanism** `flusher_threads` 的唤醒机制依赖于以下几个关键变量和函数: - **`dirty_background_ratio`**: 控制后台写回的阈值,当空闲内存低于此比例时触发写回。 - **`global_page_state(NR_FILE_DIRTY)`**: 获取当前系统的脏页数量。 - **`wake_flusher_threads`**: 显式唤醒所有可能需要参与写回的 `flusher_threads`[^2]。 --- ### 总结 `flusher_threads` 的实现围绕着脏页管理和写回展开,主要包括以下几方面: 1. 初始化阶段通过 `bdi_register` 注册设备对象并启动对应线程; 2. 主循环中监听事件并通过 `balance_dirty_pages` 执行具体的写回操作; 3. 提供灵活的唤醒机制以适应不同负载情况下的需求。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值