OpenWRT 启动流程(三) /sbin/procd 分析

本系列导航:
OpenWRT 启动流程(一) /sbin/init 进程分析
OpenWRT 启动流程(二) /etc/preinit 脚本分析
OpenWRT 启动流程(三) /sbin/procd 分析

在 /sbin/init 执行完后,调用了 spawn_procd 函数 ,启动了 procd 进程。

static void
spawn_procd(struct uloop_process *proc, int ret)
{
	char *wdt_fd = watchdog_fd();
	char *argv[] = { "/sbin/procd", NULL};
	char dbg[2];
	//......
	execvp(argv[0], argv);
}

procd 代码位于 procd-2018-03-28-dfb68f85\procd.c

跟踪代码:

main
  procd_state_next
    state_enter
      

这里主要来看 state_enter

static void state_enter(void)
{
	char ubus_cmd[] = "/sbin/ubusd";

	switch (state) {
	case STATE_EARLY:
		LOG("- early -\n");
		watchdog_init(0);
		hotplug("/etc/hotplug.json");
		procd_coldplug();
		break;

	case STATE_UBUS:
		// try to reopen incase the wdt was not available before coldplug
		watchdog_init(0);
		set_stdio("console");
		LOG("- ubus -\n");
		procd_connect_ubus();
		service_start_early("ubus", ubus_cmd);
		break;

	case STATE_INIT:
		LOG("- init -\n");
		procd_inittab();
		procd_inittab_run("respawn");
		procd_inittab_run("askconsole");
		procd_inittab_run("askfirst");
		procd_inittab_run("sysinit");

		// switch to syslog log channel
		ulog_open(ULOG_SYSLOG, LOG_DAEMON, "procd");
		break;

	case STATE_RUNNING:
		LOG("- init complete -\n");
		procd_inittab_run("respawnlate");
		procd_inittab_run("askconsolelate");
		break;

	case STATE_SHUTDOWN:
		/* Redirect output to the console for the users' benefit */
		set_console();
		LOG("- shutdown -\n");
		procd_inittab_run("shutdown");
		sync();
		break;

	case STATE_HALT:
		// To prevent killed processes from interrupting the sleep
		signal(SIGCHLD, SIG_IGN);
		LOG("- SIGTERM processes -\n");
		kill(-1, SIGTERM);
		sync();
		sleep(1);
		LOG("- SIGKILL processes -\n");
		kill(-1, SIGKILL);
		sync();
		sleep(1);
#ifndef DISABLE_INIT
		if (reboot_event == RB_POWER_OFF)
			LOG("- power down -\n");
		else
			LOG("- reboot -\n");

		/* Allow time for last message to reach serial console, etc */
		sleep(1);

		/* We have to fork here, since the kernel calls do_exit(EXIT_SUCCESS)
		 * in linux/kernel/sys.c, which can cause the machine to panic when
		 * the init process exits... */
		if (!vfork( )) { /* child */
			reboot(reboot_event);
			_exit(EXIT_SUCCESS);
		}
		while (1)
			sleep(1);
#else
		exit(0);
#endif
		break;

	default:
		ERROR("Unhandled state %d\n", state);
		return;
	};
}
  • case STATE_EARLY
    这里主要是 调用 hotplug procd_coldplug ,自动创建设备节点,生成/dev/xxx

  • case STATE_UBUS
    初始化 ubus 连接

  • case STATE_INIT
    读取 /etc/inittab

    ::sysinit:/etc/init.d/rcS S boot
    ::shutdown:/etc/init.d/rcS K shutdown
    tts/0::askfirst:/usr/libexec/login.sh
    ttyS0::askfirst:/usr/libexec/login.sh
    tty1::askfirst:/usr/libexec/login.sh
    
    
    void procd_inittab(void)
      if (add_action(a, tags[TAG_ACTION]))
          for (i = 0; i < ARRAY_SIZE(handlers); i++)   // 见 handlers 定义
            if (!strcmp(handlers[i].name, name)) {
       	    a->handler = &handlers[i];
       	    list_add_tail(&a->list, &actions);
       	    return 0;
           }
    // handlers 定义	    
    static struct init_handler handlers[] = {
    {
       .name = "sysinit",
       .cb = runrc,
    }, {
       .name = "shutdown",
       .cb = runrc,
    }, {
       .name = "askfirst",
       .cb = askfirst,
       .multi = 1,
    }
       //...
    }
    static void runrc(struct init_action *a)
    {
       if (!a->argv[1] || !a->argv[2]) {
       	ERROR("valid format is rcS <S|K> <param>\n");
       	return;
       }
    
       /* proceed even if no init or shutdown scripts run */
       if (rcS(a->argv[1], a->argv[2], rcdone))
       	rcdone(NULL);
    }
    int rcS(char *pattern, char *param, void (*q_empty)(struct runqueue *))
    {
       runqueue_init(&q);
       q.empty_cb = q_empty;
       q.max_running_tasks = 1;
    
       return _rc(&q, "/etc/rc.d", pattern, "*", param);
    }
    

    最终其实就是 读取 /etc/rc.d/ 下的启动脚本,逐个执行。 /etc/rc.d/ 目录文件实例:

    root@172:/etc/rc.d# ls |sort
    K10gpio_switch
    K10mysqld
    K10openvpn
    K50dropbear
    K50lighttpd
    K81redis
    K85odhcpd
    K89log
    K90network
    K90sysfixtime
    K98boot
    K99umount
    S00sysfixtime
    S10boot
    S10system
    S11sysctl
    S12log
    S19dnsmasq
    S19firewall
    S20network
    S35odhcpd
    S36asterisk
    S50cron
    S50dropbear
    S50lighttpd
    S50odbc
    S50php5-fastcgi
    S50php5-fpm
    S50yate
    S90openvpn
    S94gpio_switch
    S95done
    S95mysqld
    S96led
    S98redis
    S98sysntpd
    S99urandom_seed
    
  • case STATE_RUNNING:
    初始化完成

  • case STATE_SHUTDOWN:
    暂时不细究

  • case STATE_HALT:
    暂时不细究

总结:

/sbin/procd 会读取 /etc/inittab ,然后执行 /etc/rc.d/目录下的应用启动脚本。

发布了63 篇原创文章 · 获赞 20 · 访问量 7万+

猜你喜欢

转载自blog.csdn.net/agave7/article/details/99310101