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