linux内核——8_操作系统启动初始化程序init

作者:GWD 时间:2019.7.31

一、初始化代码
1、起点:磁盘引导程序,需要将内核等移入内存进行运行,并初始化多种模块和硬件
2、终点:运行第一个应用程序——系统的根文件系统
3、程序一开始运行就不断的在创建新的进程,但是如果有别的驱动之类的发生了中断就会去执行别的,执行完中断再回来执行这个初始化,子进程执行shell,父进程等待回收。
二、void init(void)分析
1、

void init(void)
{
	int pid,i;

// 读取硬盘参数包括分区表信息并建立虚拟盘和安装根文件系统设备。
// 该函数是在25 行上的宏定义的,对应函数是sys_setup(),在kernel/blk_drv/hd.c。
	setup((void *) &drive_info);

	(void) open("/dev/tty0",O_RDWR,0);	// 用读写访问方式打开设备“/dev/tty0”,
									// 这里对应终端控制台。
								// 返回的句柄号0 -- stdin 标准输入设备,也就是写。
	(void) dup(0);		// 复制句柄,产生句柄1 号-- stdout 标准输出设备。
	(void) dup(0);		// 复制句柄,产生句柄2 号-- stderr 标准出错输出设备。
// 下面fork()用于创建一个子进程(子任务)。对于被创建的子进程,fork()将返回0 值,
// 对于原(父进程)将返回子进程的进程号。所以if (!(pid=fork())) {...} 内是子进程执行的内容。
// 该子进程关闭了句柄0(stdin),以只读方式打开/etc/rc 文件,并执行/bin/sh 程序,所带参数和
// 环境变量分别由argv_rc 和envp_rc 数组给出。参见后面的描述
//创建了1号进程 如果在0号父进程创建进程成功则 fork函数返回0 ,如果在子进程中fork则返回父进程PID
// 如果fork返回值为0,则其在新进程中执行,如果返回值不为0 返回值为子进程的进程号则在父进程中进行,在前面章节提到过,调用一次fork有两个返回值。
	if (!(pid=fork())) {
//在1号进程中进行执行
		close(0); //关闭了0号进程创建的标准输入输出
		if (open("/etc/rc",O_RDONLY,0)) //打开了系统配置文件,挂接文件系统,开启shell 
//脚本,为什么要读取这个文件呢?因为这里有很多//用户的配置参数,配置参数里面比如要显示图片,挂//接某些程序都会被读出来,放在环境变量里面
			_exit(1);	
// 如果打开文件失败,则退出(/lib/_exit.c);
		execve("/bin/sh",argv_rc,envp_rc);	// 装入/bin/sh 程序并执行。(/lib/execve.c)
		_exit(2);	// 若execve()执行失败则退出(出错码2,“文件或目录不存在”)。
                //正常情况是不会执行到这里的
	}
// 下面是父进程执行的语句。wait()是等待子进程停止或终止,其返回值应是子进程的
// 进程号(pid)。这三句的作用是父进程等待子进程的结束。&i 是存放返回状态信息的
// 位置。如果wait()返回值不等于子进程号,则继续等待。
	if (pid>0)//如果执行失败则等待子进程关闭
		while (pid != wait(&i))
		{	/* nothing */;}

// --
// 如果执行到这里,说明刚创建的子进程的执行已停止或终止了。下面循环中首先再创建
// 一个子进程,如果出错,则显示“初始化程序创建子进程失败”的信息并继续执行。对
// 于所创建的子进程关闭所有以前还遗留的句柄(stdin, stdout, stderr),新创建一个
// 会话并设置进程组号,然后重新打开/dev/tty0 作为stdin,并复制成stdout 和stderr。
// 再次执行系统解释程序/bin/sh。但这次执行所选用的参数和环境数组另选了一套(见上面)。
// 然后父进程再次运行wait()等待。如果子进程又停止了执行,则在标准输出上显示出错信息
//		“子进程pid 停止了运行,返回码是i”,
// 然后继续重试下去…,形成“大”死循环。
	while (1) {
  // 不断创建子进程,如果进程创建失败,重新创建子进程
		if ((pid=fork())<0) {
			printf("Fork failed in init\r\n");
			continue;
		}
   // 在新创建的子进程中执行
		if (!pid) {
//关闭之前的所有控制台
			close(0);close(1);close(2);
			setsid();
//打开新的控制台
			(void) open("/dev/tty0",O_RDWR,0);
			(void) dup(0);
			(void) dup(0);
//执行shell脚本
			_exit(execve("/bin/sh",argv,envp));
//一般情况下是不跳出来的
		}
		while (1)
			if (pid == wait(&i))
				break;
		printf("\n\rchild %d died with code %04x\n\r",pid,i);
		sync();
	}
	_exit(0);	/* NOTE! _exit, not exit() */
}
   


三、操作系统移植的过程分为2步:

  1. 进程操作系统初始化的适配,能够让main在你的板卡上跑起来;
  2. 进程驱动的移植;
    四、接下来看一看3.4.2内核的引导程序主线
    1、首先我们查找start_kernel这个函数被哪个函数调用了:
    在这里插入图片描述
    在mmap中被调用了,然后,mmap被head.s调用
    2、BOOTLOADER的启动内核代码
    创建
    void (theKernel)(int zero, int arch, uint params);
    把指针移到ih_ep上去,Linux的启动入口
    theKernel = (void (
    )(int, int, uint))ntohl(hdr->ih_ep);
    执行Linux并传入参数
    theKernel (0, bd->bi_arch_number, bd->bi_boot_params);
    bd->bi_arch_number 称为process id CPU的架构号
    bd->bi_boot_params 成为参数地址
    3、在bootloader启动内核是如何进行的?thekernel函数指针
    4、比对当前板子的CPU是否支持Linux,如果不支持怎不启动直接退出,如果支持则继续进行
    mrc p15, 0, r9, c0, c0 @ get processor id
    bl __lookup_processor_type @ r5=procinfo r9=cpuid
    movs r10, r5 @ invalid processor (r5=0)?
    THUMB( it eq ) @ force fixup-able long branch encoding
    beq __error_p @ yes, error ‘p’
    5、设置物理内存的页面
    adr r3, 2f
    ldmia r3, {r4, r8}
    sub r4, r3, r4 @ (PHYS_OFFSET - PAGE_OFFSET)
    add r8, r8, r4 @ PHYS_OFFSET

6、验证参数是否完整(tag_list)参数
bl __vet_atags

7、进程虚拟内存映射表的创建
bl __create_page_tables
8、把函数mmap_switched的地址装载进链接寄存器(调用某个函数后执行完后再返回这个函数的地址),mmap_switch函数是拷贝内核的关键因素,虚拟内存到物理内存的交叉。

ldr r13, =__mmap_switched @ address to jump to after
在这里插入图片描述
9、进程初始化C函数的调用,start_kernel中有各种初始化
asmlinkage void __init start_kernel(void)
在rest_init()中;创建了kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND)进程这句话相当于0.11中的fork 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);
最后又执行了:
init_post();
run_init_process("/sbin/init");
run_init_process("/etc/init");
run_init_process("/bin/init");
run_init_process("/bin/sh");

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值