• busybox(一)浅析



    title: busybox(一)浅析
    tag: arm
    date: 2018-11-13 23:02:33

    busybox(一)浅析


    源码包在busybox-1.7.0.tar.bz2,一个命令对应着一个c文件,执行init命令,则是有init.c,有函数init_main

    int init_main(int argc, char **argv);
    

    最终的目的是启动客户的应用程序,需要指定具体的环境

    1. 配置文件读取
    2. 解析配置文件
    3. 执行用户程序
    

    help

    相关的帮助可以搜索下/examples下的文件,比如搜索inittab,里面有相关说明

    #define SYSINIT     0x001		//执行一次等待结束后从链表删除
    #define RESPAWN     0x002		//while循环执行
    #define ASKFIRST    0x004		//while循环执行,会打印信息,等待回车
    #define WAIT        0x008		//执行一次等待结束后从链表删除,在SYSINIT后
    #define ONCE        0x010		//与SYSINIT 区别在于不等待其执行结束
    #define CTRLALTDEL  0x020		//输入信号后执行
    #define SHUTDOWN    0x040		//输入信号后执行
    #define RESTART     0x080		//输入信号后执行
    

    流程图

    mark

    引入

    init_main函数入口分析,Linux 是按照run_init_process("/sbin/init");形式调用,没有传递参数,所以执行else分支,解析配置表

    if (argc > 1
     && (!strcmp(argv[1], "single") || !strcmp(argv[1], "-s") || LONE_CHAR(argv[1], '1'))
    ) {
    	/* 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., runs INIT_SCRIPT and then starts a pair
    	 * of "askfirst" shells */
    	parse_inittab();
    }
    

    读取inittab

    parse_inittab();读取inittab配置表,可以搜索下example下查看例子帮助,查阅如下格式

    Format for each entry: <id>:<runlevels>:<action>:<process>
    <id>: WARNING: This field has a non-traditional meaning for BusyBox init!
    <runlevels>: The runlevels field is completely ignored.
    <action>: Valid actions include: sysinit, respawn, askfirst, wait, once,restart, ctrlaltdel, and shutdown.
    <process>: Specifies the process to be executed and it's command line.
    
    标识 作用
    id 自动加上/dev/的前缀,用作终端,stdin,stdout,stderr:printf,scanf,err 可以省略
    runlevels 可以忽略
    action 指示何止执行
    process 应用程序或者可执行脚本

    最终执行new_init_action运行脚本程序

    默认的配置表读取

    #define INITTAB      "/etc/inittab"	/* inittab file location */
    static void parse_inittab(void)
    {
    	file = fopen(INITTAB, "r");
    }
    	
    

    创建执行脚本链表

    
    for (a = actions; a->name != 0; a++) {
    	if (strcmp(a->name, action) == 0) {
    		if (*id != '') {
    			if (strncmp(id, "/dev/", 5) == 0)	//这里为id加上/dev/的前缀
    				id += 5;
    			strcpy(tmpConsole, "/dev/");
    			safe_strncpy(tmpConsole + 5, id,
    				sizeof(tmpConsole) - 5);
    			id = tmpConsole;
    		}
    		new_init_action(a->action, command, id);
    		break;
    	}
    }
    

    当不存在这个配置表的时候也会有一个默认配置文件,这里以默认的其中一个脚本解析

    new_init_action(ASKFIRST, bb_default_login_shell, VC_2);
    
    # define VC_2 "/dev/tty2"
    #define ASKFIRST    0x004
    const char bb_default_login_shell[] ALIGN1 = LIBBB_DEFAULT_LOGIN_SHELL;
    #define LIBBB_DEFAULT_LOGIN_SHELL      "-/bin/sh"
    

    也就是最终执行

    new_init_action(ASKFIRST, "-/bin/sh", "/dev/tty2");
    ASKFIRST 执行的时机
    -/bin/sh 脚本程序
    /dev/tty2 id终端,加上了/dev/,符合上述描述
    

    分析下new_init_action函数内部,

    1. 创建 init_action结构,包含inittab中的信息
    2. 加入到init_action_list链表中
    for (a = last = init_action_list; a; a = a->next) {
    	/* don't enter action if it's already in the list,
    	 * but do overwrite existing actions */
    	if ((strcmp(a->command, command) == 0)
    	 && (strcmp(a->terminal, cons) == 0)
    	) {
    		a->action = action;
    		return;
    	}
    	last = a;
    }
    
    struct init_action {
    	struct init_action *next;
    	int action;		
    	pid_t pid;								//对应进程id, process id
    	char command[INIT_BUFFS_SIZE];			//对应应用程序 
    	char terminal[CONSOLE_NAME_SIZE];		//对应终端
    };
    
    

    由此,可以理解当配置文件不存在的时候,会去创建配置表

    	#define INITTAB      "/etc/inittab"	/* inittab file location */
    	file = fopen(INITTAB, "r");
    
    	if (file == NULL) {
    		/* 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", "");
    		/* Swapoff on halt/reboot */
    		if (ENABLE_SWAPONOFF) new_init_action(SHUTDOWN, "swapoff -a", "");
    		/* Prepare to restart init when a HUP is received */
    		new_init_action(RESTART, "init", "");
    		/* Askfirst shell on tty1-4 */
    		new_init_action(ASKFIRST, bb_default_login_shell, "");
    		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;
        }
    >>>>>>>>>>>>>>>>>>>>>>>>>>>>>↓
    创建类似的inittatb             ↓
    >>>>>>>>>>>>>>>>>>>>>>>>>>>>>↓
    ::CTRLALTDEL:reboot
    ::SHUTDOWN:umount -a -r
    ::RESTART:init
    ::ASKFIRST:-/bin/sh:
    tty2::ASKFIRST:-/bin/sh
    tty3::ASKFIRST:-/bin/sh
    tty4::ASKFIRST:-/bin/sh
    ::SYSINIT:/etc/init.d/rcS
    

    执行脚本

    脚本有多种类型,不同类型执行方式与时机不同

    #define SYSINIT     0x001		//执行一次等待结束后从链表删除
    #define RESPAWN     0x002		//while循环执行
    #define ASKFIRST    0x004		//while循环执行,会打印信息,等待回车
    #define WAIT        0x008		//执行一次等待结束后从链表删除,在SYSINIT后
    #define ONCE        0x010		//与SYSINIT 区别在于不等待其执行结束
    #define CTRLALTDEL  0x020		//输入信号后执行
    #define SHUTDOWN    0x040		//输入信号后执行
    #define RESTART     0x080		//输入信号后执行
    
    run_actions(SYSINIT);
       				waitfor(a, 0);//执行a,等待执行结束
       					run(a);//执行创建process子进程
       					 waitpid(runpid, &status, 0);
    				delete_init_action(a);//删除链表
    	/* Next run anything that wants to block */
    	run_actions(WAIT);
    	   				waitfor(a, 0);//执行a,等待执行结束
       					run(a);//执行创建process子进程
       					 waitpid(runpid, &status, 0);
    				delete_init_action(a);//删除链表
    	/* Next run anything to be run only once */
    	run_actions(ONCE);
    					run(a);
    					delete_init_action(a);
    		/* Now run the looping stuff for the rest of forever */
    	while (1) {//重新运行pid已经退出的子进程
    		run_actions(RESPAWN);
    				if (a->pid == 0) {  //默认pid为0
    					a->pid = run(a);}
    					
    		run_actions(ASKFIRST);
    				if (a->pid == 0) {
    					a->pid = run(a);}
    					//打印"
    Please press Enter to activate this console. ";,
    					//等待输入回车
    					//创建子进程
    		wpid = wait(NULL);//等待子进程退出
    		while (wpid > 0) {
    				a->pid--;//推出后pid=0
    			}
    		}
    	}
    

    小结

    1. 打开终端 dev/console
    2. 打开dev/null ,用作不设置终端id的时候的定位
    3. 读取配置文件etc/inittab ,需要存在配置文件的可执行程序或者脚本
    4. 执行脚本

    所以一个最小的根文件系统必备的一些资源

    dev/console
    dev/null 
    sbin/init-------------busybox提供,至少需要这个应用程序,这是linux启动的第一个应用程序
    etc/inittab-----------配置文件,定义了一些应用程序 
    配置文件制定的应用程序----配置文件指定的应用程序
    C库--------------------应用程序的C库
    
  • 相关阅读:
    Net中应用 Redis 扩展类
    【ASP.Net MVC3 】使用Unity 实现依赖注入
    NET平台常用的框架整理
    ABP 基于DDD的.NET开发框架 学习(三)创建模块:任务管理
    ABP 基于DDD的.NET开发框架 学习(二)创建实体
    ABP 基于DDD的.NET开发框架 学习(一)
    生成二维码图片
    淘宝图片指纹匹配功能c#实现
    webAPI中“System.Web.Http.HttpConfiguration”不包含“EnableSystemDiagnosticsTracing”的定义解决办法
    NuGet包 安装相关指令
  • 原文地址:https://www.cnblogs.com/zongzi10010/p/10023707.html
Copyright © 2020-2023  润新知