【内核调度、hotplug进程迁移】【migrate_tasks】

本文介绍了在CPU热插拔过程中,如何使用migrate_tasks函数迁移运行中的进程到其他CPU。在cpu_down()时,会触发迁移,而在cpu_up()时则依赖负载均衡策略。迁移过程中,每个任务的目标CPU可能各不相同,move_queued_task在__migrate_task中起关键作用。

在cpu_down()时,需要调用migration_call() -> migrate_tasks()把cpu上所有runnable进程迁移到其他cpu;在cpu_up()时,并不需要在函数中迁移进程,直接等待负载均衡算法的迁移。

migrate_tasks

需要注意的是被迁移的task的dst cpu很有可能都是不一样的。

/*
 * Migrate all tasks from the rq, sleeping tasks will be migrated by
 * try_to_wake_up()->select_task_rq().
 *
 * Called with rq->lock held even though we'er in stop_machine() and
 * there's no concurrency possible, we hold the required locks anyway
 * because of lock validation efforts.
 */
static void migrate_tasks(struct rq *dead_rq, struct rq_flags *rf)
{
	struct rq *rq = dead_rq;
	struct task_struct *next, *stop = rq->stop;
	struct rq_flags orf = *rf;
	int dest_cpu;

	/*
	 * Fudge the rq selection such that the below task selection loop
	 * doesn't get stuck on the currently eligible stop task.
	 *
	 * We're currently inside stop_machine() and the rq is either stuck
	 * in the stop_machine_cpu_stop() loop, or we're executing this code,
	 * either way we should never end up calling schedule() until we're
	 * done here.
	 */
	rq->stop = NULL;

	/*
	 * put_prev_task() and pick_next_task() sched
	 * class method both need to have an up-to-date
	 * value of rq->clock[_task]
	 */
	update_rq_clock(rq);

	for (;;) {
		/*
		 * There's this thread running, bail when that's the only
		 * remaining thread:
		 */
		if (rq->nr_running == 1)
			break;

		/*
		 * pick_next_task() assumes pinned rq->lock:
		 */
		 /* (1) 逐个从rq中获取task = next */
		next = pick_next_task(rq, &fake_task, rf);
		BUG_ON(!next);
		put_prev_task(rq, next);

		/*
		 * Rules for changing task_struct::cpus_allowed are holding
		 * both pi_lock and rq->lock, such that holding either
		 * stabilizes the mask.
		 *
		 * Drop rq->lock is not quite as disastrous as it usually is
		 * because !cpu_active at this point, which means load-balance
		 * will not interfere. Also, stop-machine.
		 */
		rq_unlock(rq, rf);
		raw_spin_lock(&next->pi_lock);
		rq_relock(rq, rf);

		/*
		 * Since we're inside stop-machine, _nothing_ should have
		 * changed the task, WARN if weird stuff happened, because in
		 * that case the above rq->lock drop is a fail too.
		 */
		if (WARN_ON(task_rq(next) != rq || !task_on_rq_queued(next))) {
			raw_spin_unlock(&next->pi_lock);
			continue;
		}

		/* Find suitable destination for @next, with force if needed. */
		/* (2) 找到最适合next进程迁移的目的cpu */
		dest_cpu = select_fallback_rq(dead_rq->cpu, next);
		/* (3) 实施进程迁移 */
		rq = __migrate_task(rq, rf, next, dest_cpu);
		if (rq != dead_rq) {
			rq_unlock(rq, rf);
			rq = dead_rq;
			*rf = orf;
			rq_relock(rq, rf);
		}
		raw_spin_unlock(&next->pi_lock);
	}

	rq->stop = stop;
}

__migrate_task函数调用move_queued_task

/*
 * Move (not current) task off this CPU, onto the destination CPU. We're doing
 * this because either it can't run here any more (set_cpus_allowed()
 * away from this CPU, or CPU going down), or because we're
 * attempting to rebalance this task on exec (sched_exec).
 *
 * So we race with normal scheduler movements, but that's OK, as long
 * as the task is no longer on this CPU.
 */
static struct rq *__migrate_task(struct rq *rq, struct rq_flags *rf,
				 struct task_struct *p, int dest_cpu)
{
	/* Affinity changed (again). */
	if (!is_cpu_allowed(p, dest_cpu))
		return rq;

	update_rq_clock(rq);
	rq = move_queued_task(rq, rf, p, dest_cpu);

	return rq;
}

move_queued_task

/*
 * This is how migration works:
 *
 * 1) we invoke migration_cpu_stop() on the target CPU using
 *    stop_one_cpu().
 * 2) stopper starts to run (implicitly forcing the migrated thread
 *    off the CPU)
 * 3) it checks whether the migrated task is still in the wrong runqueue.
 * 4) if it's in the wrong runqueue then the migration thread removes
 *    it and puts it into the right queue.
 * 5) stopper completes and stop_one_cpu() returns and the migration
 *    is done.
 */

/*
 * move_queued_task - move a queued task to new rq.
 *
 * Returns (locked) new rq. Old rq's lock is released.
 */
static struct rq *move_queued_task(struct rq *rq, struct rq_flags *rf,
				   struct task_struct *p, int new_cpu)
{
	lockdep_assert_held(&rq->lock);

	p->on_rq = TASK_ON_RQ_MIGRATING;
	dequeue_task(rq, p, DEQUEUE_NOCLOCK);
	rq_unpin_lock(rq, rf);
	double_lock_balance(rq, cpu_rq(new_cpu));
	set_task_cpu(p, new_cpu);
	double_rq_unlock(cpu_rq(new_cpu), rq);

	rq = cpu_rq(new_cpu);

	rq_lock(rq, rf);
	BUG_ON(task_cpu(p) != new_cpu);
	enqueue_task(rq, p, 0);
	p->on_rq = TASK_ON_RQ_QUEUED;
	check_preempt_curr(rq, p, 0);

	return rq;
}

 

<think>好的,我现在需要帮助用户了解如何正确注册libusb的热插拔回调函数,特别是libusb_hotplug_register_callback的参数详解和用法示例。首先,我需要回顾一下自己对libusb的了解,确保信息的准确性。 首先,libusb是一个用于用户空间访问USB设备的库,热插拔回调允许应用程序在USB设备插入或移除时得到通知。用户提到的函数libusb_hotplug_register_callback是关键,需要详细解释每个参数的作用。 根据用户提供的引用内容,特别是引用[3]中的函数定义,参数包括ctx, events, flags, vendor_id, product_id, dev_class, cb_fn, user_data, callback_handle。我需要逐一解释这些参数的含义,并说明如何正确设置它们。 接下来,我需要考虑示例代码的结构。通常,注册回调需要定义回调函数,然后调用libusb_hotplug_register_callback。示例应该展示如何设置参数,比如设置事件类型为LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED和LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT,设置厂商ID和产品ID,或者使用LIBUSB_HOTPLUG_MATCH_ANY来匹配任何设备。 另外,需要注意libusb的初始化和事件处理循环,因为注册回调后需要处理事件。用户可能对事件循环的实现不太清楚,所以示例应包括libusb_init和libusb_handle_events的相关代码。 还需要提到资源管理的问题,比如在回调中是否需要引用设备,以及如何注销回调函数。根据引用[3],libusb_ref_device和libusb_unref_device用于管理设备的引用计数,防止设备在使用中被释放。同时,引用[1]提到libusb_hotplug_deregister_callback用于注销回调,确保资源正确释放。 可能的疑问点:vendor_id和product_id设置为0或LIBUSB_HOTPLUG_MATCH_ANY的区别,dev_class的作用,以及如何确保回调函数正确处理不同的事件类型。此外,用户可能想知道在多线程环境下如何处理事件循环,或者如何处理多个回调的情况。 最后,生成相关问题时要围绕libusb的热插拔机制,可能的优化、错误处理、平台差异等。确保问题有针对性,帮助用户深入理解。</think>以下是对`libusb_hotplug_register_callback`函数的参数详解及用法说明: ### 一、函数原型 ```c int libusb_hotplug_register_callback( libusb_context *ctx, int events, int flags, int vendor_id, int product_id, int dev_class, libusb_hotplug_callback_fn cb_fn, void *user_data, libusb_hotplug_callback_handle *callback_handle ); ``` ### 二、参数详解 1. **`ctx`** - 类型:`libusb_context*` - 作用:上下文对象,通常传递`NULL`表示使用默认上下文[^2] - 示例:`NULL` 2. **`events`** - 类型:`int` - 作用:指定监听的热插拔事件类型,通过位掩码组合: - `LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED`(设备接入) - `LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT`(设备移除) - 示例:`LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT` 3. **`flags`** - 类型:`int` - 作用:控制匹配逻辑的标志,常用值: - `LIBUSB_HOTPLUG_MATCH_ANY`(匹配任意设备) - 示例:`LIBUSB_HOTPLUG_MATCH_ANY` 4. **`vendor_id`/`product_id`** - 类型:`int` - 作用:筛选设备的厂商ID和产品ID - 特殊值:`LIBUSB_HOTPLUG_MATCH_ANY`(不限制) - 示例:`0x1234`(特定设备)或`LIBUSB_HOTPLUG_MATCH_ANY` 5. **`dev_class`** - 类型:`int` - 作用:筛选设备的USB类代码 - 特殊值:`LIBUSB_HOTPLUG_MATCH_ANY` - 示例:`LIBUSB_CLASS_HID`(人机接口设备类) 6. **`cb_fn`** - 类型:`libusb_hotplug_callback_fn` - 作用:热插拔事件触发时调用的回调函数 - 函数签名: ```c int (*libusb_hotplug_callback_fn)(libusb_context *ctx, libusb_device *device, libusb_hotplug_event event, void *user_data); ``` 7. **`user_data`** - 类型:`void*` - 作用:传递给回调函数的自定义数据指针 - 示例:传递设备管理器对象指针 8. **`callback_handle`** - 类型:`libusb_hotplug_callback_handle*` - 作用:输出参数,用于存储注册回调的句柄,后续用于注销回调[^1] --- ### 三、用法示例 ```c #include <libusb-1.0/libusb.h> // 定义回调函数 static int hotplug_callback(libusb_context *ctx, libusb_device *device, libusb_hotplug_event event, void *user_data) { if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED) { printf("Device connected\n"); libusb_ref_device(device); // 增加设备引用计数[^3] } else if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT) { printf("Device disconnected\n"); libusb_unref_device(device); // 减少引用计数[^3] } return 0; } int main() { libusb_init(NULL); // 初始化libusb libusb_hotplug_callback_handle handle; int ret = libusb_hotplug_register_callback( NULL, // 默认上下文 LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT, LIBUSB_HOTPLUG_MATCH_ANY, LIBUSB_HOTPLUG_MATCH_ANY, // 任意厂商 LIBUSB_HOTPLUG_MATCH_ANY, // 任意产品 LIBUSB_HOTPLUG_MATCH_ANY, // 任意设备类 hotplug_callback, NULL, // 无自定义数据 &handle ); // 事件处理循环 while (1) { libusb_handle_events(NULL); } libusb_hotplug_deregister_callback(NULL, handle); // 注销回调 libusb_exit(NULL); return 0; } ``` --- ### 四、关键注意事项 1. **设备引用计数** 在回调函数中操作`libusb_device`时,必须通过`libusb_ref_device()`和`libusb_unref_device()`管理引用计数,避免设备被意外释放[^3]。 2. **线程安全** 回调函数可能在其他线程执行,需确保代码的线程安全性。 3. **平台限制** Linux内核需启用`CONFIG_USB_DEVICEFS`和`CONFIG_HOTPLUG`,Windows需要安装WinUSB驱动。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值