构建根文件系统学习笔记

uboot:启动内核
内核 :启动应用程序
      ||
构建: 根文件系统中


挂载完根文件系统后运行的函数为:init/main.c中init_post()
部分代码如下:
static noinline int init_post(void)
{
/* need to finish all async __init code before freeing the memory */
async_synchronize_full();
free_initmem();
mark_rodata_ro();
system_state = SYSTEM_RUNNING;
numa_default_policy();


current->signal->flags |= SIGNAL_UNKILLABLE;

if (ramdisk_execute_command) {
run_init_process(ramdisk_execute_command);
printk(KERN_WARNING "Failed to execute %s\n",
ramdisk_execute_command);
}

/*  
* We try each of these until one succeeds.
*
* The Bourne shell can be used instead of init if we are
* trying to recover a really broken machine.
*/
//如果命令行定义了以下参数,就执行这些应用程序
//bootargs=root=/dev/mtdblock2
//rootfstype=yaffs2
//init=/linuxrc
//console=ttySAC0,115200
//androidboot.console=s3c2410_serial0

if (execute_command) {
//启动应用程序
run_init_process(execute_command);
printk(KERN_WARNING "Failed to execute %s. Attempting "
"defaults...\n", execute_command);
}
//如果命令行没有定义
//以下应用都是死循环,如果上一条指令不执行,才会执行下一条
run_init_process("/sbin/init");
run_init_process("/etc/init");
run_init_process("/bin/init");
run_init_process("/bin/sh");

panic("No init found. Try passing init= option to kernel. "
"See Linux Documentation/init.txt for guidance.");
}

###########################################################################
//打开设备放到了init/main.c里的kernel_init函数中
//标准输入/输入/标准错误
/* Open the /dev/console on the rootfs, this should never fail */
if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0)
printk(KERN_WARNING "Warning: unable to open an initial console.\n");


(void) sys_dup(0);
(void) sys_dup(0);


###########################################################################
根文件系统的烧写


命令行执行的命令都会对应busybox中的指令
执行过程:
init_main
parse_inittab
parser_t *parser = config_open2("/etc/inittab", fopen_for_read);

new_init_action //1 创建一个init action结构,填充
  // 2 把这个结构放入init_action_list链表
run_actions(SYSINIT);
pid_t pid = run(a);//创建process子进程
waitfor(pid); //执行应用程序
run_actions(WAIT);
while (1) {
run_actions(RESPAWN | ASKFIRST);
if (a->pid == 0)
a->pid = run(a);
打印 "\nPlease press Enter to activate this console. ";
等待回车 while (safe_read(STDIN_FILENO, &c, 1) == 1 && c != '\n')
创建子进程:
while (1) {
wpid = waitpid(-1, NULL, maybe_WNOHANG);
}
}


//其他的action
check_delayed_sigs()
if (sig == SIGINT)
run_actions(CTRLALTDEL);




查看busybox源码
控制台的命令对应的是busybox中的命令
在开发板中可以通过以下命令进行查看:
/ # ls -l /bin/sh
lrwxrwxrwx root root 2012-09-18 08:09 sh -> /system/busybox/bin


init进程也是到busybox的链接


cp命令在coreutils/cp.c文件中




init程序所作的工作(打开终端/dev/console):
1 配置文件 /etc/initab
2解析配置文件
3执行用户程序


============================================================
busybox中init/init.c中的init_main函数的部分代码
============================================================
int init_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int init_main(int argc UNUSED_PARAM, char **argv)
{
die_sleep = 30 * 24*60*60; /* if xmalloc would ever die... */

if (argv[1] && strcmp(argv[1], "-q") == 0) {
return kill(1, SIGHUP);
}

if (!DEBUG_INIT) {
/* Expect to be invoked as init with PID=1 or be invoked as linuxrc */
if (getpid() != 1
&& (!ENABLE_FEATURE_INITRD || !strstr(applet_name, "linuxrc"))
) {
bb_show_usage();
}
/* Turn off rebooting via CTL-ALT-DEL - we get a
* SIGINT on CAD so we can shut things down gracefully... */
reboot(RB_DISABLE_CAD); /* misnomer */
}

/* Figure out where the default console should be */
console_init();
set_sane_term();
xchdir("/");
setsid();

/* Make sure environs is set to something sane */
putenv((char *) "HOME=/");
putenv((char *) bb_PATH_root_path);
putenv((char *) "SHELL=/bin/sh");
putenv((char *) "USER=root"); /* needed? why? */

if (argv[1])
xsetenv("RUNLEVEL", argv[1]);

#if !ENABLE_FEATURE_EXTRA_QUIET
/* Hello world */
message(L_CONSOLE | L_LOG, "init started: %s", bb_banner);
#endif

/* Make sure there is enough memory to do something useful. */
if (ENABLE_SWAPONOFF) {
struct sysinfo info;

if (sysinfo(&info) == 0
&& (info.mem_unit ? info.mem_unit : 1) * (long long)info.totalram < 1024*1024
) {
message(L_CONSOLE, "Low memory, forcing swapon");
/* swapon -a requires /proc typically */
new_init_action(SYSINIT, "mount -t proc proc /proc", "");
/* Try to turn on swap */
new_init_action(SYSINIT, "swapon -a", "");
run_actions(SYSINIT); /* wait and removing */
}
}

/* Check if we are supposed to be in single user mode */
//如果命令行没有指定参数,那么argv=0,所以只会执行else语句
if (argv[1]
&& (strcmp(argv[1], "single") == 0 || strcmp(argv[1], "-s") == 0 || LONE_CHAR(argv[1], '1'))
) {
/* ??? shouldn't we set RUNLEVEL="b" here? */
/* Start a shell on console */
new_init_action(RESPAWN, bb_default_login_shell, "");
} else {
/* Not in single user mode - see what inittab says */

/* NOTE that if CONFIG_FEATURE_USE_INITTAB is NOT defined,
* then parse_inittab() simply adds in some default
* actions(i.e., INIT_SCRIPT and a pair
* of "askfirst" shells */
//具体功能如下:
parse_inittab();
}

#if ENABLE_SELINUX
if (getenv("SELINUX_INIT") == NULL) {
int enforce = 0;

putenv((char*)"SELINUX_INIT=YES");
if (selinux_init_load_policy(&enforce) == 0) {
BB_EXECVP(argv[0], argv);
} else if (enforce > 0) {
/* SELinux in enforcing mode but load_policy failed */
message(L_CONSOLE, "can't load SELinux Policy. "
"Machine is in enforcing mode. Halting now.");
exit(EXIT_FAILURE);
}
}
#endif

/* Make the command line just say "init" - thats all, nothing else */
strncpy(argv[0], "init", strlen(argv[0]));
/* Wipe argv[1]-argv[N] so they don't clutter the ps listing */
while (*++argv)
memset(*argv, 0, strlen(*argv));

/* Set up signal handlers */
if (!DEBUG_INIT) {
struct sigaction sa;

bb_signals(0
+ (1 << SIGUSR1) /* halt */
+ (1 << SIGTERM) /* reboot */
+ (1 << SIGUSR2) /* poweroff */
, halt_reboot_pwoff);
signal(SIGQUIT, restart_handler); /* re-exec another init */

/* Stop handler must allow only SIGCONT inside itself */
memset(&sa, 0, sizeof(sa));
sigfillset(&sa.sa_mask);
sigdelset(&sa.sa_mask, SIGCONT);
sa.sa_handler = stop_handler;
/* NB: sa_flags doesn't have SA_RESTART.
* It must be able to interrupt wait().
*/
sigaction_set(SIGTSTP, &sa); /* pause */
/* Does not work as intended, at least in 2.6.20.
* SIGSTOP is simply ignored by init:
*/
sigaction_set(SIGSTOP, &sa); /* pause */

/* SIGINT (Ctrl-Alt-Del) must interrupt wait(),
* setting handler without SA_RESTART flag.
*/
bb_signals_recursive_norestart((1 << SIGINT), record_signo);
}

/* Set up "reread /etc/inittab" handler.
* Handler is set up without SA_RESTART, it will interrupt syscalls.
*/
if (!DEBUG_INIT && ENABLE_FEATURE_USE_INITTAB)
bb_signals_recursive_norestart((1 << SIGHUP), record_signo);


//
/* Now run everything that needs to be run */
/* First run the sysinit command */
run_actions(SYSINIT);
check_delayed_sigs();
/* Next run anything that wants to block */
run_actions(WAIT);
check_delayed_sigs();
/* Next run anything to be run only once */
run_actions(ONCE);

/* Now run the looping stuff for the rest of forever.
*/
while (1) {
int maybe_WNOHANG;

maybe_WNOHANG = check_delayed_sigs();

/* (Re)run the respawn/askfirst stuff */
run_actions(RESPAWN | ASKFIRST);
maybe_WNOHANG |= check_delayed_sigs();

/* Don't consume all CPU time - sleep a bit */
sleep(1);
maybe_WNOHANG |= check_delayed_sigs();

/* Wait for any child process(es) to exit.
*
* If check_delayed_sigs above reported that a signal
* was caught, wait will be nonblocking. This ensures
* that if SIGHUP has reloaded inittab, respawn and askfirst
* actions will not be delayed until next child death.
*/
if (maybe_WNOHANG)
maybe_WNOHANG = WNOHANG;
while (1) {
pid_t wpid;
struct init_action *a;

/* If signals happen _in_ the wait, they interrupt it,
* bb_signals_recursive_norestart set them up that way
*/
wpid = waitpid(-1, NULL, maybe_WNOHANG);
if (wpid <= 0)
break;

a = mark_terminated(wpid);
if (a) {
message(L_LOG, "process '%s' (pid %d) exited. "
"Scheduling for restart.",
a->command, wpid);
}
/* See if anyone else is waiting to be reaped */
maybe_WNOHANG = WNOHANG;
}
} /* while (1) */
}

...............




/* NOTE that if CONFIG_FEATURE_USE_INITTAB is NOT defined,
* then parse_inittab() simply adds in some default
* actions(i.e., runs INIT_SCRIPT and then starts a pair
* of "askfirst" shells). If CONFIG_FEATURE_USE_INITTAB
* _is_ defined, but /etc/inittab is missing, this
* results in the same set of default behaviors.
*/
static void parse_inittab(void)
{
//打开本地配置文件
//在源码中查看inittab的例子,位于\busybox-1.17.2\examples
//inittab格式:<id>:<runlevels>:<action>:<process>
//id=>dev/id,用作终端,stdin,stdout,stderr,printf,scannf,err
//runlevels:可以忽略掉
//action:执行时机 sysinit, respawn, askfirst, wait, once,restart, ctrlaltdel, and shutdown.
//process:应用程序和角本
#if ENABLE_FEATURE_USE_INITTAB
char *token[4];
parser_t *parser = config_open2("/etc/inittab", fopen_for_read);


//如果配置文件为空
if (parser == NULL)
#endif
{
/* No inittab file - set up some default behavior */
/* Reboot on Ctrl-Alt-Del */
new_init_action(CTRLALTDEL, "reboot", "");
/* Umount all filesystems on halt/reboot */
new_init_action(SHUTDOWN, "umount -a -r", "");
/*pc机上的规则:如果系统资源不够的时候,会把当前内存中的应用程序换到硬盘上
*然后把新的程序加入内存
*嵌入式系统中是没有用的。
*/
/* Swapoff on halt/reboot */
if (ENABLE_SWAPONOFF)
new_init_action(SHUTDOWN, "swapoff -a", "");
/* Prepare to restart init when a QUIT is received */
new_init_action(RESTART, "init", "");
/* Askfirst shell on tty1-4 */
new_init_action(ASKFIRST, bb_default_login_shell, "");
//TODO: VC_1 instead of ""? "" is console -> ctty problems -> angry users
//以此为例分析new_init_action函数:
//函数原型:static void new_init_action(uint8_t action_type, const char *command, const char *cons)
//套入真实值: new_init_action(ASKFIRST, "-/bin/sh", "/dev/tty2");
//1 创建一个init action结构,填充
// 2 把这个结构放入init_action_list链表
new_init_action(ASKFIRST, bb_default_login_shell, VC_2);
new_init_action(ASKFIRST, bb_default_login_shell, VC_3);
new_init_action(ASKFIRST, bb_default_login_shell, VC_4);
/* sysinit */
new_init_action(SYSINIT, INIT_SCRIPT, "");
return;
}
//如果配置文件不为空
#if ENABLE_FEATURE_USE_INITTAB
/* optional_tty:ignored_runlevel:action:command
* Delims are not to be collapsed and need exactly 4 tokens
*/
while (config_read(parser, token, 4, 0, "#:",
PARSE_NORMAL & ~(PARSE_TRIM | PARSE_COLLAPSE))) {
/* order must correspond to SYSINIT..RESTART constants */
static const char actions[] ALIGN1 =
"sysinit\0""wait\0""once\0""respawn\0""askfirst\0"
"ctrlaltdel\0""shutdown\0""restart\0";
int action;
char *tty = token[0];

if (!token[3]) /* less than 4 tokens */
goto bad_entry;
action = index_in_strings(actions, token[2]);
if (action < 0 || !token[3][0]) /* token[3]: command */
goto bad_entry;
/* turn .*TTY -> /dev/TTY */
if (tty[0]) {
tty = concat_path_file("/dev/", skip_dev_pfx(tty));
}
new_init_action(1 << action, token[3], tty);
if (tty[0])
free(tty);
continue;
bad_entry:
message(L_LOG | L_CONSOLE, "Bad inittab entry at line %d",
parser->lineno);
}
config_close(parser);
#endif
}




.....................
// 此函数的功能
//1 创建一个init action结构,填充
 // 2 把这个结构放入init_action_list链表
static void new_init_action(uint8_t action_type, const char *command, const char *cons)
{
struct init_action *a, **nextp;

/* Scenario:
* old inittab:
* ::shutdown:umount -a -r
* ::shutdown:swapoff -a
* new inittab:
* ::shutdown:swapoff -a
* ::shutdown:umount -a -r
* On reload, we must ensure entries end up in correct order.
* To achieve that, if we find a matching entry, we move it
* to the end.
*/
nextp = &init_action_list;
while ((a = *nextp) != NULL) {

//如果action已经存在就覆盖掉
/* Don't enter action if it's already in the list,
* This prevents losing running RESPAWNs.
*/
if (strcmp(a->command, command) == 0
&& strcmp(a->terminal, cons) == 0
) {
/* Remove from list */
*nextp = a->next;
/* Find the end of the list */
while (*nextp != NULL)
nextp = &(*nextp)->next;
a->next = NULL;
break;
}
nextp = &a->next;
}
//如果不存在就重新分配内存
if (!a)
a = xzalloc(sizeof(*a));
/* Append to the end of the list */
*nextp = a;
a->action_type = action_type;
safe_strncpy(a->command, command, sizeof(a->command));
safe_strncpy(a->terminal, cons, sizeof(a->terminal));
dbg_message(L_LOG | L_CONSOLE, "command='%s' action=%d tty='%s'\n",
a->command, a->action_type, a->terminal);
}




..............

/* A linked list of init_actions, to be read from inittab */
struct init_action {
struct init_action *next;
pid_t pid;     //进程号
uint8_t action_type; //执行时机
char terminal[CONSOLE_NAME_SIZE]; //终端
char command[COMMAND_SIZE]; //要启动的应用程序
};


=========================================================================
最小根文件系统
1 /dev/console
2 /dev/null
3 /init=>busybox
4 /etc/inittab (配置文件)
5 配置文件中指定的程序
6 c库


======================================================================
配置编译busybox源码
将光盘所带的busybox的源码,复制到指定位置
编译安装方法可以查年根目录下的install文件
部分内容如下:
make menuconfig # This creates a file called ".config"
   make # This creates the "busybox" executable
#如果不是直接编译安装,而是给开发板使用,就需要执行注释中方法
  make install # or make CONFIG_PREFIX=/path/from/root install

1 make menuconfig 常用配置项和需要设置项
  设置交叉编译工具链,
方法一:在makefile文件修改:CROSS_COMPILE ?=arm-linux-
方法二:可以在make menuconfig中设置
busybox settings-->build options-->cross compiler prefix中进行设置


设置tab自动补全
busybox settings-->busybox library tuning-->选中tab completion
压缩命令:archival utilities(无需设置)
模块设置项:linux module utilities(无需设置)
系统设置项:linux system utilities-->mdev (方便构造/dev目录,可以支持热拔插设备)
网络设置项:network utilities
2 make 编译
3 make CONFIG_PREFIX=指定的文件夹目录 install
[root@localhost first_fs]# mkdir first_fs
[root@localhost busybox-1.17.2]# make CONFIG_PREFIX=/mnt/sda3/FriendlyARM/mini6410/linux/first_fs/ install
root@localhost first_fs]# ls
bin linuxrc sbin usr


指定文件夹下会生成以下目录:
bin linuxrc sbin usr


====================================================================
构建一个最小根文件系统
====================================================================
需要以下几个步骤:
1 /dev/console
2 /dev/null
3 /init=>busybox
4 /etc/inittab (配置文件)
5 配置文件中指定的程序
6 c库




1在上一节构建的first_fs中创建/dev/console /dev/null两个文件
查看pc的这两个文件:
[root@localhost rootfs_qtopia_qt4]# ls /dev/console /dev/null -l
crw-------. 1 root root 5, 1 Nov 17 17:26 /dev/console
crw-rw-rw-. 1 root root 1, 3 Nov 17 17:26 /dev/null
创建这两个文件
[root@localhost first_fs]# mkdir dev
[root@localhost first_fs]# cd dev
#c字符设备,5主设备号,1次设备号
[root@localhost dev]# mknod console c 5 1
[root@localhost dev]# ls
[root@localhost dev]# mknod null c 1 3
[root@localhost dev]# ls
console null
2 由于是在安装好的busybox的基础上来作,所以第三步/init=>busybox已经完成
所以直接构建配置文件/etc/inittab
[root@localhost first_fs]# mkdir etc
[root@localhost first_fs]# cd etc/
[root@localhost etc]# vi inittab
[root@localhost etc]# cat inittab
console::askfirst:-/bin/sh
3 第四步可以先不作,直接安装c库
[root@localhost first_fs]# mkdir lib
[root@localhost first_fs]# ls
bin dev etc lib linuxrc sbin usr
[root@localhost first_fs]# cd lib/
[root@localhost lib]# cp /mnt/sda3/FriendlyARM/toolschain/4.5.1/arm-none-linux-gnueabi/lib/*.so* ./


通过以下步骤,一个最小的根文件系统已经构建完成,如果需要烧写到开发板上
需要编译成为一个映象文件


编译成映象文件的过程如下:
1 安装编译工具:可以按照用户手册中的方法安装文件系统映象工具
2 编译:
查看命令选项:
[root@localhost linux]# mkyaffs2image
mkyaffsimage: image building tool for YAFFS built Apr 29 2008
usage: mkyaffsimage dir image_file [convert]
dir the directory tree to be converted
image_file the output file to hold the image
'convert' produce a big-endian image from a little-endian machine
编译生成映象文件:
[root@localhost linux]# mkyaffs2image-128M first_fs first_fs.yaffs2


烧写到开发板
===================================================================
运行ps命令时会不显示结果


原因:虚拟文件系统
在/etc/inittab中添加如下:
::sysinit:/ect/init.d/rcS
创建角本文件
mkdir etc/init.d
vi etc/init.d/rcS添加如下内容:
mount -t proc none /proc
给rcS添加可执行权限
chmod +x etc/init.d/rcS
----------------------------
mount -a也可以实现以上功能


在/etc/inittab中添加如下:
::sysinit:/ect/init.d/rcS
创建角本文件
mkdir etc/init.d
vi etc/init.d/rcS添加如下内容:
mount -a
创建etc/fstab文件
mkdir etc/fstab 添加如下内容:
# device mount-point type option dump fsck order
proc /proc proc defaults 0 0


============================================================
运行ls dev命令不显示结果


udev 自动创建/dev/设备节点
在busybox中有udev的简化版本mdev
具体操作可以参见busybox源码中mdev.txt文件:
  部分内容如下:
  Alternatively, without procfs the above becomes:
[1] mount -t sysfs sysfs /sys
[2] sysctl -w kernel.hotplug=/sbin/mdev
[3] mdev -s


Of course, a more "full" setup would entail executing this before the previous
code snippet:
[4] mount -t tmpfs -o size=64k,mode=0755 tmpfs /dev
[5] mkdir /dev/pts
[6] mount -t devpts devpts /dev/pts


1 mkdir /sys
2 vi etc/fstab 添加如下内容:
# device mount-point type option dump fsck order
sysfs /sys sysfs default 0 0
tmpfs /dev tmpfs default 0 0
3 在etc/init.d/rcS中添加如下内容:
mkdir /dev/pts
mount -t devpts devpts /dev/pts
#热插拔设备时重新执行mdev
echo /sbin/mdev > /proc/sys/kernel/hotplug
mdev -s
=====================================================================
nfs网络文件系统
方法一(先从flash上启动根文件系统,再用命令挂接nfs):
1条件:根文件系统所在目录,需要允许被挂接
在/etc/exports文件中添加:/mnt/sda3/FriendlyARM/mini6410/linux/first_fs.yaffs2 *(rw,sync,no_root_squash)
重启nfs服务
2 开发板挂接根文件系统
mkdir /mnt
mount -t nfs -o rolock 服务器ip:/mnt/sda3/FriendlyARM/mini6410/linux/first_fs /mnt
方法二(直接从nfs启动,需要修改启动参数):
正常的启动参数:
bootargs=root=/dev/mtdblock2 rootfstype=yaffs2 init=/linuxrc console=ttySAC0,115200 androidboot.console=s3c2410_serial0


参数的格式可以从内核源码中查找:Documentation\filesystems\nfs\nfsroot.txt
部分内容如下:
nfsroot=[<server-ip>:]<root-dir>[,<nfs-options>]
ip=<client-ip>:<server-ip>:<gw-ip>:<netmask>:<hostname>:<device>:<autoconf>

需要完成的工作:1 设置服备器ip,根文件目录
2 设置单板的ip 
最终参数:
bootargs=root=/dev/nfs nfsroot=服务器ip:/mnt/sda3/FriendlyARM/mini6410/linux/first_fs ip=开发板ip:服务器ip:网关:子网掩码:可不写:第一个设备eth0:off init=/linuxrc console=ttySAC0,115200 androidboot.console=s3c2410_serial0















  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值