Linux内核执行start_kernel函数时会调用kernel_init来启动init进程,流程如下图:

graph LR A[start_kernel] -->B(rest_init) B --> C(kernel_init) C --> D[try_to_run_init_process]

kernel_init部分代码如下:

 994     if (execute_command) {995         ret = run_init_process(execute_command);996         if (!ret)997             return 0;998         panic("Requested init %s failed (error %d).",999               execute_command, ret);
1000     }
1001     if (!try_to_run_init_process("/sbin/init") ||
1002         !try_to_run_init_process("/etc/init") ||
1003         !try_to_run_init_process("/bin/init") ||
1004         !try_to_run_init_process("/bin/sh"))
1005         return 0;
1006
1007     panic("No working init found.  Try passing init= option to kernel. "
1008           "See Linux Documentation/init.txt for guidance.");

接着分析openwrtpackage/system/procd/Makefile,这里将procd源码编译生成的可执行文件安装到文件系统的*/sbin*目录中。

define Package/procd/install$(INSTALL_DIR) $(1)/sbin $(1)/etc $(1)/lib/functions$(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/{init,procd,askfirst,udevtrigger} $(1)/sbin/$(INSTALL_DATA) $(PKG_INSTALL_DIR)/usr/lib/libsetlbf.so $(1)/lib$(INSTALL_BIN) ./files/reload_config $(1)/sbin/$(INSTALL_DATA) ./files/hotplug*.json $(1)/etc/$(INSTALL_DATA) ./files/procd.sh $(1)/lib/functions/
endef

查看procd源码目录的CMakeList.txt,以init为例,对应源码编译文件如下

 56 IF(DISABLE_INIT)57 ADD_DEFINITIONS(-DDISABLE_INIT)58 ELSE()59 ADD_EXECUTABLE(init initd/init.c initd/early.c initd/preinit.c initd/mkdev.c watchdog.c60     utils/utils.c ${SOURCES_ZRAM})61 TARGET_LINK_LIBRARIES(init ${LIBS})62 INSTALL(TARGETS init63     RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR}64 )6566 ADD_EXECUTABLE(udevtrigger plug/udevtrigger.c)67 INSTALL(TARGETS udevtrigger68     RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR}69 )70 ENDIF()

main函数入口位于initd/init.c

int
main(int argc, char **argv)
{pid_t pid;ulog_open(ULOG_KMSG, LOG_DAEMON, "init");sigaction(SIGTERM, &sa_shutdown, NULL);sigaction(SIGUSR1, &sa_shutdown, NULL);sigaction(SIGUSR2, &sa_shutdown, NULL);early();cmdline();watchdog_init(1);pid = fork();if (!pid) {char *kmod[] = { "/sbin/kmodloader", "/etc/modules-boot.d/", NULL };if (debug < 3)patch_stdio("/dev/null");execvp(kmod[0], kmod);ERROR("Failed to start kmodloader\n");exit(-1);}if (pid <= 0) {ERROR("Failed to start kmodloader instance\n");} else {int i;for (i = 0; i < 1200; i++) {if (waitpid(pid, NULL, WNOHANG) > 0)break;usleep(10 * 1000);watchdog_ping();}}uloop_init();preinit();uloop_run();return 0;
}

uloop_init实现位于libubox源码uloop.c

int uloop_init(void)
{if (uloop_init_pollfd() < 0)return -1;if (waker_init() < 0) {uloop_done();return -1;}return 0;
}static int uloop_init_pollfd(void)
{if (poll_fd >= 0)return 0;poll_fd = epoll_create(32); if (poll_fd < 0)return -1;fcntl(poll_fd, F_SETFD, fcntl(poll_fd, F_GETFD) | FD_CLOEXEC);return 0;
}

preinit实现位于procd源码文件initd/preinit.c

static struct uloop_process preinit_proc;
static struct uloop_process plugd_proc;void
preinit(void)
{char *init[] = { "/bin/sh", "/etc/preinit", NULL };char *plug[] = { "/sbin/procd", "-h", "/etc/hotplug-preinit.json", NULL };int fd;LOG("- preinit -\n");plugd_proc.cb = plugd_proc_cb;plugd_proc.pid = fork();if (!plugd_proc.pid) {execvp(plug[0], plug);ERROR("Failed to start plugd\n");exit(-1);}if (plugd_proc.pid <= 0) {ERROR("Failed to start new plugd instance\n");return;}uloop_process_add(&plugd_proc);  setenv("PREINIT", "1", 1);fd = creat("/tmp/.preinit", 0600);if (fd < 0)ERROR("Failed to create sentinel file\n");elseclose(fd);preinit_proc.cb = spawn_procd;preinit_proc.pid = fork();if (!preinit_proc.pid) {execvp(init[0], init);ERROR("Failed to start preinit\n");exit(-1);}if (preinit_proc.pid <= 0) {ERROR("Failed to start new preinit instance\n");return;}uloop_process_add(&preinit_proc);DEBUG(4, "Launched preinit instance, pid=%d\n", (int) preinit_proc.pid);
}

这里fork出2个子进程,执行procd和*/etc/preinit*,

  • 首先看procd,因为带有参数*“-h /etc/hotplug-preinit.json”,所以会执行hotplug_run*函数。
int main(int argc, char **argv)
{int ch;char *dbglvl = getenv("DBGLVL");int ulog_channels = ULOG_KMSG;if (dbglvl) {debug = atoi(dbglvl);unsetenv("DBGLVL");}while ((ch = getopt(argc, argv, "d:s:h:S")) != -1) {switch (ch) {case 'h':return hotplug_run(optarg);case 's':ubus_socket = optarg;break;case 'd':debug = atoi(optarg);break;case 'S':ulog_channels = ULOG_STDIO;break;default:return usage(argv[0]);}}ulog_open(ulog_channels, LOG_DAEMON, "procd");setsid();uloop_init();procd_signal();if (getpid() != 1)procd_connect_ubus();elseprocd_state_next();uloop_run();uloop_done();return 0;
}

hotplug实现如下,这里是建立netlink通信机制,完成用户层和内核的交互,监听内核的uevent事件。

void hotplug(char *rules)
{struct sockaddr_nl nls;int nlbufsize = 512 * 1024;rule_file = strdup(rules);memset(&nls,0,sizeof(struct sockaddr_nl));nls.nl_family = AF_NETLINK;nls.nl_pid = getpid();nls.nl_groups = -1;if ((hotplug_fd.fd = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_KOBJECT_UEVENT)) == -1) {ERROR("Failed to open hotplug socket: %s\n", strerror(errno));exit(1);}if (bind(hotplug_fd.fd, (void *)&nls, sizeof(struct sockaddr_nl))) {ERROR("Failed to bind hotplug socket: %s\n", strerror(errno));exit(1);}if (setsockopt(hotplug_fd.fd, SOL_SOCKET, SO_RCVBUFFORCE, &nlbufsize, sizeof(nlbufsize)))ERROR("Failed to resize receive buffer: %s\n", strerror(errno));json_script_init(&jctx);queue_proc.cb = queue_proc_cb;uloop_fd_add(&hotplug_fd, ULOOP_READ);
}int hotplug_run(char *rules)
{uloop_init();hotplug(rules);uloop_run();return 0;
}
  • /etc/preinit脚本大致内容如下,先调用另外的shell脚本,获取函数定义
. /lib/functions.sh
. /lib/functions/preinit.sh
. /lib/functions/system.sh# 初始化hook链
boot_hook_init preinit_essential
boot_hook_init preinit_main
boot_hook_init failsafe
boot_hook_init initramfs# 依次执行/lib/preinit目录中的脚本,将函数调用添加到hook链中
for pi_source_file in /lib/preinit/*; do. $pi_source_file
done# 执行preinit_essential注册的hook链的所有函数
boot_run_hook preinit_essential# 执行preinit_main注册的hook链的所有函数
boot_run_hook preinit_main

lib/functions/preinit.sh中定义

boot_hook_init() {local hook="${1}_hook"export -n "PI_STACK_LIST=${PI_STACK_LIST:+$PI_STACK_LIST }$hook"export -n "$hook="
}

/lib/preinit/10_sysinfo中添加hook函数

boot_hook_add preinit_main do_sysinfo_generic

/etc/preinit脚本执行完成后,调用spawn_procd

static void
spawn_procd(struct uloop_process *proc, int ret)
{char *wdt_fd = watchdog_fd();char *argv[] = { "/sbin/procd", NULL};struct stat s;char dbg[2];if (plugd_proc.pid > 0)kill(plugd_proc.pid, SIGKILL);if (!stat("/tmp/sysupgrade", &s))while (true)sleep(1);unsetenv("INITRAMFS");unsetenv("PREINIT");unlink("/tmp/.preinit");DEBUG(2, "Exec to real procd now\n");if (wdt_fd)setenv("WDTFD", wdt_fd, 1);check_dbglvl();if (debug > 0) {snprintf(dbg, 2, "%d", debug);setenv("DBGLVL", dbg, 1);}//调用procdexecvp(argv[0], argv);
}

此时getpid()等于1,所以调用procd_state_next,进入到状态机处理中。

对应的procd log如下,procd state不断迁移,包括STATE_EARLYSTATE_UBUSSTATE_INIT等。

[    3.161338@3] init: Console is alive
[    3.173921@3] init: Ping
[    3.184207@3] init: Ping
[    3.192558@1] kmodloader: loading kernel modules from /etc/modules-boot.d/*
[    3.194447@3] init: Ping
[    3.196209@1] kmodloader: done loading kernel modules from /etc/modules-boot.d/*
[    3.204716@3] init: Ping
[    3.206180@3] init: - preinit -
[    3.208671@3] init: Launched preinit instance, pid=1308
[    3.302967@3] init: Exec to real procd now
[    3.308865@3] procd: - early -
[    3.524654@2] procd: Finished udevtrigger
[    4.024929@2] procd: Coldplug complete
[    4.028061@2] procd: - ubus -
[    4.029198@2] procd: Create service ubus
[    4.030829@2] procd: Create instance ubus::instance1
[    4.032109@2] procd: Started instance ubus::instance1[1554]
[    4.098895@2] procd: Connected to ubus, id=459ede6c
[    4.099092@2] procd: - init -
[    4.102474@2] procd: Launched new askconsole action, pid=1555
[    4.104142@2] procd: Launched new askfirst action, pid=1556

STATE_INIT为例,执行procd_inittab_run("xxx")会调用对应handlers的callback,对应所有的init_action是在*procd_inittab()*中添加的。

    case STATE_INIT:LOG("- init -\n");procd_inittab();procd_inittab_run("respawn");procd_inittab_run("askconsole");procd_inittab_run("askfirst");procd_inittab_run("sysinit");static struct init_handler handlers[] = {{.name = "sysinit",.cb = runrc,}, {.name = "shutdown",.cb = runrc,}, {.name = "askfirst",.cb = askfirst,.multi = 1,}, {.name = "askconsole",.cb = askconsole,.multi = 1,}, {.name = "respawn",.cb = rcrespawn,.multi = 1,}
};
void procd_inittab_run(const char *handler)
{struct init_action *a;list_for_each_entry(a, &actions, list) {if (!strcmp(a->handler->name, handler)) {if (a->handler->multi) {a->handler->cb(a);continue;}a->handler->cb(a);break;}}
}

这里来看runrc的实现,代码位于inittab.c

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)) {printf("---rcdone---\n");rcdone(NULL);} else {printf("----rcdone error\n");}
}

rcS.c

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*目录下S/K开头的脚本

转载于:https://www.cnblogs.com/tinylaker/p/10573390.html

openwrt procd启动流程和脚本分析相关推荐

  1. 【Android 插件化】Hook 插件化框架 ( Hook Activity 启动流程 | Hook 点分析 )

    Android 插件化系列文章目录 [Android 插件化]插件化简介 ( 组件化与插件化 ) [Android 插件化]插件化原理 ( JVM 内存数据 | 类加载流程 ) [Android 插件 ...

  2. uboot启动流程概述_uboot 分析之 启动流程

    uboot的启动流程: 看一幅图: 1.第一阶段:start.s的内容: 点击(此处)折叠或打开 #include @该文件是第二步中mkconfig文件执行时创建的.include/config.h ...

  3. EOS主网启动流程实践及分析(搭建自己的EOS多节点测试主网)

    昨晚在细看ram消耗的问题时候,搭建了完整的测试主网,经过更新,发现现在的搭建过程还是很便利的,于是刚刚整理了下,希望对大家有帮助. EOS dawn-v4.1.0版本开始就有EOS主网启动教程项目了 ...

  4. 详细的FFmpeg编译流程与脚本分析

    FFmpeg是一个跨平台的多媒体库,也是目前音视频领域应用最广泛的库.它包括libavcodec.libavformat.libavutil.libavdevice.libavfilter.libsw ...

  5. 【openwrt】netifd组件——netifd-proto脚本分析

    netifd-proto脚本 utils.sh脚本 netifd-proto.sh脚本 netifd-wireless.sh netifd工具提供如下3个脚本用于网络配置 utils.sh脚本 net ...

  6. 【ElasticSearch】Es 启动流程 源码分析

    文章目录 1. 概述 2. start方法 2.1 启动生命周期相关的组件 2.2 启动IndicesService 2.3 IndicesClusterStateService启动 2.4 Snap ...

  7. ARMv8架构u-boot启动流程详细分析(一)

    文章目录 1 概述 2 armv8 u-boot的启动 3 u-boot源码整体结构和一些编译配置方式 3.1 编译配置方式 3.2 u-boot源码结构 4 u-boot armv8链接脚本 4.1 ...

  8. 海思uboot启动流程详细分析(二)

    1. 第二个start.S 从start_armboot开始,在startup.c中有包含#include <config.h> 在config.h中: /* Automatically ...

  9. 【SemiDrive源码分析】【X9芯片启动流程】30 - AP1 Android Kernel 启动流程 start_kernel 函数详细分析(一)

    [SemiDrive源码分析][X9芯片启动流程]30 - AP1 Android Kernel 启动流程 start_kernel 函数详细分析(一) 一.Android Kernel 启动流程分析 ...

最新文章

  1. 漫画 | 你对加班有什么看法?
  2. 虚幻UE4常见问题最全集合
  3. 【Flutter】Hero 动画 ( Hero 动画使用流程 | 创建 Hero 动画核心组件 | 创建源页面 | 创建目的页面 | 页面跳转 )
  4. Oracle查找重复数据
  5. 一个简单而又灵活的数据库操作类
  6. Intellj IDEA 注册码 2018
  7. mac os x 10.11 php7,mac osx 10.11.2安装PHP7提示“configure: error: libcrypto not found!”
  8. python有float行吗_python – 有什么方法可以在将pandas系列从str转换为float时跳过不可转换的行?...
  9. 安装配置文件共享协议(SAMBA)
  10. Python input()和raw_input()的区别
  11. python遍历文件夹下所有文件
  12. 面向对象的15位、18位中国大陆身份证号码解析、验证工具
  13. beamforming matlab,Beamforming- 波束形成Matlab程序,通过 了解 的算法过程,以及其含义 272万源代码下载- www.pudn.com...
  14. Android Room 数据访问对象(DAO)详解
  15. PPT技巧分享,教你制作美美的PPT图表
  16. Kafka安装与使用
  17. java根据url获取pdf流_从URL获取动态创建的PDF
  18. python爬虫获取豆瓣正在热播电影
  19. 计算机技术与软件专业技术资格(水平)考试介绍
  20. 日本干预郭台铭收购东芝半导体 担心技术流向中国

热门文章

  1. 【Java SE】多线程
  2. html中保留空格及换行
  3. 末学者笔记--apache编译安装及LAMP架构上线
  4. M580PLC以太网读通讯和写通讯
  5. 【多媒体编解码】Openmax IL (一)官方文档概述
  6. adb 禁止app联网_通过ADB启用和禁用系统应用
  7. 深度学习之10分钟入门h5py
  8. 携程酒店评论EDA及词云展示—数据来自和鲸社区
  9. 关于vite配置postcss未生效问题
  10. Android计算标准BMI值