`
womendu
  • 浏览: 1480533 次
  • 性别: Icon_minigender_2
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

MTD系列 - android平台上linux启动时init进程解析init.rc文件分析

阅读更多

题记:
前段时间,分析linux中的nand驱动,不知不觉就搞到来MTD层,到了MTD层之后还行往上,却发现往上很难继续,
功夫不复有心人呐,终于有些思路,发现MTD设备存在很多用户,什么char,block,ftl,nftl,jffs,yaffs等比较多,
但是并不是所有的用户都经过了MTD的块设备层,比如jffs和yaffs文件系统就时直接建立在MTD的原始设备层之上的。
好奇心强了就会没完没了,想跟踪一下yaffs2文件系统的挂载过程,心有点大了,不过我还是有勇气去跟踪它。
我手上的项目使用了android系统,这篇文章就上来讲讲android中的init进程所做的事情。当然它所做的事情多来去来,
这里只涉及和yaffs2相关的一些东西。同时,本文不会详细分析这部分的代码,只看流程。

* linux2.6.29
* android2.0
* 李枝果/lizgo 2010-11-4 lizhiguo0532@163.com
* 本人水平有限,难免由不妥之处,烦请指正,谢谢!


话说在start_kernel()函数中的vfs_caches_init()中将rootfs建立起来后,根文件系统就一直是内存中的这个rootfs,
直到在内核线程kernel_init中,通过函数populate_rootfs()函数将initrd或者initramfs释放到内存中的rootfs空间中
去后,至此,rootfs被initrd代替(initrd是文件系统目录使用命令cpio处理后压缩的镜像),这个时候根目录就会呈现
出initrd原本的目录结构,其中包含一些初始化系统所必须的文件。

这个制作initrd的原始文件系统从那里的来呢?
当我们在编译完android系统后,会在目录out/target/product/xxx(xxx为你所建工程名字)中出现一个root目录,
我们可以使用这个目录来制作initrd,下面是我的root目录内容:
drwxr-xr-x 2 lzg lzg 4096 2010-08-23 08:59 data
-rw-r--r-- 1 lzg lzg 118 2010-08-23 08:59 default.prop
drwxr-xr-x 2 lzg lzg 4096 2010-08-23 08:59 dev
-rwxr-xr-x 1 lzg lzg 103104 2010-08-23 11:17 init
-rw-r--r-- 1 lzg lzg 1677 2010-08-19 14:29 init.goldfish.rc
-rwxr-xr-x 1 lzg lzg 4211 2010-08-02 15:22 init.pxa930.rc
-rwxr-xr-x 1 lzg lzg 11275 2010-08-02 15:22 init.rc
drwxr-xr-x 2 lzg lzg 4096 2010-08-23 08:59 proc
drwxr-xr-x 2 lzg lzg 4096 2010-08-23 11:19 sbin
drwxr-xr-x 2 lzg lzg 4096 2010-08-23 08:59 sys
drwxr-xr-x 2 lzg lzg 4096 2010-08-23 08:59 system
其中的init是一个elf格式的可执行文件,该文件会在rootfs被initrd替换后得到执行:
kernel_init内核线程中的init_post()-->run_init_process(init)-->kernel_execve()来创建进程init。
下面所分析的部分就是init进程的源码,源码位置: system/core/init

main()// init.c
--> parse_config_file("/init.rc")// parser.c
--> data = read_file(fn, 0);// 读取文件
--> parse_config(fn, data);// 解析文件--- 较详细的分解看附录内容
在解析文件的过程中是不会实际执行的,android这里实际上使用了服务器来统一执行。函数parse_config()函数会
首先找到init.rc中类似下面的行:
service mountd /system/bin/mountd
socket vold stream 0660 root mount// mountd的源码在system/core/mountd/

这种就时描述了需要启动一个服务器来做事情,至于什么时候启动,怎么做,做那些事情,都是由后面跟着的选项所
决定的。该函数将这些服务器找到后组织成struct service的结构体,并且通过该结构体中的挂钩slist将这些服务器
挂在全局的链表service_list中,同时每找到一个服务器,都会使用函数
list_init(&svc>onrestart.commands)来初始化该服务器重启的时候需要执行的命令的链表,以备后续挂接网上挂接
命令结构体。接着解析分析该init.rc文件,遇到command类型的行,会建立struct command类型的命令结构体,
然后挂在对应服务器的链表头中,等待执行。

比如,我这里关系的mount命令何时执行,那么从上面可以看出,系统将来会启动mountd服务器来执行init.rc中
所有mount命令。那么对于mount命令对应需要执行的函数时什么呢?这个很容易看出,从keyword.h中就可以看出
是do_mount()函数。

这里就不继续深入分析这个服务器是怎么建立起来的了,我想,如果知道这个过程后,我们最关心的还是最后执行命令
的函数吧!接下来我们来看看do_mount()函数吧

先列举一下init.rc文件中关于挂载nand分区的命令:
# mount mtd partitions

mount yaffs2 mtd@system /system

mount yaffs2 mtd@opl /opl

mount yaffs2 mtd@userdata /data nosuid nodev
chown system system /data
chmod 0771 /data

mount yaffs2 mtd@local /local nosuid nodev
chown system system /local
chmod 0777 /local

mount yaffs2 mtd@cache /cache nosuid nodev
chown system cache /cache
chmod 0770 /cache
...

可以看出这些分区中存放的全是yaffs2文件系统格式的数据。

do_mount()函数在builtins.c文件中实现:

static struct {
const char *name;
unsigned flag;
} mount_flags[] = {
{ "noatime", MS_NOATIME },
{ "nosuid", MS_NOSUID },
{ "nodev", MS_NODEV },
{ "nodiratime", MS_NODIRATIME },
{ "ro", MS_RDONLY },
{ "rw", 0 },
{ "remount", MS_REMOUNT },
{ "defaults", 0 },
{ 0, 0 },
};

/* mount <type> <device> <path> <flags ...> <options> */
int do_mount(int nargs, char **args)
{
char tmp[64];
char *source, *target, *system;
char *options = NULL;
unsigned flags = 0;
int n, i;

for (n = 4; n < nargs; n++) {
for (i = 0; mount_flags[i].name; i++) {
if (!strcmp(args[n], mount_flags[i].name)) {
flags |= mount_flags[i].flag;
break;
}
}

/* if our last argument isn't a flag, wolf it up as an option string */
if (n + 1 == nargs && !mount_flags[i].name)
options = args[n];
}

system = args[1];// yaffs2
source = args[2];
target = args[3];

if (!strncmp(source, "mtd@", 4)) {// 看,mtd的标志
n = mtd_name_to_number(source + 4);// 这个函数咋回事,请看该函数后面的部分
if (n < 0) {// 看到了吧,这里就是根据init.rc中的mount 命令行中的
// mtd@后面的字符串来得到以这个名字命名的分区号是多少
return -1;// 分区号从0开始
}

// 根据我们项目的情况,列出上面mount命令的分区号:
// system - 4 ;opl - 7 ;userdata - 13 ;local - 5 ;cache - 9

sprintf(tmp, "/dev/block/mtdblock%d", n);// /dev/block/mtdblock4- system
// /dev/block/mtdblock7- opl
// /dev/block/mtdblock13- userdata
// /dev/block/mtdblock5- local
// /dev/block/mtdblock9- cache

if (mount(tmp, target, system, flags, options) < 0) {
// 这里直接调用libc库中的mount函数,最中会经过软中断调用到内核中的sys_mount()系统调用的。
return -1;
// 本文到这里就结束了哦,接下来去看看sys_mount()系统调用!!!!!!!!!!!!!!!!!!!!!
}

return 0;
} else if (!strncmp(source, "loop@", 5)) {
int mode, loop, fd;
struct loop_info info;

mode = (flags & MS_RDONLY) ? O_RDONLY : O_RDWR;
fd = open(source + 5, mode);
if (fd < 0) {
return -1;
}

for (n = 0; ; n++) {
sprintf(tmp, "/dev/block/loop%d", n);
loop = open(tmp, mode);
if (loop < 0) {
return -1;
}

/* if it is a blank loop device */
if (ioctl(loop, LOOP_GET_STATUS, &info) < 0 && errno == ENXIO) {
/* if it becomes our loop device */
if (ioctl(loop, LOOP_SET_FD, fd) >= 0) {
close(fd);

if (mount(tmp, target, system, flags, options) < 0) {
ioctl(loop, LOOP_CLR_FD, 0);
close(loop);
return -1;
}

close(loop);
return 0;
}
}

close(loop);
}

close(fd);
ERROR("out of loopback devices");
return -1;
} else {
if (mount(source, target, system, flags, options) < 0) {
return -1;
}

return 0;
}
}


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% mtd_name_to_number
int mtd_name_to_number(const char *name)
{
int n;
if (mtd_part_count < 0) {
mtd_part_count = 0;
find_mtd_partitions();
}
for (n = 0; n < mtd_part_count; n++) {
if (!strcmp(name, mtd_part_map[n].name)) {// 靠,mtd_part_map,它是嘛时候冒出来?
return mtd_part_map[n].number;
}
}
return -1;
}

#define MAX_MTD_PARTITIONS 16

static struct {
char name[16];
int number;
} mtd_part_map[MAX_MTD_PARTITIONS];// 噢,原来这里在定义,那哪里填充了呢?请看find_mtd_partitions函数

static int mtd_part_count = -1;

static void find_mtd_partitions(void)
{
int fd;
char buf[1024];
char *pmtdbufp;
ssize_t pmtdsize;
int r;

fd = open("/proc/mtd", O_RDONLY);// 呵呵,原来从/proc/mtd文件中读出了NAND设备的分区信息呀,记住哦,在以后分析MTD层的时候会看到创建来这个文件
if (fd < 0)
return;

buf[sizeof(buf) - 1] = '\0';
pmtdsize = read(fd, buf, sizeof(buf) - 1);
pmtdbufp = buf;
while (pmtdsize > 0) {// 下面就是解析这个文件中的数据,填充结构体数组mtd_part_map
int mtdnum, mtdsize, mtderasesize;
char mtdname[16];
mtdname[0] = '\0';
mtdnum = -1;
r = sscanf(pmtdbufp, "mtd%d: %x %x %15s",
&mtdnum, &mtdsize, &mtderasesize, mtdname);
if ((r == 4) && (mtdname[0] == '"')) {
char *x = strchr(mtdname + 1, '"');
if (x) {
*x = 0;
}
INFO("mtd partition %d, %s\n", mtdnum, mtdname + 1);
if (mtd_part_count < MAX_MTD_PARTITIONS) {
strcpy(mtd_part_map[mtd_part_count].name, mtdname + 1);
mtd_part_map[mtd_part_count].number = mtdnum;
mtd_part_count++;
} else {
ERROR("too many mtd partitions\n");
}
}
while (pmtdsize > 0 && *pmtdbufp != '\n') {
pmtdbufp++;
pmtdsize--;
}
if (pmtdsize > 0) {
pmtdbufp++;
pmtdsize--;
}
}
close(fd);
}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%


附录:
//////////////////////parser.c
#define SVC_MAXARGS 64

#define T_EOF 0
#define T_TEXT 1
#define T_NEWLINE 2

struct parse_state
{
char *ptr;
char *text;
int line;
int nexttoken;
void *context;
void (*parse_line)(struct parse_state *state, int nargs, char **args);
const char *filename;
};

static void parse_config(const char *fn, char *s)
{
struct parse_state state;
char *args[SVC_MAXARGS];// 存放参数的指针数组
int nargs;// 参数个数

nargs = 0;
state.filename = fn;
state.line = 1;
state.ptr = s;
state.nexttoken = 0;
state.parse_line = parse_line_no_op;
// 依次读取该文件的每一个字符,判断是否时文件正文,还是新的一行,还是文件结束
for (;;) {
switch (next_token(&state)) {
case T_EOF:
state.parse_line(&state, 0, 0);
return;
case T_NEWLINE:// 表明一行读取完成,记下来就时解析参数,并执行了。
if (nargs) {// 空行跳过
int kw = lookup_keyword(args[0]);// args[0]中存放的是关键参数,遍历关键字列表
if (kw_is(kw, SECTION)) {
state.parse_line(&state, 0, 0);
parse_new_section(&state, kw, nargs, args);
} else {// 类型不是SECTION
state.parse_line(&state, nargs, args);
}
nargs = 0;
}
break;
case T_TEXT:
if (nargs < SVC_MAXARGS) {// 如果读到的下一个标签是正文,就认为这是一个参数,一行中参数不能超过64个
args[nargs++] = state.text;
}
break;
}
}
}

下面的函数就是从关键字的第二个字符开始比较,匹配成功,返回相应值。例如,mount,那么就会和ount来匹配,成功后返回K_mount,那么这个K_mount是什么呢?请看下面
int lookup_keyword(const char *s)
{
switch (*s++) {
case 'c':
if (!strcmp(s, "opy")) return K_copy;
if (!strcmp(s, "apability")) return K_capability;
if (!strcmp(s, "lass")) return K_class;
if (!strcmp(s, "lass_start")) return K_class_start;
if (!strcmp(s, "lass_stop")) return K_class_stop;
if (!strcmp(s, "onsole")) return K_console;
if (!strcmp(s, "hown")) return K_chown;
if (!strcmp(s, "hmod")) return K_chmod;
if (!strcmp(s, "ritical")) return K_critical;
break;
case 'd':
if (!strcmp(s, "isabled")) return K_disabled;
if (!strcmp(s, "omainname")) return K_domainname;
if (!strcmp(s, "evice")) return K_device;
break;
case 'e':
if (!strcmp(s, "xec")) return K_exec;
if (!strcmp(s, "xport")) return K_export;
break;
case 'g':
if (!strcmp(s, "roup")) return K_group;
break;
case 'h':
if (!strcmp(s, "ostname")) return K_hostname;
break;
case 'i':
if (!strcmp(s, "fup")) return K_ifup;
if (!strcmp(s, "nsmod")) return K_insmod;
if (!strcmp(s, "mport")) return K_import;
break;
case 'k':
if (!strcmp(s, "eycodes")) return K_keycodes;
break;
case 'l':
if (!strcmp(s, "oglevel")) return K_loglevel;
break;
case 'm':
if (!strcmp(s, "kdir")) return K_mkdir;
if (!strcmp(s, "ount")) return K_mount;
break;
case 'o':
if (!strcmp(s, "n")) return K_on;
if (!strcmp(s, "neshot")) return K_oneshot;
if (!strcmp(s, "nrestart")) return K_onrestart;
break;
case 'r':
if (!strcmp(s, "estart")) return K_restart;
break;
case 's':
if (!strcmp(s, "ervice")) return K_service;
if (!strcmp(s, "etenv")) return K_setenv;
if (!strcmp(s, "etkey")) return K_setkey;
if (!strcmp(s, "etprop")) return K_setprop;
if (!strcmp(s, "etrlimit")) return K_setrlimit;
if (!strcmp(s, "ocket")) return K_socket;
if (!strcmp(s, "tart")) return K_start;
if (!strcmp(s, "top")) return K_stop;
if (!strcmp(s, "ymlink")) return K_symlink;
if (!strcmp(s, "ysclktz")) return K_sysclktz;
break;
case 't':
if (!strcmp(s, "rigger")) return K_trigger;
break;
case 'u':
if (!strcmp(s, "ser")) return K_user;
break;
case 'w':
if (!strcmp(s, "rite")) return K_write;
break;
}
return K_UNKNOWN;
}

parser.c文件中有这么一段:
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
#define SECTION 0x01
#define COMMAND 0x02
#define OPTION 0x04

#include "keywords.h"// 看看这个keywords.h头文件中时什么内容吧!

#define KEYWORD(symbol, flags, nargs, func) \
[ K_##symbol ] = { #symbol, func, nargs + 1, flags, },

struct {
const char *name;
int (*func)(int nargs, char **args);
unsigned char nargs;
unsigned char flags;
} keyword_info[KEYWORD_COUNT] = {
[ K_UNKNOWN ] = { "unknown", 0, 0, 0 },
#include "keywords.h"
};
#undef KEYWORD

#define kw_is(kw, type) (keyword_info[kw].flags & (type))
#define kw_name(kw) (keyword_info[kw].name)
#define kw_func(kw) (keyword_info[kw].func)
#define kw_nargs(kw) (keyword_info[kw].nargs)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

//////////////////经过keywords.h中宏的控制,该部分最后变成
-------------------------------------------------------------------------------------------------
#define SECTION 0x01
#define COMMAND 0x02
#define OPTION 0x04

int do_class_start(int nargs, char **args);
int do_class_stop(int nargs, char **args);
int do_domainname(int nargs, char **args);
int do_exec(int nargs, char **args);
int do_export(int nargs, char **args);
int do_hostname(int nargs, char **args);
int do_ifup(int nargs, char **args);
int do_insmod(int nargs, char **args);
int do_import(int nargs, char **args);
int do_mkdir(int nargs, char **args);
int do_mount(int nargs, char **args);
int do_restart(int nargs, char **args);
int do_setkey(int nargs, char **args);
int do_setprop(int nargs, char **args);
int do_setrlimit(int nargs, char **args);
int do_start(int nargs, char **args);
int do_stop(int nargs, char **args);
int do_trigger(int nargs, char **args);
int do_symlink(int nargs, char **args);
int do_sysclktz(int nargs, char **args);
int do_write(int nargs, char **args);
int do_copy(int nargs, char **args);
int do_chown(int nargs, char **args);
int do_chmod(int nargs, char **args);
int do_loglevel(int nargs, char **args);
int do_device(int nargs, char **args);
#define __MAKE_KEYWORD_ENUM__
#define KEYWORD(symbol, flags, nargs, func) K_##symbol,

enum {
K_UNKNOWN,
K_capability,
K_class,
...
K_mount,
K_device,
KEYWORD_COUNT,
};

#define KEYWORD(symbol, flags, nargs, func) \
[ K_##symbol ] = { #symbol, func, nargs + 1, flags, },

struct {
const char *name;
int (*func)(int nargs, char **args);
unsigned char nargs;
unsigned char flags;
} keyword_info[KEYWORD_COUNT] = {
[ K_UNKNOWN ] = { "unknown", 0, 0, 0 },
[K_capability] = {"capability", 0, 1, OPTION},
...
[K_mount] = {"mount", do_mount, 4, COMMAND},
...
[K_device] = {"device", do_device, 5, COMMAND}
};

#undef KEYWORD

#define kw_is(kw, type) (keyword_info[kw].flags & (type))
#define kw_name(kw) (keyword_info[kw].name)
#define kw_func(kw) (keyword_info[kw].func)
#define kw_nargs(kw) (keyword_info[kw].nargs)
-------------------------------------------------------------------------------------------------

///////////////////////keyword.h

#ifndef KEYWORD
int do_class_start(int nargs, char **args);
int do_class_stop(int nargs, char **args);
int do_domainname(int nargs, char **args);
int do_exec(int nargs, char **args);
int do_export(int nargs, char **args);
int do_hostname(int nargs, char **args);
int do_ifup(int nargs, char **args);
int do_insmod(int nargs, char **args);
int do_import(int nargs, char **args);
int do_mkdir(int nargs, char **args);
int do_mount(int nargs, char **args);
int do_restart(int nargs, char **args);
int do_setkey(int nargs, char **args);
int do_setprop(int nargs, char **args);
int do_setrlimit(int nargs, char **args);
int do_start(int nargs, char **args);
int do_stop(int nargs, char **args);
int do_trigger(int nargs, char **args);
int do_symlink(int nargs, char **args);
int do_sysclktz(int nargs, char **args);
int do_write(int nargs, char **args);
int do_copy(int nargs, char **args);
int do_chown(int nargs, char **args);
int do_chmod(int nargs, char **args);
int do_loglevel(int nargs, char **args);
int do_device(int nargs, char **args);
#define __MAKE_KEYWORD_ENUM__
#define KEYWORD(symbol, flags, nargs, func) K_##symbol,
enum {
K_UNKNOWN,
#endif
KEYWORD(capability, OPTION, 0, 0)
KEYWORD(class, OPTION, 0, 0)
KEYWORD(class_start, COMMAND, 1, do_class_start)
KEYWORD(class_stop, COMMAND, 1, do_class_stop)
KEYWORD(console, OPTION, 0, 0)
KEYWORD(critical, OPTION, 0, 0)
KEYWORD(disabled, OPTION, 0, 0)
KEYWORD(domainname, COMMAND, 1, do_domainname)
KEYWORD(exec, COMMAND, 1, do_exec)
KEYWORD(export, COMMAND, 2, do_export)
KEYWORD(group, OPTION, 0, 0)
KEYWORD(hostname, COMMAND, 1, do_hostname)
KEYWORD(ifup, COMMAND, 1, do_ifup)
KEYWORD(insmod, COMMAND, 1, do_insmod)
KEYWORD(import, COMMAND, 1, do_import)
KEYWORD(keycodes, OPTION, 0, 0)
KEYWORD(mkdir, COMMAND, 1, do_mkdir)
KEYWORD(mount, COMMAND, 3, do_mount)
KEYWORD(on, SECTION, 0, 0)
KEYWORD(oneshot, OPTION, 0, 0)
KEYWORD(onrestart, OPTION, 0, 0)
KEYWORD(restart, COMMAND, 1, do_restart)
KEYWORD(service, SECTION, 0, 0)
KEYWORD(setenv, OPTION, 2, 0)
KEYWORD(setkey, COMMAND, 0, do_setkey)
KEYWORD(setprop, COMMAND, 2, do_setprop)
KEYWORD(setrlimit, COMMAND, 3, do_setrlimit)
KEYWORD(socket, OPTION, 0, 0)
KEYWORD(start, COMMAND, 1, do_start)
KEYWORD(stop, COMMAND, 1, do_stop)
KEYWORD(trigger, COMMAND, 1, do_trigger)
KEYWORD(symlink, COMMAND, 1, do_symlink)
KEYWORD(sysclktz, COMMAND, 1, do_sysclktz)
KEYWORD(user, OPTION, 0, 0)
KEYWORD(write, COMMAND, 2, do_write)
KEYWORD(copy, COMMAND, 2, do_copy)
KEYWORD(chown, COMMAND, 2, do_chown)
KEYWORD(chmod, COMMAND, 2, do_chmod)
KEYWORD(loglevel, COMMAND, 1, do_loglevel)
KEYWORD(device, COMMAND, 4, do_device)
#ifdef __MAKE_KEYWORD_ENUM__
KEYWORD_COUNT,
};
#undef __MAKE_KEYWORD_ENUM__
#undef KEYWORD
#endif

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics