http://www.2cto.com/kf/201411/354953.html
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
|
int
main(
int
argc,
char
**argv)
{
//<part 1="">
if
(!strcmp(basename(argv[
0
]),
"ueventd"
))
return
ueventd_main(argc, argv);
if
(!strcmp(basename(argv[
0
]),
"watchdogd"
))
return
watchdogd_main(argc, argv);
//<part2>
umask(
0
);
mkdir(
"/dev"
,
0755
);
mkdir(
"/proc"
,
0755
);
mkdir(
"/sys"
,
0755
);
mount(
"tmpfs"
,
"/dev"
,
"tmpfs"
, MS_NOSUID,
"mode=0755"
);
mkdir(
"/dev/pts"
,
0755
);
mkdir(
"/dev/socket"
,
0755
);
mount(
"devpts"
,
"/dev/pts"
,
"devpts"
,
0
, NULL);
mount(
"proc"
,
"/proc"
,
"proc"
,
0
, NULL);
mount(
"sysfs"
,
"/sys"
,
"sysfs"
,
0
, NULL);
....
open_devnull_stdio();
klog_init();
property_init();
....
//<part3>
INFO(
"reading config file\n"
);
init_parse_config_file(
"/init.rc"
);
...
action_for_each_trigger(
"early-init"
, action_add_queue_tail);
....
queue_builtin_action(queue_property_triggers_action,
"queue_property_triggers"
);
//<part4>
for
(;;) {
...
execute_one_command();
restart_processes();
....
nr = poll(ufds, fd_count, timeout);
if
(nr <=
0
)
continue
;
for
(i =
0
; i < fd_count; i++) {
if
(ufds[i].revents & POLLIN) {
if
(ufds[i].fd == get_property_set_fd())
handle_property_set_fd();
else
if
(ufds[i].fd == get_keychord_fd())
handle_keychord();
else
if
(ufds[i].fd == get_signal_fd())
handle_signal();
}
}
}
return
0
;
}</part4></part3></part2></part>
|
umaks(0)用于设定当前进程(即/init)的文件模型创建掩码(file mode creation mask),注意这里的文件是广泛意义上的文件,包括普通文件、目录、链接文件、设备节点等。
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
|
open_devnull_stdio();
klog_init();
property_init();
get_hardware_name(hardware, &revision);
process_kernel_cmdline();
union selinux_callback cb;
cb.func_log = log_callback;
selinux_set_callback(SELINUX_CB_LOG, cb);
cb.func_audit = audit_callback;
selinux_set_callback(SELINUX_CB_AUDIT, cb);
selinux_initialize();
/* These directories were necessarily created before initial policy load
* and therefore need their security context restored to the proper value.
* This must happen before /dev is populatedproperty_init(); by ueventd.
*/
restorecon(
"/dev"
);
restorecon(
"/dev/socket"
);
restorecon(
"/dev/__properties__"
);
restorecon_recursive(
"/sys"
);
is_charger = !strcmp(bootmode,
"charger"
);
INFO(
"property init\n"
);
property_load_boot_defaults();
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
void
open_devnull_stdio(
void
)
{
int
fd;
static
const
char
*name =
"/dev/__null__"
;
if
(mknod(name, S_IFCHR |
0600
, (
1
<<
8
) |
3
) ==
0
) {
fd = open(name, O_RDWR);
unlink(name);
if
(fd >=
0
) {
dup2(fd,
0
);
dup2(fd,
1
);
dup2(fd,
2
);
if
(fd >
2
) {
close(fd);
}
return
;
}
}
exit(
1
);
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
1
char
Memory devices
1
= /dev/mem Physical memory access
2
= /dev/kmem Kernel virtual memory access
3
= /dev/
null
Null device
4
= /dev/port I/O port access
5
= /dev/zero Null
byte
source
6
= /dev/core OBSOLETE - replaced by /proc/kcore
7
= /dev/full Returns ENOSPC on write
8
= /dev/random Nondeterministic random number gen.
9
= /dev/urandom Faster, less secure random number gen.
10
= /dev/aio Asynchronous I/O notification
interface
11
= /dev/kmsg Writes to
this
come out as printk's
12
= /dev/oldmem Used by crashdump kernels to access
the memory of the kernel that crashed.
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
void
klog_init(
void
)
{
static
const
char
*name =
"/dev/__kmsg__"
;
if
(klog_fd >=
0
)
return
;
/* Already initialized */
if
(mknod(name, S_IFCHR |
0600
, (
1
<<
8
) |
11
) ==
0
) {
klog_fd = open(name, O_WRONLY);
if
(klog_fd <
0
)
return
;
fcntl(klog_fd, F_SETFD, FD_CLOEXEC);
unlink(name);
}
}
|
1
2
|
P.S.根据unlink的mannul,(man
2
unlink),其中写道:
If the name was the last link to a file but any processes still have the file open the file will remain in existence until the last file descriptor referring to it is closed.
|
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
|
static
int
klog_level = KLOG_DEFAULT_LEVEL;
int
klog_get_level(
void
) {
return
klog_level;
}
void
klog_set_level(
int
level) {
klog_level = level;
}
#define LOG_BUF_MAX
512
void
klog_vwrite(
int
level,
const
char
*fmt, va_list ap)
{
char
buf[LOG_BUF_MAX];
if
(level > klog_level)
return
;
if
(klog_fd <
0
) klog_init();
if
(klog_fd <
0
)
return
;
vsnprintf(buf, LOG_BUF_MAX, fmt, ap);
buf[LOG_BUF_MAX -
1
] =
0
;
write(klog_fd, buf, strlen(buf));
}
void
klog_write(
int
level,
const
char
*fmt, ...)
{
va_list ap;
va_start(ap, fmt);
klog_vwrite(level, fmt, ap);
va_end(ap);
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
|
#define KLOG_ERROR_LEVEL
3
#define KLOG_WARNING_LEVEL
4
#define KLOG_NOTICE_LEVEL
5
#define KLOG_INFO_LEVEL
6
#define KLOG_DEBUG_LEVEL
7
#define KLOG_ERROR(tag,x...) klog_write(KLOG_ERROR_LEVEL,
"<3>"
tag
": "
x)
#define KLOG_WARNING(tag,x...) klog_write(KLOG_WARNING_LEVEL,
"<4>"
tag
": "
x)
#define KLOG_NOTICE(tag,x...) klog_write(KLOG_NOTICE_LEVEL,
"<5>"
tag
": "
x)
#define KLOG_INFO(tag,x...) klog_write(KLOG_INFO_LEVEL,
"<6>"
tag
": "
x)
#define KLOG_DEBUG(tag,x...) klog_write(KLOG_DEBUG_LEVEL,
"<7>"
tag
": "
x)
#define KLOG_DEFAULT_LEVEL
3
/* messages <= this level are logged */
|
这一句用来初始化Android的属性系统,将在init之属性系统中专门介绍。
get_hardware_name(hardware, &revision)通过读取/proc/cpuinfo文件获取硬件信息,以笔者的山寨机为例,该文件内容如下。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
shell
@android
:/ $ cat /proc/cpuinfo
Processor : ARMv7 Processor rev
1
(v7l)
processor :
0
BogoMIPS :
348.76
processor :
1
BogoMIPS :
348.76
processor :
2
BogoMIPS :
348.76
processor :
3
BogoMIPS :
348.76
Features : swp half thumb fastmult vfp edsp thumbee neon vfpv3 tls vfpv4
CPU implementer :
0x41
CPU architecture:
7
CPU variant :
0x0
CPU part :
0xc05
CPU revision :
1
Hardware : QRD MSM8625Q SKUD
Revision :
0000
Serial :
0000000000000000
|
接下来init程序调用函数process_kernel_cmdline解析内核启动参数。内核通常由bootloader(启动引导程序)加载启动,目前广泛使用的bootloader大都基于u-boot定制。内核允许bootloader启动自己时传递参数。在内核启动完毕之后,启动参数可通过/proc/cmdline查看。
例如android4.4模拟器启动后,查看其内核启动参数,如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
static
void
process_kernel_cmdline(
void
)
{
/* don't expose the raw commandline to nonpriv processes */
chmod(
"/proc/cmdline"
,
0440
);
/* first pass does the common stuff, and finds if we are in qemu.
* second pass is only necessary for qemu to export all kernel params
* as props.
*/
import_kernel_cmdline(
0
, import_kernel_nv);
if
(qemu[
0
])
import_kernel_cmdline(
1
, import_kernel_nv);
/* now propogate the info given on command line to internal variables
* used by init as well as the current required properties
*/
export_kernel_boot_props();
}
|
import_kernel_cmdline函数将/proc/cmdline内容读入到内部缓冲区中,并将cmdline内容的以空格拆分成小段字符串,依次传递给import_kernel_nv函数处理。以前面/proc/cmdline的输出为例子,该字符串共可以拆分成以下几段
1
2
3
4
5
6
|
qemu.gles=
0
qemu=
1
console=ttyS0
android.qemud=ttyS1
android.checkjni=
1
ndns=
1
|
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
void
import_kernel_nv(
char
*name,
int
for_emulator)
{
char
*value = strchr(name,
'='
);
int
name_len = strlen(name);
if
(value ==
0
)
return
;
*value++ =
0
;
if
(name_len ==
0
)
return
;
if
(for_emulator) {
/* in the emulator, export any kernel option with the
* ro.kernel. prefix */
char
buff[PROP_NAME_MAX];
int
len = snprintf( buff, sizeof(buff),
"ro.kernel.%s"
, name );
if
(len < (
int
)sizeof(buff))
property_set( buff, value );
return
;
}
if
(!strcmp(name,
"qemu"
)) {
strlcpy(qemu, value, sizeof(qemu));
}
else
if
(!strncmp(name,
"androidboot."
,
12
) && name_len >
12
) {
const
char
*boot_prop_name = name +
12
;
char
prop[PROP_NAME_MAX];
int
cnt;
cnt = snprintf(prop, sizeof(prop),
"ro.boot.%s"
, boot_prop_name);
if
(cnt < PROP_NAME_MAX)
property_set(prop, value);
}
}
|
此时回到process_kernel_cmdline函数,继续执行
1
2
|
if
(qemu[
0
])
import_kernel_cmdline(
1
, import_kernel_nv);
|
1
2
3
4
5
6
7
|
root
@generic
:/ # getprop | grep ro.kernel.
[ro.kernel.android.checkjni]: [
1
]
[ro.kernel.android.qemud]: [ttyS1]
[ro.kernel.console]: [ttyS0]
[ro.kernel.ndns]: [
1
]
[ro.kernel.qemu.gles]: [
0
]
[ro.kernel.qemu]: [
1
]
|
接下来继续执行process_kernel_cmdline函数的最后一句export_kernel_boot_props。由于该函数实现非常直观,其代码不在详细描述。该函数用于设置几个系统属性,具体包括如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
union selinux_callback cb;
cb.func_log = log_callback;
selinux_set_callback(SELINUX_CB_LOG, cb);
cb.func_audit = audit_callback;
selinux_set_callback(SELINUX_CB_AUDIT, cb);
selinux_initialize();
/* These directories were necessarily created before initial policy load
* and therefore need their security context restored to the proper value.
* This must happen before /dev is populated by ueventd.
*/
restorecon(
"/dev"
);
restorecon(
"/dev/socket"
);
restorecon(
"/dev/__properties__"
);
restorecon_recursive(
"/sys"
);
|
回到init函数继续分析
1
2
3
4
|
is_charger = !strcmp(bootmode,
"charger"
);
INFO(
"property init\n"
);
property_load_boot_defaults();
|
接下来调用INFO宏打印一条log语句,此宏定义在init/log.h中,其实现如下
1
2
3
|
#define ERROR(x...) KLOG_ERROR(
"init"
, x)
#define NOTICE(x...) KLOG_NOTICE(
"init"
, x)
#define INFO(x...) KLOG_INFO(
"init"
, x)
|
到这里init.c main函数之代码分析分析完毕。
接下来代码涉及init进程核心功能:init.rc解析。这部分代码逻辑我们将在独立文章《Android init源代码分析(2)init.rc解析》中介绍。