linux reboot函数各分支对比分析

来源:互联网 发布:软件系统概述模板 编辑:程序博客网 时间:2024/06/15 01:08

在linux里面,关机、重启可以通过不同的命令实现,这些命令的具体了解,我推荐下面这篇博文:

        linux关机命令总结

说明:下面我贴的代码是基于linux 3.13.0版本内核的代码,和其它版本可能有一定程度的差异。

一、reboot

/* * Reboot system call: for obvious reasons only root may call it, * and even root needs to set up some magic numbers in the registers * so that some mistake won't make this reboot the whole machine. * You can also set the meaning of the ctrl-alt-del-key here. * * reboot doesn't sync: do that yourself before calling this. */SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd,void __user *, arg){struct pid_namespace *pid_ns = task_active_pid_ns(current);char buffer[256];int ret = 0;/* We only trust the superuser with rebooting the system. */if (!ns_capable(pid_ns->user_ns, CAP_SYS_BOOT))return -EPERM;/* For safety, we require "magic" arguments. */if (magic1 != LINUX_REBOOT_MAGIC1 ||(magic2 != LINUX_REBOOT_MAGIC2 &&magic2 != LINUX_REBOOT_MAGIC2A &&magic2 != LINUX_REBOOT_MAGIC2B &&magic2 != LINUX_REBOOT_MAGIC2C))return -EINVAL;/* * If pid namespaces are enabled and the current task is in a child * pid_namespace, the command is handled by reboot_pid_ns() which will * call do_exit(). */ret = reboot_pid_ns(pid_ns, cmd);if (ret)return ret;/* Instead of trying to make the power_off code look like * halt when pm_power_off is not set do it the easy way. */if ((cmd == LINUX_REBOOT_CMD_POWER_OFF) && !pm_power_off)cmd = LINUX_REBOOT_CMD_HALT;mutex_lock(&reboot_mutex);switch (cmd) {case LINUX_REBOOT_CMD_RESTART:kernel_restart(NULL);break;case LINUX_REBOOT_CMD_CAD_ON:C_A_D = 1;break;case LINUX_REBOOT_CMD_CAD_OFF:C_A_D = 0;break;case LINUX_REBOOT_CMD_HALT:kernel_halt();do_exit(0);panic("cannot halt");case LINUX_REBOOT_CMD_POWER_OFF:kernel_power_off();do_exit(0);break;case LINUX_REBOOT_CMD_RESTART2:ret = strncpy_from_user(&buffer[0], arg, sizeof(buffer) - 1);if (ret < 0) {ret = -EFAULT;break;}buffer[sizeof(buffer) - 1] = '\0';kernel_restart(buffer);break;#ifdef CONFIG_KEXECcase LINUX_REBOOT_CMD_KEXEC:ret = kernel_kexec();break;#endif#ifdef CONFIG_HIBERNATIONcase LINUX_REBOOT_CMD_SW_SUSPEND:ret = hibernate();break;#endifdefault:ret = -EINVAL;break;}mutex_unlock(&reboot_mutex);return ret;}
在第44行之前,是所有的命令都要执行的,而在本文,我们简要分析不同命令的区别;所以主要分析44行后switch的各分支。

根据switch的case分支可知,命令和函数的对应关系为:

函数指针函数restartkernel_restarthaltkernel_haltpoweroffkernel_power_offsuspendhibernatekexec没有具体实现,略过

下面我们先把个实现函数的代码贴上来,看一下流程上的差别。


二、各分支函数预览

2.1、kernel_restart

/** *kernel_restart - reboot the system *@cmd: pointer to buffer containing command to execute for restart *or %NULL * *Shutdown everything and perform a clean reboot. *This is not safe to call in interrupt context. */void kernel_restart(char *cmd){kernel_restart_prepare(cmd);migrate_to_reboot_cpu();syscore_shutdown();if (!cmd)pr_emerg("Restarting system\n");elsepr_emerg("Restarting system with command '%s'\n", cmd);kmsg_dump(KMSG_DUMP_RESTART);machine_restart(cmd);}
2.2、kernel_halt
/** * kernel_halt - halt the system * * Shutdown everything and perform a clean system halt. */void kernel_halt(void){kernel_shutdown_prepare(SYSTEM_HALT);migrate_to_reboot_cpu();syscore_shutdown();pr_emerg("System halted\n");kmsg_dump(KMSG_DUMP_HALT);machine_halt();}


2.3、kernel_power_off

/** * kernel_power_off - power_off the system * * Shutdown everything and perform a clean system power_off. */void kernel_power_off(void){kernel_shutdown_prepare(SYSTEM_POWER_OFF);if (pm_power_off_prepare)pm_power_off_prepare();migrate_to_reboot_cpu();syscore_shutdown();pr_emerg("Power down\n");kmsg_dump(KMSG_DUMP_POWEROFF);machine_power_off();}

2.4、hibernate

/** * hibernate - Carry out system hibernation, including saving the image. */int hibernate(void){int error;lock_system_sleep();/* The snapshot device should not be opened while we're running */if (!atomic_add_unless(&snapshot_device_available, -1, 0)) {error = -EBUSY;goto Unlock;}pm_prepare_console();error = pm_notifier_call_chain(PM_HIBERNATION_PREPARE);if (error)goto Exit;printk(KERN_INFO "PM: Syncing filesystems ... ");sys_sync();printk("done.\n");error = freeze_processes();if (error)goto Exit;lock_device_hotplug();/* Allocate memory management structures */error = create_basic_memory_bitmaps();if (error)goto Thaw;error = hibernation_snapshot(hibernation_mode == HIBERNATION_PLATFORM);if (error || freezer_test_done)goto Free_bitmaps;if (in_suspend) {unsigned int flags = 0;if (hibernation_mode == HIBERNATION_PLATFORM)flags |= SF_PLATFORM_MODE;if (nocompress)flags |= SF_NOCOMPRESS_MODE;else       flags |= SF_CRC32_MODE;pr_debug("PM: writing image.\n");error = swsusp_write(flags);swsusp_free();if (!error)power_down();in_suspend = 0;pm_restore_gfp_mask();} else {pr_debug("PM: Image restored successfully.\n");} Free_bitmaps:free_basic_memory_bitmaps(); Thaw:unlock_device_hotplug();thaw_processes();/* Don't bother checking whether freezer_test_done is true */freezer_test_done = false; Exit:pm_notifier_call_chain(PM_POST_HIBERNATION);pm_restore_console();atomic_inc(&snapshot_device_available); Unlock:unlock_system_sleep();return error;}


看完以上四个函数的代码,可以知道,前面三个函数在流程上差别不是很大,第四个函数hibernate的流程就完全不同。

下面我们以表格的方式来整理一下前三个函数的区别。

三、reboot中各函数流程对比

函数流程对比序号kernel_restartkernel_haltkernel_power_off1kernel_restart_preparekernel_shutdown_preparekernel_shutdown_prepare2migrate_to_reboot_cpumigrate_to_reboot_cpumigrate_to_reboot_cpu3syscore_shutdownsyscore_shutdownsyscore_shutdown4kmsg_dumpkmsg_dumpkmsg_dump5machine_restartmachine_haltmachine_power_off

上面的三个函数的第2、3、4个步骤都是一样的。

kernel_restart函数在linux关机时emmc驱动处理流程时主干代码基本分析了,所以在这里第2、3、4步骤就不再分析。

而kernel_halt和kernel_power_off的第一个步骤是一样的。

来看一下第一个步骤中kernel_restart_prepare函数和kernel_shutdown_prepare函数的源码实现。

四、prepare函数对比

4.1、kernel_restart_prepare
通过第一节中的reboot函数可以知道,reboot传递给kernel_restart的形参cmd的值为NULL;
所以kernel_restart传递给kernel_restart_prepare的参数值也为NULL,下面看一下kernel_restart_prepare的实现:

void kernel_restart_prepare(char *cmd){blocking_notifier_call_chain(&reboot_notifier_list, SYS_RESTART, cmd);system_state = SYSTEM_RESTART;usermodehelper_disable();device_shutdown();}
这个函数我们在linux关机时emmc驱动处理流程这篇文章的第二节有分析过,这里就不详细分析了,需要注意的是,这里通知链传递的通知消息为SYS_RESTART。

4.2、kernel_shutdown_prepare

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;usermodehelper_disable();device_shutdown();}

这个函数和4.1的kernel_restart_prepare函数整体差不多,唯一不同的就是传递给通知链的第二个参数;

当调用kernel_halt传递的参数为SYS_HALT,否则传递SYS_POWER_OFF。

五、restart、halt、power_off

machine_restart、machine_halt、machine_power_off函数的实现差异比较大,我暂时还没弄懂,这里有一篇文章,做了相关介绍,推荐看:
Linux电源管理(3)_Generic PM之Reboot过程


回到第一节,有一个hibernate函数,我们还没有分析;
这个函数的代码在2.4节已经贴出来了,涉及的代码比较多,我还没完全理解,推荐两个链接:

Linux进程冻结技术

android 休眠唤醒机制分析(三) — suspend

六、休眠对于emmc的影响

在android 休眠唤醒机制分析(三) — suspend一文中,第3节的device_suspend函数如下(我的源代码是3.13.0):

/** * device_suspend - Execute "suspend" callbacks for given device. * @dev: Device to handle. * @state: PM transition of the system being carried out. * @async: If true, the device is being suspended asynchronously. */static int __device_suspend(struct device *dev, pm_message_t state, bool async){pm_callback_t callback = NULL;char *info = NULL;int error = 0;DECLARE_DPM_WATCHDOG_ON_STACK(wd);dpm_wait_for_children(dev, async);if (async_error)goto Complete;/* * If a device configured to wake up the system from sleep states * has been suspended at run time and there's a resume request pending * for it, this is equivalent to the device signaling wakeup, so the * system suspend operation should be aborted. */if (pm_runtime_barrier(dev) && device_may_wakeup(dev))pm_wakeup_event(dev, 0);if (pm_wakeup_pending()) {async_error = -EBUSY;goto Complete;}if (dev->power.syscore)goto Complete;dpm_watchdog_set(&wd, dev);device_lock(dev);if (dev->pm_domain) {info = "power domain ";callback = pm_op(&dev->pm_domain->ops, state);goto Run;}if (dev->type && dev->type->pm) {info = "type ";callback = pm_op(dev->type->pm, state);goto Run;}if (dev->class) {if (dev->class->pm) {info = "class ";callback = pm_op(dev->class->pm, state);goto Run;} else if (dev->class->suspend) {pm_dev_dbg(dev, state, "legacy class ");error = legacy_suspend(dev, state, dev->class->suspend,"legacy class ");goto End;}}if (dev->bus) {if (dev->bus->pm) {info = "bus ";callback = pm_op(dev->bus->pm, state);} else if (dev->bus->suspend) {pm_dev_dbg(dev, state, "legacy bus ");error = legacy_suspend(dev, state, dev->bus->suspend,"legacy bus ");goto End;}} Run:if (!callback && dev->driver && dev->driver->pm) {info = "driver ";callback = pm_op(dev->driver->pm, state);}error = dpm_run_callback(callback, dev, state, info); End:if (!error) {dev->power.is_suspended = true;if (dev->power.wakeup_path    && dev->parent && !dev->parent->power.ignore_children)dev->parent->power.wakeup_path = true;}device_unlock(dev);dpm_watchdog_clear(&wd); Complete:complete_all(&dev->power.completion);if (error)async_error = error;return error;}

通过追溯源代码,我们可以知道,当系统在深度休眠时传递给此函数的state参数为:PM_EVENT_SUSPEND。

第51行:如果设备注册了pm函数集指针就执行设备的函数集指针,否则执行第64行的函数集指针;从我们的emmc驱动来看,它没有注册设备的函数集指针,但是有注册总线的pm函数集指针,在bus.c文件中有如下代码:

static struct bus_type mmc_bus_type = {.name= "mmc",.dev_groups= mmc_dev_groups,.match= mmc_bus_match,.uevent= mmc_bus_uevent,.probe= mmc_bus_probe,.remove= mmc_bus_remove,.shutdown= mmc_bus_shutdown,.pm= &mmc_bus_pm_ops,};

而mmc_bus_pm_ops的实现如下:

static const struct dev_pm_ops mmc_bus_pm_ops = {SET_RUNTIME_PM_OPS(mmc_runtime_suspend, mmc_runtime_resume,mmc_runtime_idle)SET_SYSTEM_SLEEP_PM_OPS(mmc_bus_suspend, mmc_bus_resume)};

SET_RUNTIME_PM_OPS宏的实现如下:

#ifdef CONFIG_PM_RUNTIME#define SET_RUNTIME_PM_OPS(suspend_fn, resume_fn, idle_fn) \.runtime_suspend = suspend_fn, \.runtime_resume = resume_fn, \.runtime_idle = idle_fn,#else#define SET_RUNTIME_PM_OPS(suspend_fn, resume_fn, idle_fn)#endif
SET_SYSTEM_SLEEP_PM_OPS的实现如下:

#ifdef CONFIG_PM_SLEEP#define SET_SYSTEM_SLEEP_PM_OPS(suspend_fn, resume_fn) \.suspend = suspend_fn, \.resume = resume_fn, \.freeze = suspend_fn, \.thaw = resume_fn, \.poweroff = suspend_fn, \.restore = resume_fn,#else#define SET_SYSTEM_SLEEP_PM_OPS(suspend_fn, resume_fn)#endif

__device_suspend函数的第67行:callback = pm_op(dev->class->pm, state);代码展开:

/** * pm_op - Return the PM operation appropriate for given PM event. * @ops: PM operations to choose from. * @state: PM transition of the system being carried out. */static pm_callback_t pm_op(const struct dev_pm_ops *ops, pm_message_t state){switch (state.event) {#ifdef CONFIG_SUSPENDcase PM_EVENT_SUSPEND:return ops->suspend;case PM_EVENT_RESUME:return ops->resume;#endif /* CONFIG_SUSPEND */#ifdef CONFIG_HIBERNATE_CALLBACKScase PM_EVENT_FREEZE:case PM_EVENT_QUIESCE:return ops->freeze;case PM_EVENT_HIBERNATE:return ops->poweroff;case PM_EVENT_THAW:case PM_EVENT_RECOVER:return ops->thaw;break;case PM_EVENT_RESTORE:return ops->restore;#endif /* CONFIG_HIBERNATE_CALLBACKS */}return NULL;}
而在本节的前面,我们知道__device_suspend的参数state值为PM_EVENT_SUSPEND。
所以,这里执行的就是emmc驱动模块的bus.c中的mmc_bus_suspend函数:
static int mmc_bus_suspend(struct device *dev){struct mmc_driver *drv = to_mmc_driver(dev->driver);struct mmc_card *card = mmc_dev_to_card(dev);struct mmc_host *host = card->host;int ret;if (dev->driver && drv->suspend) {ret = drv->suspend(card);if (ret)return ret;}ret = host->bus_ops->suspend(host);return ret;}
而该函数最终调用的是驱动的suspend和总线ops的suspend;


在emmc驱动的block.c文件中,驱动的suspend函数指针指向mmc_blk_suspend函数,实现如下:

static int mmc_blk_suspend(struct mmc_card *card){return _mmc_blk_suspend(card);}
而_mmc_blk_suspend在linux关机时emmc驱动处理流程的第七节我们详细讲解过,这里就不再重复。


在emmc驱动模块的bus.c文件中,我们知道总线ops的suspend函数,是指向mmc_suspend函数,实现如下:

static int mmc_suspend(struct mmc_host *host){int err;err = _mmc_suspend(host, true);if (!err) {pm_runtime_disable(&host->card->dev);pm_runtime_set_suspended(&host->card->dev);}return err;}
由此,我们可以知道,其实它是依赖于_mmc_suspend函数的,而_mmc_suspend函数我们在linux关机时emmc驱动处理流程的第十一节中详细描述过,这里也不再描述。

本节总结:系统休眠对emmc的影响(从inux关机时emmc驱动处理流程拷过来的):

1、将消息对列挂载起来,暂时不执行,并没有发送什么命令

2、如果设备是挂载状态,则直接返回,否则执行下面的流程:
3、如果设备当前正在执行块请求操作,则发送停止块请求操作(停止编程操作),会涉及到以下命令:
MMC_SLEEP_AWAKE 5 /* ac [31:16] RCA 15:flg R1b */
MMC_SWITCH 6 /* ac [31:0] See below R1b */
MMC_SELECT_CARD 7 /* ac [31:16] RCA 
#define MMC_STOP_TRANSMISSION 12/* ac R1b */

#define MMC_SEND_STATUS 13 /* ac [31:16] RCA R1 */

4、将缓存数据写到卡上,发送MMC_SWITCH 命令。

5、根据emmc的当前状态,可能会发送CMD6设置host状态为poweroff状态,或者是发送CMD1命令进入休眠,或者是发送CMD7命令设置host与card的断开;

6、通过设置设备的ocr寄存器设置设备断电。

7、设置设备为挂载状态。








0 0
原创粉丝点击