日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > Android >内容正文

Android

android 关机 流程_Android系统关机的全流程解析

發(fā)布時(shí)間:2025/4/5 Android 61 豆豆
生活随笔 收集整理的這篇文章主要介紹了 android 关机 流程_Android系统关机的全流程解析 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

在PowerManager的API文檔中,給出了一個(gè)關(guān)機(jī)/重啟接口:

public void reboot (String reason)

對(duì)于這個(gè)接口的描述很簡(jiǎn)單,就是幾句話。

接口的作用就是重啟設(shè)備,而且,就算重啟成功了也沒有返回值。

需要包含REBOOT權(quán)限,也就是android.permission.REBOOT

唯一參數(shù)reason代表需要的特定重啟模式,比如recovery,當(dāng)然也可以為null。

一、上層空間1.frameworks/base/core/java/android/os/PowerManager.java

/**

* Reboot the device. Will not return if the reboot is

* successful. Requires the {@link android.Manifest.permission#REBOOT}

* permission.

*

* @param reason code to pass to the kernel (e.g., "recovery") to

* request special boot modes, or null.

*/

public void reboot(String reason)

{

try {

mService.reboot(reason);

} catch (RemoteException e) {

}

}

mService為IPowerManager Binder接口服務(wù)。

/**

* {@hide}

*/

public PowerManager(IPowerManager service, Handler handler)

{

mService = service;

mHandler = handler;

}

2.frameworks/base/core/java/android/os/IPowerManager.aidl

interface IPowerManager

{

...

void reboot(String reason);

...

}

3.frameworks/base/services/java/com/android/server/PowerManagerService.java

/**

* Reboot the device immediately, passing 'reason' (may be null)

* to the underlying __reboot system call. Should not return.

*/

public void reboot(String reason)

{

mContext.enforceCallingOrSelfPermission(android.Manifest.permission.REBOOT, null);

if (mHandler == null || !ActivityManagerNative.isSystemReady()) {

throw new IllegalStateException("Too early to call reboot()");

}

final String finalReason = reason;

Runnable runnable = new Runnable() {

public void run() {

synchronized (this) {

ShutdownThread.reboot(getUiContext(), finalReason, false);

}

}

};

// ShutdownThread must run on a looper capable of displaying the UI.

mHandler.post(runnable);

// PowerManager.reboot() is documented not to return so just wait for the inevitable.

synchronized (runnable) {

while (true) {

try {

runnable.wait();

} catch (InterruptedException e) {

}

}

}

}

4.frameworks/base/services/java/com/android/server/pm/ShutdownThread.java

/**

* Request a clean shutdown, waiting for subsystems to clean up their

* state etc. Must be called from a Looper thread in which its UI

* is shown.

*

* @param context Context used to display the shutdown progress dialog.

* @param reason code to pass to the kernel (e.g. "recovery"), or null.

* @param confirm true if user confirmation is needed before shutting down.

*/

public static void reboot(final Context context, String reason, boolean confirm) {

mReboot = true;

mRebootSafeMode = false;

mRebootReason = reason;

shutdownInner(context, confirm);

}

這里說明是需要重啟,且不是安全模式,重啟參數(shù)為傳遞下來的reason,shutdownInner的confirm參數(shù)是用來設(shè)置是否有確認(rèn)提示框的,通過reboot接口調(diào)用重啟是沒有的,為false。

重啟的實(shí)現(xiàn)在run()中,因?yàn)镾hutdownThread是Thread的擴(kuò)展,所以run會(huì)自動(dòng)運(yùn)行。

/**

* Makes sure we handle the shutdown gracefully.

* Shuts off power regardless of radio and bluetooth state if the alloted time has passed.

*/

public void run() {

BroadcastReceiver br = new BroadcastReceiver() {

@Override public void onReceive(Context context, Intent intent) {

// We don't allow apps to cancel this, so ignore the result.

actionDone();

}

};

/*

* Write a system property in case the system_server reboots before we

* get to the actual hardware restart. If that happens, we'll retry at

* the beginning of the SystemServer startup.

*/

{

String reason = (mReboot ? "1" : "0") + (mRebootReason != null ? mRebootReason : "");

SystemProperties.set(SHUTDOWN_ACTION_PROPERTY, reason);

}

/*

* If we are rebooting into safe mode, write a system property

* indicating so.

*/

if (mRebootSafeMode) {

SystemProperties.set(REBOOT_SAFEMODE_PROPERTY, "1");

}

...

rebootOrShutdown(mReboot, mRebootReason);

}

在重啟前會(huì)將重啟原因?qū)懭雜ys.shutdown.requested,如果沒有則為空,如果是安全模式還會(huì)將persist.sys.safemode置1,之后會(huì)進(jìn)行一些關(guān)機(jī)前的預(yù)處理,關(guān)閉ActivityManager以及MountService,最終調(diào)用rebootOrShutdown進(jìn)行關(guān)機(jī)操作。

/**

* Do not call this directly. Use {@link #reboot(Context, String, boolean)}

* or {@link #shutdown(Context, boolean)} instead.

*

* @param reboot true to reboot or false to shutdown

* @param reason reason for reboot

*/

public static void rebootOrShutdown(boolean reboot, String reason) {

if (reboot) {

Log.i(TAG, "Rebooting, reason: " + reason);

try {

PowerManagerService.lowLevelReboot(reason);

} catch (Exception e) {

Log.e(TAG, "Reboot failed, will attempt shutdown instead", e);

}

} else if (SHUTDOWN_VIBRATE_MS > 0) {

// vibrate before shutting down

Vibrator vibrator = new SystemVibrator();

try {

vibrator.vibrate(SHUTDOWN_VIBRATE_MS);

} catch (Exception e) {

// Failure to vibrate shouldn't interrupt shutdown. Just log it.

Log.w(TAG, "Failed to vibrate during shutdown.", e);

}

// vibrator is asynchronous so we need to wait to avoid shutting down too soon.

try {

Thread.sleep(SHUTDOWN_VIBRATE_MS);

} catch (InterruptedException unused) {

}

}

// Shutdown power

Log.i(TAG, "Performing low-level shutdown...");

PowerManagerService.lowLevelShutdown();

}

}

如果確認(rèn)重啟,則調(diào)用PowerManagerService的lowLevelReboot函數(shù),參數(shù)就是傳遞下來的reason,稍后分析。如果不是重啟,即mReboot=false,那就是需要關(guān)機(jī)了,在shutdown函數(shù)中就能夠知道。

/**

* Request a clean shutdown, waiting for subsystems to clean up their

* state etc. Must be called from a Looper thread in which its UI

* is shown.

*

* @param context Context used to display the shutdown progress dialog.

* @param confirm true if user confirmation is needed before shutting down.

*/

public static void shutdown(final Context context, boolean confirm) {

mReboot = false;

mRebootSafeMode = false;

shutdownInner(context, confirm);

}

關(guān)機(jī)的時(shí)候需要震動(dòng),就是這里了SHUTDOWN_VIBRATE_MS,默認(rèn)的定義是500ms。但是在代碼上看,無論如何,最后都會(huì)調(diào)用一下lowLevelShutdown函數(shù),也就是關(guān)機(jī)。邏輯上,這里可能是個(gè)問題,但是實(shí)際中,如果重啟操作能夠調(diào)用成功的話,整個(gè)系統(tǒng)都重啟了,后邊的代碼當(dāng)然不可能執(zhí)行到了。

目光轉(zhuǎn)回PowerManagerService

4.frameworks/base/services/java/com/android/server/PowerManagerService.java

/**

* Low-level function to reboot the device.

*

* @param reason code to pass to the kernel (e.g. "recovery"), or null.

* @throws IOException if reboot fails for some reason (eg, lack of

* permission)

*/

public static void lowLevelReboot(String reason) throws IOException {

nativeReboot(reason);

}

/**

* Low-level function turn the device off immediately, without trying

* to be clean. Most people should use

* {@link com.android.server.pm.internal.app.ShutdownThread} for a clean shutdown.

*/

public static void lowLevelShutdown() {

nativeShutdown();

}

很熟悉的字樣native,是JNI調(diào)用了:

private static native void nativeShutdown();

private static native void nativeReboot(String reason) throws IOException;

5.frameworks/base/services/jni/com_android_server_PowerManagerService.cpp

static JNINativeMethod gPowerManagerServiceMethods[] = {

/* name, signature, funcPtr */

...

{ "nativeShutdown", "()V",

(void*) nativeShutdown },

{ "nativeReboot", "(Ljava/lang/String;)V",

(void*) nativeReboot },

...

};

這兩個(gè)好哥倆的實(shí)現(xiàn)也是在一起的:

static void nativeShutdown(JNIEnv *env, jobject clazz) {

android_reboot(ANDROID_RB_POWEROFF, 0, 0);

}

static void nativeReboot(JNIEnv *env, jobject clazz, jstring reason) {

if (reason == NULL) {

android_reboot(ANDROID_RB_RESTART, 0, 0);

} else {

const char *chars = env->GetStringUTFChars(reason, NULL);

android_reboot(ANDROID_RB_RESTART2, 0, (char *) chars);

env->ReleaseStringUTFChars(reason, chars); // In case it fails.

}

jniThrowIOException(env, errno);

}

可以看到無論是關(guān)機(jī)還是重啟,都是調(diào)用android_reboot來實(shí)現(xiàn)的,只是參數(shù)不一樣而已。

6.system/core/libcutils/android_reboot.c

int android_reboot(int cmd, int flags, char *arg)

{

int ret = 0;

int reason = -1;

#ifdef RECOVERY_PRE_COMMAND

if (cmd == (int) ANDROID_RB_RESTART2) {

if (arg && strlen(arg) > 0) {

char cmd[PATH_MAX];

sprintf(cmd, RECOVERY_PRE_COMMAND " %s", arg);

system(cmd);

}

}

#endif

if (!(flags & ANDROID_RB_FLAG_NO_SYNC))

sync();

if (!(flags & ANDROID_RB_FLAG_NO_REMOUNT_RO))

remount_ro();

switch (cmd) {

case ANDROID_RB_RESTART:

reason = RB_AUTOBOOT;

break;

case ANDROID_RB_POWEROFF:

ret = reboot(RB_POWER_OFF);

return ret;

case ANDROID_RB_RESTART2:

// REBOOT_MAGIC

break;

default:

return -1;

}

#ifdef RECOVERY_PRE_COMMAND_CLEAR_REASON

reason = RB_AUTOBOOT;

#endif

if (reason != -1)

ret = reboot(reason);

else

ret = __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,

LINUX_REBOOT_CMD_RESTART2, arg);

return ret;

}

以reboot recovery為例,arg即為recovery,所在在第五步的時(shí)候會(huì)傳入ANDROID_RB_RESTART2。到了android_reboot函數(shù)中,會(huì)看到這樣的定義#ifdef RECOVERY_PRE_COMMAND,即屬于重啟前會(huì)執(zhí)行的命令,如果定義了就會(huì)執(zhí)行。

下面也是做了一些關(guān)機(jī)重啟前的預(yù)處理工作,sync()作用是將緩存中的信息寫入磁盤,以免程序異常結(jié)束導(dǎo)致文件被損壞,linux系統(tǒng)關(guān)機(jī)前會(huì)做幾次這樣的動(dòng)作;而remount_ro()作用是通過調(diào)用emergency_remount()強(qiáng)制將文件系統(tǒng)掛載為只讀,不再允許任何寫入操作,同時(shí)會(huì)通過檢查/proc/mounts的設(shè)備狀態(tài)來確認(rèn)是否當(dāng)前的所有寫入工作已經(jīng)完成,這個(gè)檢查過程是阻塞操作。

接下來才是對(duì)參數(shù)的解析處理:

1)普通重啟 ANDROID_RB_RESTART, reason = RB_AUTOBOOT;

2)關(guān)機(jī) ANDROID_RB_POWEROFF, 無需reason,直接調(diào)用reboot進(jìn)行關(guān)機(jī);

3)帶參數(shù)的特殊重啟 ANDROID_RB_RESTART2, reason 將為默認(rèn)值 -1

這里又出現(xiàn)一個(gè)#ifdef RECOVERY_PRE_COMMAND_CLEAR_REASON,如果定義了它,則無論上層傳下來的參數(shù)是什么樣的,最終都只是普通重啟而已。定義它的方式是在BoardConfig.mk中加入TARGET_RECOVERY_PRE_COMMAND_CLEAR_REASON := true,應(yīng)該有廠商會(huì)喜歡這么做的,畢竟除了普通重啟,都可能帶給用戶一定的風(fēng)險(xiǎn)。

最后會(huì)對(duì)reason進(jìn)行一個(gè)檢測(cè),那么通過上邊的分析,其實(shí)只有帶參數(shù)的特殊重啟才會(huì)為-1,而不等于-1的情況中有普通重啟和關(guān)機(jī),而關(guān)機(jī)已經(jīng)自行解決了……所以,不等于-1的情況到了這里也只有普通重啟了。最終這里就是區(qū)分普通重啟與特殊重啟的地方了。這里再插入一個(gè)問題,其他的幾個(gè)cmd都是什么值呢?答案在bionic/libc/include/sys/reboot.h中:

#define RB_AUTOBOOT LINUX_REBOOT_CMD_RESTART

#define RB_HALT_SYSTEM LINUX_REBOOT_CMD_HALT

#define RB_ENABLE_CAD LINUX_REBOOT_CMD_CAD_ON

#define RB_DISABLE_CAD LINUX_REBOOT_CMD_CAD_OFF

#define RB_POWER_OFF LINUX_REBOOT_CMD_POWER_OFF

而,LINUX_REBOOT_XXXX之類的在bionic/libc/kernel/common/linux/reboot.h中:

#define LINUX_REBOOT_MAGIC1 0xfee1dead

#define LINUX_REBOOT_MAGIC2 672274793

/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */

#define LINUX_REBOOT_MAGIC2A 85072278

#define LINUX_REBOOT_MAGIC2B 369367448

#define LINUX_REBOOT_MAGIC2C 537993216

#define LINUX_REBOOT_CMD_RESTART 0x01234567

/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */

#define LINUX_REBOOT_CMD_HALT 0xCDEF0123

#define LINUX_REBOOT_CMD_CAD_ON 0x89ABCDEF

#define LINUX_REBOOT_CMD_CAD_OFF 0x00000000

#define LINUX_REBOOT_CMD_POWER_OFF 0x4321FEDC

/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */

#define LINUX_REBOOT_CMD_RESTART2 0xA1B2C3D4

#define LINUX_REBOOT_CMD_SW_SUSPEND 0xD000FCE2

#define LINUX_REBOOT_CMD_KEXEC 0x45584543

至于為什么他們是這樣奇怪的值這個(gè)問題,我只能說他們是magic number,魔法嘛,本來就是正常人不能夠理解的,所以~~~放過他們吧,只要知道他們沒有是-1的就OK啦。

先來看reboot函數(shù),按照往常的經(jīng)驗(yàn),reboot最終一定會(huì)調(diào)用到__reboot的。

7.bionic/libc/unistd/reboot.c

int reboot (int mode)

{

return __reboot( LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, mode, NULL );

}

Bingo!果然是這樣,如此說來reboot(reason) -> reboot(RB_AUTOBOOT) -> __reboot( LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_RESTART, NULL ),要是直接這樣寫多好~~~免得繞這一層了。

二、KERNEL域8.__reboot通過syscall來到內(nèi)核

這里用一些篇幅簡(jiǎn)要介紹syscall,以后遇到類似的東西更好追蹤一些。

第七步中的__reboot在arm架構(gòu)的實(shí)現(xiàn)是這樣的(bionic/libc/arch-arm/syscalls/__reboot.S)

ENTRY(__reboot)

.save {r4, r7}

stmfd sp!, {r4, r7}

ldr r7, =__NR_reboot

swi #0

ldmfd sp!, {r4, r7}

movs r0, r0

bxpl lr

b __set_syscall_errno

END(__reboot)

可以看出來,這里將__reboot的實(shí)現(xiàn)映射到了__NR_reboot, 而在bionic/libc/sys/linux-syscalls.h能夠找到:

#define __NR_reboot (__NR_SYSCALL_BASE + 88)

其被指定了一個(gè)固定的偏移量,在被調(diào)用的時(shí)候就是通過這個(gè)偏移量去內(nèi)核中尋找對(duì)應(yīng)的入口的,由此可見,內(nèi)核中一定有著相同的定義,否則將不能成功調(diào)用。內(nèi)核中對(duì)syscall偏移量的定義在內(nèi)核源碼中的arch/arm/include/asm/unistd.h,相關(guān)信息完全一致。

已經(jīng)找到了內(nèi)核中的對(duì)應(yīng)映射,那么下一步就要去找尋真正的實(shí)現(xiàn)函數(shù)了,在include/asm-generic/unistd.h中可以找到內(nèi)核對(duì)__NR_reboot的syscall函數(shù)映射,即

/* kernel/sys.c */

#define __NR_setpriority 140

__SYSCALL(__NR_setpriority, sys_setpriority)

#define __NR_getpriority 141

__SYSCALL(__NR_getpriority, sys_getpriority)

#define __NR_reboot 142

__SYSCALL(__NR_reboot, sys_reboot)

同時(shí),能夠發(fā)現(xiàn)如此溫馨的一幕,內(nèi)核已經(jīng)指引我們下一步該去哪里尋找sys_reboot,即kernel/sys.c。

9.kernel/sys.c

在進(jìn)入這個(gè)文件前,我們先去include/linux/syscalls.h中查看一下sys_reboot的定義:

asmlinkage long sys_reboot(int magic1, int magic2, unsigned int cmd,

void __user *arg);

與__reboot的調(diào)用參數(shù)一致。

進(jìn)入sys.c文件后,并沒有找到名為sys_reboot的函數(shù),而通過仔細(xì)查找,發(fā)現(xiàn)一個(gè)很有趣的函數(shù),其定義為SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd, void __user *, arg),對(duì)比__reboot的參數(shù),能夠符合。究竟是不是這個(gè)函數(shù)?

同樣在include/linux/syscalls.h文件中,能夠找到這樣幾個(gè)定義:

#define SYSCALL_DEFINE1(name, ...) SYSCALL_DEFINEx(1, _##name, __VA_ARGS__)

#define SYSCALL_DEFINE2(name, ...) SYSCALL_DEFINEx(2, _##name, __VA_ARGS__)

#define SYSCALL_DEFINE3(name, ...) SYSCALL_DEFINEx(3, _##name, __VA_ARGS__)

#define SYSCALL_DEFINE4(name, ...) SYSCALL_DEFINEx(4, _##name, __VA_ARGS__)

#define SYSCALL_DEFINE5(name, ...) SYSCALL_DEFINEx(5, _##name, __VA_ARGS__)

#define SYSCALL_DEFINE6(name, ...) SYSCALL_DEFINEx(6, _##name, __VA_ARGS__)

...

#define SYSCALL_DEFINEx(x, sname, ...) \

__SYSCALL_DEFINEx(x, sname, __VA_ARGS__)

...

#define __SYSCALL_DEFINEx(x, name, ...) \

asmlinkage long sys##name(__SC_DECL##x(__VA_ARGS__))

整合后等價(jià)于:

#define SYSCALL_DEFINE4(name, ...) \

asmlinkage long sys##_name(__SC_DECL##4(__VA_ARGS__))

這樣就不難看出,SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd, void __user *, arg)就是sys_reboot,也就是上層調(diào)用的__reboot的最終實(shí)現(xiàn)。函數(shù)實(shí)現(xiàn)如下:

/*

* 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)

{

char buffer[256];

int ret = 0;

/* We only trust the superuser with rebooting the system. */

if (!capable(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;

/* 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:

if (strncpy_from_user(&buffer[0], arg, sizeof(buffer) - 1) < 0) {

ret = -EFAULT;

break;

}

buffer[sizeof(buffer) - 1] = '\0';

kernel_restart(buffer);

break;

#ifdef CONFIG_KEXEC

case LINUX_REBOOT_CMD_KEXEC:

ret = kernel_kexec();

break;

#endif

#ifdef CONFIG_HIBERNATION

case LINUX_REBOOT_CMD_SW_SUSPEND:

ret = hibernate();

break;

#endif

default:

ret = -EINVAL;

break;

}

mutex_unlock(&reboot_mutex);

return ret;

}

在此函數(shù)中,首先會(huì)檢測(cè)權(quán)限問題,只有超級(jí)用戶才可以執(zhí)行重啟系統(tǒng)的操作:

/* We only trust the superuser with rebooting the system. */

if (!capable(CAP_SYS_BOOT))

return -EPERM;

否則將返回權(quán)限錯(cuò)誤。對(duì)應(yīng)的權(quán)限列表在include/linux/capability.h中,重啟操作為22.

隨后對(duì)magic number進(jìn)行了校驗(yàn):

/* 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;

如果數(shù)據(jù)傳輸過程中沒有發(fā)生錯(cuò)誤的話,這里也當(dāng)然不會(huì)有問題,所以只是一個(gè)安全性校驗(yàn),基本不會(huì)發(fā)生錯(cuò)誤。

之后有一個(gè)很有趣的檢查,如果用戶要求關(guān)機(jī),而pm_power_off為空的話,就把用戶的關(guān)機(jī)命令轉(zhuǎn)換為掛起:

/* 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;

在arch/arm/kernel/process.c中可以找到它的定義:

/*

* Function pointers to optional machine specific functions

*/

void (*pm_power_off)(void);

EXPORT_SYMBOL(pm_power_off);

好的,只是一個(gè)函數(shù)指針,而且做了全局操作,整個(gè)kernel都可以調(diào)用它。以高通msm7x30為例,在arch/arm/mach-msm/pm2.c中對(duì)這個(gè)函數(shù)指針進(jìn)行了賦值:

pm_power_off = msm_pm_power_off;

msm_pm_power_off的具體實(shí)現(xiàn)就不再跟蹤了,各家的都不一樣,跟下去沒有太大意義。現(xiàn)在只要知道,我分析的這個(gè)kernel是給了這個(gè)函數(shù)指針賦值的,所以不為空,關(guān)機(jī)命令將正常執(zhí)行。

接下來就是這個(gè)函數(shù)的正題了,對(duì)用戶命令進(jìn)行解析操作,同時(shí)這個(gè)過程是用reboot_mutex互斥鎖來進(jìn)行保護(hù)的,以保證同一時(shí)間只可能有一個(gè)解析過程,避免沖突。

下邊貼出所有關(guān)機(jī)重啟相關(guān)的命令定義:

/*

* Commands accepted by the _reboot() system call.

*

* RESTART Restart system using default command and mode.

* HALT Stop OS and give system control to ROM monitor, if any.

* CAD_ON Ctrl-Alt-Del sequence causes RESTART command.

* CAD_OFF Ctrl-Alt-Del sequence sends SIGINT to init task.

* POWER_OFF Stop OS and remove all power from system, if possible.

* RESTART2 Restart system using given command string.

* SW_SUSPEND Suspend system using software suspend if compiled in.

* KEXEC Restart system using a previously loaded Linux kernel

*/

#define LINUX_REBOOT_CMD_RESTART 0x01234567

#define LINUX_REBOOT_CMD_HALT 0xCDEF0123

#define LINUX_REBOOT_CMD_CAD_ON 0x89ABCDEF

#define LINUX_REBOOT_CMD_CAD_OFF 0x00000000

#define LINUX_REBOOT_CMD_POWER_OFF 0x4321FEDC

#define LINUX_REBOOT_CMD_RESTART2 0xA1B2C3D4

#define LINUX_REBOOT_CMD_SW_SUSPEND 0xD000FCE2

#define LINUX_REBOOT_CMD_KEXEC 0x45584543

注釋中的說明很詳細(xì)了,比較陌生的就是關(guān)于CAD,其實(shí)就是用來想用Ctrl+Alt+Del操作的;然后SW_SYSPEND是軟件休眠;KEXEC就太高端了,屬于內(nèi)核的一個(gè)補(bǔ)丁,用來利用老內(nèi)核重啟,詳細(xì)資料:http://www.ibm.com/developerworks/cn/linux/l-kexec/?ca=dwcn-newsletter-linux

以上這些只有前六個(gè)命令被Android系統(tǒng)所使用,為什么這么說,可以去看bionic/libc/include/sys/reboot.h,上邊已經(jīng)貼出了。LINUX_REBOOT_CMD_HALT雖有定義,但是也沒有發(fā)現(xiàn)Android系統(tǒng)中哪里有調(diào)用,有高手找到的話,希望能夠告知一下。最終的最終,能夠用到的就只有三個(gè):

RESTART

POWER_OFF

RESTART2

10.最終實(shí)現(xiàn)

重啟調(diào)用的是kernel_restart,區(qū)別是參數(shù)是不是空,關(guān)機(jī)則調(diào)用kernel_power_off(),先看關(guān)機(jī):

/**

* 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();

disable_nonboot_cpus();

syscore_shutdown();

printk(KERN_EMERG "Power down.\n");

kmsg_dump(KMSG_DUMP_POWEROFF);

machine_power_off();

}

EXPORT_SYMBOL_GPL(kernel_power_off);

最了一系列準(zhǔn)備工作,最終調(diào)用machine_power_off():

void machine_power_off(void)

{

machine_shutdown();

if (pm_power_off)

pm_power_off();

}

之前找尋的pm_power_off在這里就有用處了,是關(guān)機(jī)的最后一步操作。關(guān)機(jī)完成,之后看下重啟操作:

/**

* 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);

if (!cmd)

printk(KERN_EMERG "Restarting system.\n");

else

printk(KERN_EMERG "Restarting system with command '%s'.\n", cmd);

kmsg_dump(KMSG_DUMP_RESTART);

machine_restart(cmd);

}

EXPORT_SYMBOL_GPL(kernel_restart);

同樣的套路,也是會(huì)進(jìn)行一些準(zhǔn)備工作,之后調(diào)用machine_restart(cmd), 如果是普通重啟,那么中個(gè)cmd就為NULL,如果是特殊重啟,那么這個(gè)cmd就是一層一層傳遞下來得那個(gè)arg了。

void machine_restart(char *cmd)

{

machine_shutdown();

arm_pm_restart(reboot_mode, cmd);

}

...

void (*arm_pm_restart)(char str, const char *cmd) = arm_machine_restart;

EXPORT_SYMBOL_GPL(arm_pm_restart);

而還記得剛才的pm2.c嗎?在那里同樣對(duì)arm_pm_restart進(jìn)行了指針賦值:

arm_pm_restart = msm_pm_restart;

賦值的函數(shù)為msm_pm_init, 其調(diào)用為

late_initcall_sync(msm_pm_init);

late_initcall_sync的啟動(dòng)優(yōu)先級(jí)是最低的,為7。module_init其實(shí)是6的優(yōu)先級(jí),數(shù)字越大優(yōu)先級(jí)越低。所以,這樣推斷的話,最終arm_pm_restart這個(gè)函數(shù)指針會(huì)指向msm_pm_restart。關(guān)于msm_pm_restart的具體實(shí)現(xiàn)也不細(xì)看了,跟前邊說的一樣,都是各家不一樣,就幾行代碼:

static void msm_pm_restart(char str, const char *cmd)

{

msm_rpcrouter_close();

msm_proc_comm(PCOM_RESET_CHIP, &restart_reason, 0);

for (;;)

;

}

但是細(xì)心的朋友可能會(huì)發(fā)現(xiàn)這里有一個(gè)restart_reason,這個(gè)并不是傳遞下來的參數(shù)。事實(shí)上,這個(gè)值已經(jīng)在之前kernel_restart_prepare(cmd)的時(shí)候就已經(jīng)設(shè)置好了。

void kernel_restart_prepare(char *cmd)

{

blocking_notifier_call_chain(&reboot_notifier_list, SYS_RESTART, cmd);

system_state = SYSTEM_RESTART;

usermodehelper_disable();

device_shutdown();

syscore_shutdown();

}

就是blocking_notifier機(jī)制,這個(gè)操作在之前的shutdown關(guān)機(jī)操作中也有,且是同一個(gè)list,都是reboot_notifier_list。也很容易理解,就是將注冊(cè)在reboot_notifier_list上的函數(shù)傳入相關(guān)參數(shù)后執(zhí)行,作為了解,看一下具體是怎么使用的:(arch/arm/mach-msm/pm2.c)

static int msm_reboot_call

(struct notifier_block *this, unsigned long code, void *_cmd)

{

if ((code == SYS_RESTART) && _cmd) {

char *cmd = _cmd;

if (!strcmp(cmd, "bootloader")) {

restart_reason = 0x77665500;

} else if (!strcmp(cmd, "recovery")) {

restart_reason = 0x77665502;

} else if (!strcmp(cmd, "eraseflash")) {

restart_reason = 0x776655EF;

} else if (!strncmp(cmd, "oem-", 4)) {

unsigned code = simple_strtoul(cmd + 4, 0, 16) & 0xff;

restart_reason = 0x6f656d00 | code;

} else {

restart_reason = 0x77665501;

}

}

return NOTIFY_DONE;

}

static struct notifier_block msm_reboot_notifier = {

.notifier_call = msm_reboot_call,

};

...

static int __init msm_pm_init(void)

{

...

register_reboot_notifier(&msm_reboot_notifier);

...

}

OK,萬事大吉,在kernel_restart_prepare的時(shí)候msm_reboot_call會(huì)被首先調(diào)用,這個(gè)函數(shù)的作用就是根據(jù)用戶命令給restart_reason賦值,從而在之后調(diào)用msm_pm_restart的時(shí)候使用。這里我們發(fā)現(xiàn)在reboot的時(shí)候可以帶的參數(shù)不僅有recovery,bootloader,還有eraseflash和oem-???,字面上看應(yīng)該是用來擦除ROM和解鎖之類的操作了。

三、關(guān)機(jī)怎么用?本文的分析是由Android給出的reboot接口開始的,但是分析來分析去,回頭想一想會(huì)發(fā)現(xiàn),Android給出的接口reboot就真的只能重啟而已,不能進(jìn)行關(guān)機(jī)操作,可以在跟蹤這個(gè)流程的過程中會(huì)發(fā)現(xiàn),確實(shí)是有存在關(guān)機(jī)的相關(guān)接口的。那么關(guān)機(jī)該怎么用呢?

frameworks/base/services/java/com/android/serverBatteryService.java

private final void shutdownIfNoPower() {

// shut down gracefully if our battery is critically low and we are not powered.

// wait until the system has booted before attempting to display the shutdown dialog.

if (mBatteryLevel == 0 && !isPowered() && ActivityManagerNative.isSystemReady()) {

Intent intent = new Intent(Intent.ACTION_REQUEST_SHUTDOWN);

intent.putExtra(Intent.EXTRA_KEY_CONFIRM, false);

intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

mContext.startActivity(intent);

}

時(shí)間: 2016-02-20

總結(jié)

以上是生活随笔為你收集整理的android 关机 流程_Android系统关机的全流程解析的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。