我们经常查看 Settings -> About phone -> Kernel version 中的编译时间,那么,这个时间到底怎么来的呢?
我们今天基于Android 7.0代码来分析一下。
Settings中用于显示Kernel version的关键代码如下:
Java部分
1 2 3 4 5 6 7 8
| public void onCreate(Bundle icicle) { ... findPreference(KEY_KERNEL_VERSION).setSummary(DeviceInfoUtils.getFormattedKernelVersion()); ... }
|
我们来看一下DeviceInfoUtils.getFormattedKernelVersion()的实现:
1 2 3 4 5 6 7 8 9 10
| private static final String FILENAME_PROC_VERSION = "/proc/version";
public static String getFormattedKernelVersion() { ... return formatKernelVersion(readLine(FILENAME_PROC_VERSION)); ... }
|
我们来看一下formatKernelVersion()的实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| public static String formatKernelVersion(String rawKernelVersion) { final String PROC_VERSION_REGEX = "Linux version (\\S+) " + "\\((\\S+?)\\) " + "(?:\\(gcc.+? \\)) " + "(#\\d+) " + "(?:.*?)?" + "((Sun|Mon|Tue|Wed|Thu|Fri|Sat).+)"; Matcher m = Pattern.compile(PROC_VERSION_REGEX).matcher(rawKernelVersion); ... return m.group(1) + "\n" + m.group(2) + " " + m.group(3) + "\n" + m.group(4); }
|
我们可以看到,Kernel version是通过解析”/proc/version”文件得到的,而”/proc/version” 则是被”kernel-3.18/fs/proc/version.c”生成的。
Kernel部分
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| static int version_proc_show(struct seq_file *m, void *v) { seq_printf(m, linux_proc_banner, utsname()->sysname, utsname()->release, utsname()->version); return 0; }
static int version_proc_open(struct inode *inode, struct file *file) { return single_open(file, version_proc_show, NULL); }
static const struct file_operations version_proc_fops = { .open = version_proc_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, };
static int __init proc_version_init(void) { proc_create("version", 0, NULL, &version_proc_fops); return 0; }
|
其中utsname()的定义如下:
1 2 3 4 5
| static inline struct new_utsname *utsname(void) { return ¤t->nsproxy->uts_ns->name; }
|
nsproxy 的定义如下:
1 2 3 4 5 6 7 8
| struct nsproxy { atomic_t count; struct uts_namespace *uts_ns; struct ipc_namespace *ipc_ns; struct mnt_namespace *mnt_ns; struct pid_namespace *pid_ns; struct net *net_ns; };
|
uts_ns的初始化代码如下:
1 2 3 4 5 6 7 8 9 10 11 12
| struct nsproxy init_nsproxy = { .count = ATOMIC_INIT(1), .uts_ns = &init_uts_ns, #if defined(CONFIG_POSIX_MQUEUE) || defined(CONFIG_SYSVIPC) .ipc_ns = &init_ipc_ns, #endif .mnt_ns = NULL, .pid_ns = &init_pid_ns, #ifdef CONFIG_NET .net_ns = &init_net, #endif };
|
可以看到uts_ns = &init_uts_ns, init_uts_ns的定义如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| struct uts_namespace init_uts_ns = { .kref = { .refcount = ATOMIC_INIT(2), }, .name = { .sysname = UTS_SYSNAME, .nodename = UTS_NODENAME, .release = UTS_RELEASE, .version = UTS_VERSION, .machine = UTS_MACHINE, .domainname = UTS_DOMAINNAME, }, .user_ns = &init_user_ns, .proc_inum = PROC_UTS_INIT_INO, };
|
我们可以看到version = UTS_VERSION,UTS_VERSION这个宏定义在compile.h中,这是一个生成文件,是被 kernel-3.18/scripts/mkcompile_h 这个脚本生成的,生成路径为:
1
| ./out/target/product/projectname/obj/KERNEL_OBJ/include/generated/compile.h
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| # kernel-3.18/scripts/mkcompile_h # 通过抓Log,$KBUILD_BUILD_TIMESTAMP 为空 if [ -z "$KBUILD_BUILD_TIMESTAMP" ]; then # 获取系统当前的日期和时间 TIMESTAMP=`date` else TIMESTAMP=$KBUILD_BUILD_TIMESTAMP fi # 组合出UTS_VERSION UTS_VERSION="$UTS_VERSION $CONFIG_FLAGS $TIMESTAMP" # Generate a temporary compile.h ( echo /\* This file is auto generated, version $VERSION \*/ if [ -n "$CONFIG_FLAGS" ] ; then echo "/* $CONFIG_FLAGS */"; fi echo \#define UTS_MACHINE \"$ARCH\" # 输出UTS_VERSION 到compile.h echo \#define UTS_VERSION \"`echo $UTS_VERSION | $UTS_TRUNCATE`\" echo \#define LINUX_COMPILE_BY \"`echo $LINUX_COMPILE_BY | $UTS_TRUNCATE`\" echo \#define LINUX_COMPILE_HOST \"`echo $LINUX_COMPILE_HOST | $UTS_TRUNCATE`\" echo \#define LINUX_COMPILER \"`$CC -v 2>&1 | tail -n 1`\" ) > .tmpcompile
|
至此,打完收功!