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

来源:互联网 发布:云端软件平台官网 编辑:程序博客网 时间:2024/05/12 18:16

题记:
 前段时间,分析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