Linux驱动中remove和shutdown的区别

Linux驱动中的remove和shutdown回调在不同场景下触发:卸载驱动时调用remove,系统关机/重启时调用shutdown。remove用于设备绑定驱动的解除,而在系统关闭时,shutdown执行。在设备移除或驱动卸载过程中,涉及设备驱动的释放和sys文件系统下文件的删除。

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

驱动中remove和shutdown根本的区别在于调用的时机不同,我们以platform驱动为例,如下:

static struct platform_driver s3cmci_driver = {
	.driver	= {},
	.remove		= __devexit_p(s3cmci_remove),
	.shutdown	= s3cmci_shutdown,
};

static int __init s3cmci_init(void)
{
	return platform_driver_register(&s3cmci_driver);
}

int platform_driver_register(struct platform_driver *drv)
{
	if (drv->remove)
		drv->driver.remove = platform_drv_remove;
	if (drv->shutdown)
		drv->driver.shutdown = platform_drv_shutdown;

	return driver_register(&drv->driver);
}
14、15行,drv->remove是一个特定(具体)platform驱动的remove回调,比如这里的s3cmci_remove函数。drv->driver.remove是platform框架提供的,所有platform驱动通用。那么,一个特定驱动的remove函数如何得到调用?答案在platform_drv_remove函数中:

static int platform_drv_remove(struct device *_dev)
{
	struct platform_driver *drv = to_platform_driver(_dev->driver);
	struct platform_device *dev = to_platform_device(_dev);

	return drv->remove(dev);
}
platform_drv_remove相当于一个桥梁,实际干事的还是s3cmci_remove函数。16、17行的shutdown同理。至此,我们知道往后只需关注drv->driver.remove和drv->driver.shutdown就可以了。


remove/shutdown回调时机:

(1)卸载驱动的时候,remove调用

(2)设备移除的时候,与设备关联的驱动需要移除,remove调用

(3)系统关机/重启的时候,shutdown调用


驱动卸载:

void platform_driver_unregister(struct platform_driver *drv)
-->driver_unregister(&drv->driver)
---->bus_remove_driver(drv)
------>driver_detach(drv)
{
	struct device_private *dev_prv;
	struct device *dev;

	for (;;) {
		spin_lock(&drv->p->klist_devices.k_lock);
		if (list_empty(&drv->p->klist_devices.k_list)) {
			spin_unlock(&drv->p->klist_devices.k_lock);
			break;
		}
		dev_prv = list_entry(drv->p->klist_devices.k_list.prev,
				     struct device_private,
				     knode_driver.n_node);
		dev = dev_prv->device;
		get_device(dev);
		spin_unlock(&drv->p->klist_devices.k_lock);

		if (dev->parent)	/* Needed for USB */
			device_lock(dev->parent);
		device_lock(dev);
		if (dev->driver == drv)
			__device_release_driver(dev);
		device_unlock(dev);
		if (dev->parent)
			device_unlock(dev->parent);
		put_device(dev);
}
-------->void __device_release_driver(struct device *dev)
{
	struct device_driver *drv;

	drv = dev->driver;
	if (drv) {
		pm_runtime_get_noresume(dev);
		pm_runtime_barrier(dev);

		driver_sysfs_remove(dev);


		if (dev->bus && dev->bus->remove)
			dev->bus->remove(dev);
		else if (drv->remove)
			drv->remove(dev);
		devres_release_all(dev);
		dev->driver = NULL;
		klist_remove(&dev->p->knode_driver);

		pm_runtime_put_sync(dev);
	}
}

我们知道:一个设备只能绑定一个驱动,而同一个驱动可以适用于多个设备(不用想就知道为什么)。所以第9行开始的for死循环就是遍历挂在driver上的device链表,直到链表为空,遍历完成退出for循环;否则,如果device的驱动是当前要卸载的驱动(dev->driver == drv),就调用__device_release_driver进行进一步操作。

__device_release_driver函数中,pm_runtime_xxx之类的函数防止卸载过程中系统休眠,41行移除driver呈现在sys文件系统下的文件,44、45调用bus上的remove函数,而我们的主角-remove就在46、47行调用,进而执行s3cmci_remove函数

现在,device没有驱动了,所以设置device的driver成员为NULL:

dev->driver = NULL

设备移除:

void platform_device_del(struct platform_device *pdev)
-->device_del(&pdev->dev)
---->bus_remove_device(dev)
{
	if (dev->bus) {
		sysfs_remove_link(&dev->kobj, "subsystem");
		sysfs_remove_link(&dev->bus->p->devices_kset->kobj,
				  dev_name(dev));
		device_remove_attrs(dev->bus, dev);
		if (klist_node_attached(&dev->p->knode_bus))
			klist_del(&dev->p->knode_bus);

		pr_debug("bus: '%s': remove device %s\n",
			 dev->bus->name, dev_name(dev));
		device_release_driver(dev);
		bus_put(dev->bus);
	}
}
------>device_release_driver(dev)
{
	/*
	 * If anyone calls device_release_driver() recursively from
	 * within their ->remove callback for the same device, they
	 * will deadlock right here.
	 */
	device_lock(dev);
	__device_release_driver(dev);
	device_unlock(dev);
}
-------->__device_release_driver(dev)
第5行,翻译过来就是:移除bus上的设备。整个bus_remove_device函数也非常简单,删除sys文件系统与该设备相关的文件(链接)、删除设备的属性文件、完了通过bus_put减少device的引用计数(Decrement the refcount),当其引用计数为0的时候,才会彻底清楚该device及其占用的资源。


系统关机/重启流程:

void device_shutdown(void)
{
	struct device *dev;

	spin_lock(&devices_kset->list_lock);
	/*
	 * Walk the devices list backward, shutting down each in turn.
	 * Beware that device unplug events may also start pulling
	 * devices offline, even as the system is shutting down.
	 */
	while (!list_empty(&devices_kset->list)) {
		dev = list_entry(devices_kset->list.prev, struct device,
				kobj.entry);
		get_device(dev);
		/*
		 * Make sure the device is off the kset list, in the
		 * event that dev->*->shutdown() doesn't remove it.
		 */
		list_del_init(&dev->kobj.entry);
		spin_unlock(&devices_kset->list_lock);

		if (dev->bus && dev->bus->shutdown) {
			dev_dbg(dev, "shutdown\n");
			dev->bus->shutdown(dev);
		} else if (dev->driver && dev->driver->shutdown) {
			dev_dbg(dev, "shutdown\n");
			dev->driver->shutdown(dev);
		}
		put_device(dev);

		spin_lock(&devices_kset->list_lock);
	}
	spin_unlock(&devices_kset->list_lock);
}

void kernel_restart_prepare(char *cmd)
{
	blocking_notifier_call_chain(&reboot_notifier_list, SYS_RESTART, cmd);
	system_state = SYSTEM_RESTART;
	device_shutdown();
	sysdev_shutdown();
}

static void kernel_shutdown_prepare(enum system_states state)
{
	blocking_notifier_call_chain(&reboot_notifier_list,
		(state == SYSTEM_HALT)?SYS_HALT:SYS_POWER_OFF, NULL);
	system_state = state;
	device_shutdown();
}
这几个函数实现都简明易懂,再加上注释更加不在话下,25~28行调用shutdown函数s3cmci_shutdown。这地方简单是因为有之前的基础,所以说:提纲契领,举一反三。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值