四、openwrt 嵌入式系统启动流程

嵌入式系统启动流程分析

  • 目的:通过分析启动流程,了解各类嵌入式系统的启动方法、启动步骤、代谢文件、达到什么目的、逐步掌握分析内核源码的能力

Linux 启动流程

  • Bootloader (Uboot):启动引导代码
    • Bootloader 是 与内核不能同时存在的,在内核
      启动完成后Bootloader 会关闭
      
    • Bootloader 会初始化内核所运行需要的硬件环境,
        比如初始化CPU、 串口,其实是对硬件的初始化
      
    • 	为什么刚开始不是用C语言?
      
      • 	 以为C语言需要用到栈 、堆,刚开始没有栈、堆
        
    • CPU初始化
      
    • 内存初始化
      
    • 硬盘初始化
      
    • 代码搬移
      
    • 各类硬件的初始化
      
    • 进行参数设定 ---->struct tag_ist  struct  tag  .
      形成的  tsg_list 会存放在固定的内存里
      
  •   Bootloader 怎么给 内核传递参数?
    
    •   	Bootloader设置参数的格式是与内核解析
        	 的参数的格式是一致的
      
    •   	Bootloader 会把初始化的参数 放在一个固定的
        	内存里, 内核去 固定的内存里找就可以
      
可以修改传递的 一些命令,来达到内核启动后要执行的一些功能
struct tag{
	struct tag_header hdr;
	union{
		struct tag_core core;
		struct tag_mem_range mem_range;
		struct tag_cmdline cmdline; //命令
		struct tag_clock clock;
		struct tag_ethernet ethernet
	}u;
};

  • 启动内核 、

  • 0.11内核代码里 的boot 文件下有下面三个文件

  • 在这里插入图片描述

  • 其中 bootsect.s 放在磁盘的第一个扇区里,类型nor flash前4k 内存一样

  • setup .s 是 内核解析当前BootLoader 传递进来的参数,并且赋给某些全局变量

  • head.s 是执行当前 内核的头,从内核的头开始执行,从 main函数 开始运行

  • 完成内存划分在这里插入图片描述

  • 完成各模块的启动功能(main.c)

#define BCD_TO_BIN(val) ((val)=((val)&15) + ((val)>>4)*10)

//系统时间
static void time_init(void)
{
	struct tm time;
    //从rtc芯片中读取开机的时间
	do {
		time.tm_sec = CMOS_READ(0);
		time.tm_min = CMOS_READ(2);
		time.tm_hour = CMOS_READ(4);
		time.tm_mday = CMOS_READ(7);
		time.tm_mon = CMOS_READ(8);
		time.tm_year = CMOS_READ(9);
	} while (time.tm_sec != CMOS_READ(0));
  //时间进制的转化
	BCD_TO_BIN(time.tm_sec);
	BCD_TO_BIN(time.tm_min);
	BCD_TO_BIN(time.tm_hour);
	BCD_TO_BIN(time.tm_mday);
	BCD_TO_BIN(time.tm_mon);
	BCD_TO_BIN(time.tm_year);
	time.tm_mon--;
  //计算开机时间
	startup_time = kernel_mktime(&time);
}

static long memory_end = 0;
static long buffer_memory_end = 0;
static long main_memory_start = 0;

struct drive_info { char dummy[32]; } drive_info;

void main(void)		/* This really IS void, no error here. */
{			/* The startup routine assumes (well, ...) this */
/*
	 * Interrupts are still disabled. Do necessary setups, then
	 * enable them
	 */
	 	//设置操作系统的根文件
		ROOT_DEV = ORIG_ROOT_DEV;
		// 设置操作系统驱动参数 
		drive_info = DRIVE_INFO;
		// 解析setup.s代码后获取系统内存参数
		//做内存镜像的映射
		memory_end = (1<<20) + (EXT_MEM_K<<10);
    // 4K取整
		memory_end &= 0xfffff000;
    // 当前Linux内核最大支持内存为16M
		if (memory_end > 16*1024*1024)
			memory_end = 16*1024*1024;
    //确定高速缓冲区的内存大小
		if (memory_end > 12*1024*1024) 
			buffer_memory_end = 4*1024*1024;
		else if (memory_end > 6*1024*1024)
			buffer_memory_end = 2*1024*1024;
		else
			buffer_memory_end = 1*1024*1024;

    
		main_memory_start = buffer_memory_end;

    
	#ifdef RAMDISK
		main_memory_start += rd_init(main_memory_start, RAMDISK*1024);
	#endif


		// 进行用户内存的初始化
		mem_init(main_memory_start,memory_end);
		// 系统中断初始化(已讲)
		trap_init();
	      //进行块设备驱动的初始化,加载块设备驱动
		blk_dev_init();
	    // 进行字符型设备驱动的初始化,加载字符型设备驱动
		chr_dev_init();
	  //进行控制台设备的初始化,加载显示和传输设备的驱动
		tty_init();
	  //初始化系统时钟
		time_init();
	  //进行进程调度初始化(已讲)
		sched_init();
	  //进行缓冲区初始化(已讲)
		buffer_init(buffer_memory_end);
	  //进行硬盘设备的初始化,加载硬盘驱动
		hd_init();
	  //进行软盘设备的初始化,加载软盘驱动
		floppy_init();

    
	  // 开启中断
		sti();
	// 从内核的初始化状态切换到用户模式
		move_to_user_mode();

  
	//创建0号进程  运行最初的应用程序
	if (!fork()) {		/* we count on this going ok */
		init();
	}
/*
 *   NOTE!!   For any other task 'pause()' would mean we have to get a
 * signal to awaken, but task0 is the sole exception (see 'schedule()')
 * as task 0 gets activated at every idle moment (when no other tasks
 * can run). For task0 'pause()' just means we go check if some other
 * task can run, and if not we return here.
 */
		for(;;) pause();
}

static int printf(const char *fmt, ...)
{
	va_list args;
	int i;

	va_start(args, fmt);
	write(1,printbuf,i=vsprintf(printbuf, fmt, args));
	va_end(args);
	return i;
}

static char * argv_rc[] = { "/bin/sh", NULL };
static char * envp_rc[] = { "HOME=/", NULL };

static char * argv[] = { "-/bin/sh",NULL };
static char * envp[] = { "HOME=/usr/root", NULL };

void init(void)
{
	int pid,i;
  //运行setup.s程序,进行配置
  //获取由setup程序进行解析的参数
	setup((void *) &drive_info);
  //open标准输入的文件句柄
	(void) open("/dev/tty0",O_RDWR,0);   //打开标准输入控制台
	(void) dup(0); //打开标准输出控制台
	(void) dup(0); //打开标准错误控制台
	//make
	mkdir("/proc",755);
    mknod("/proc/sysinfo", S_IFPROC,8 );

    
	printf("%d buffers = %d bytes buffer space\n\r",NR_BUFFERS,
		NR_BUFFERS*BLOCK_SIZE);
	printf("Free mem: %d bytes\n\r",memory_end-main_memory_start);
  //创建1号进程
	if (!(pid=fork())) {
		close(0);
		if (open("/etc/rc",O_RDONLY,0))
			_exit(1);
		execve("/bin/sh",argv_rc,envp_rc); //如果成功启动则阻塞
		_exit(2);
		
	}
	if (pid>0)
		while (pid != wait(&i)) //做循环等待
   

    //如果启动不正常,出错状态
			/* nothing */;
	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); 
			_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() */
}

  • 切换运行态
  • fork 多进程进行第一个进程的创建,名字为 init ,PID=1
    • 获得标准输出 标准错误 标准输入

Openwrt 的启动流程

  • 1.openwrt 启动的第一个进程为 /etc/preinit (/package/base-files/files/etc/preinit)
  • 我们打开此文件看,它是个shell脚本
  • 里面的 $PREINIT 刚开始没有被定义在这里插入图片描述
  • 2.启动 /sbin/init,进行环境变量设置 、文件系统的挂接、进行所有内核模块的加载,创建两个进程
  • /sbin/init 会执行这个进程,它做了初始化的工作,如挂载
    在这里插入图片描述
  • 挂接 /etc/fstab
  • <file system> <mount point> <type> <options> <dump> <pass>
    
  • 3./sbin/procd(/build_dir/target-i386_pentium4_mus1-1.1.16/procd-2017-08-08-66be6a23) PREINIT变量进行设置
  • 4./sbin/kmodloader 加载内核模块
  • 5./etc/inittab 初始化文件系统
  • /etc/init.d/ 系统的开机启动脚本,各类软件的开机启动脚本都会放在这
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值