Hotplug原理
Hotplug即热插拔,它的功能就是允许用户在不关闭系统,不切断电源的情况下取出和更换设备,最常见的如USB键盘,鼠标。
Procd是OpenWRT下新的预初始化,初始化,热插拔和事件系统。在openwrt 中, procd 作为 init 进程会处理许多事情, 其中就包括 hotplug。procd本身并不知道如何处理hotplug事件,也没有必要知道,因为它只实现机制,而不实现策略。事件的处理是由配置文件决定的,这些配置文件即所谓的rules.。老版本下独立的hotplug2在r36987被移除了。所以下面我们要介绍的就是新版本下Hotplug的机制。
热插拔与冷插拔入口
要了解Hotplug运行的整个过程,首先得了解procd系统的工作流程。才能从全局了解hotplug是如何工作的。
调用 openWRT的state_enter() 状态机函数,此函数中,在系统early阶段初始 hotplug()、procd_coldplug()函数。注册RPC服务响应程序。源码如下:
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"); //热插拔函数初始化,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 perform_halt(); #else exit(EXIT_SUCCESS); #endif break; default: ERROR("Unhandled state %d\n", state); return; }; }
一、热插拔源码走读
void hotplug(char *rules) { struct sockaddr_nl nls = {}; int nlbufsize = 512 * 1024; rule_file = strdup(rules); nls.nl_family = AF_NETLINK; nls.nl_pid = getpid(); nls.nl_groups = -1; // 建立 NETLINK_KOBJECT_UEVENT 连接 if ((hotplug_fd.fd = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_KOBJECT_UEVENT)) == -1) { ERROR("Failed to open hotplug socket: %m\n"); exit(1); } if (bind(hotplug_fd.fd, (void *)&nls, sizeof(struct sockaddr_nl))) { ERROR("Failed to bind hotplug socket: %m\n"); exit(1); } // 建立 监听 if (setsockopt(hotplug_fd.fd, SOL_SOCKET, SO_RCVBUFFORCE, &nlbufsize, sizeof(nlbufsize))) ERROR("Failed to resize receive buffer: %m\n"); json_script_init(&jctx); queue_proc.cb = queue_proc_cb; //设置回调函数 uloop_fd_add(&hotplug_fd, ULOOP_READ); //把 hotplug_fd 添加到 ubus 链表中 }
文件 /etc/hotplug.json
[ [ "case", "ACTION", { "add": [ [ "if", [ "and", [ "has", "MAJOR" ], [ "has", "MINOR" ], ], [ [ "if", [ "or", [ "eq", "DEVNAME", [ "null", "full", "ptmx", "zero" ], ], [ "regex", "DEVNAME", [ "^gpio", "^hvc" ], ], ], [ [ "makedev", "/dev/%DEVNAME%", "0666" ], [ "return" ], ] ], [ "if", [ "or", [ "eq", "DEVNAME", "mapper/control" ], [ "regex", "DEVPATH", "^ppp" ], ], [ [ "makedev", "/dev/%DEVNAME%", "0600" ], [ "return" ], ], ], [ "if", [ "has", "DEVNAME" ], [ "makedev", "/dev/%DEVNAME%", "0644" ], ], ], ], [ "if", [ "has", "FIRMWARE" ], [ [ "exec", "/sbin/hotplug-call", "%SUBSYSTEM%" ], [ "load-firmware", "/lib/firmware" ], [ "return" ] ] ], ], "remove" : [ [ "if", [ "and", [ "has", "DEVNAME" ], [ "has", "MAJOR" ], [ "has", "MINOR" ], ], [ "rm", "/dev/%DEVNAME%" ] ] ] } ], [ "if", [ "eq", "SUBSYSTEM", "platform" ], [ "exec", "/sbin/hotplug-call", "%SUBSYSTEM%" ] ], [ "if", [ "and", [ "has", "BUTTON" ], [ "eq", "SUBSYSTEM", "button" ], ], [ "exec", "/etc/rc.button/%BUTTON%" ] ], [ "if", [ "eq", "SUBSYSTEM", [ "net", "input", "usb", "ieee1394", "block", "atm", "zaptel", "tty", "button" ] ], [ "exec", "/sbin/hotplug-call", "%SUBSYSTEM%" ] ], [ "if", [ "and", [ "eq", "SUBSYSTEM", "usb-serial" ], [ "regex", "DEVNAME", [ "^ttyUSB", "^ttyACM" ] ], ], [ "exec", "/sbin/hotplug-call", "tty" ] ], ]
热插拔回调函数
static void hotplug_handler(struct uloop_fd *u, unsigned int ev) { int i = 0; static char buf[4096]; int len = recv(u->fd, buf, sizeof(buf) - 1, MSG_DONTWAIT); void *index; if (len < 1) return; buf[len] = '\0'; blob_buf_init(&b, 0); index = blobmsg_open_table(&b, NULL); while (i < len) { int l = strlen(buf + i) + 1; char *e = strstr(&buf[i], "="); if (e) { *e = '\0'; blobmsg_add_string(&b, &buf[i], &e[1]); } i += l; } blobmsg_close_table(&b, index); hotplug_handler_debug(b.head); json_script_run(&jctx, rule_file, blob_data(b.head)); //执行 /etc/hotplug.json 执行 json 脚本程序。 }
热插拔回调函数中,调用json_script_run()函数,此函数调用如下函数
static void __json_script_run(struct json_call *call, struct json_script_file *file, struct blob_attr *context) { struct json_script_ctx *ctx = call->ctx; if (file->seq == call->seq) { if (context) ctx->handle_error(ctx, "Recursive include", context); return; } file->seq = call->seq; while (file) { // 遍历 json 文件中所有的脚本文件 json_process_cmd(call, file->data); // 执行脚本中命令 file = file->next; } }
调用如下函数,此函数
static int __json_process_cmd(struct json_call *call, struct blob_attr *cur) { struct json_script_ctx *ctx = call->ctx; const char *name; bool found; int ret; if (blobmsg_type(cur) != BLOBMSG_TYPE_ARRAY || blobmsg_type(blobmsg_data(cur)) != BLOBMSG_TYPE_STRING) { ctx->handle_error(ctx, "Unexpected element type", cur); return -1; } ret = __json_process_type(call, cur, cmd, ARRAY_SIZE(cmd), &found); if (found) return ret; name = blobmsg_data(blobmsg_data(cur)); ret = cmd_process_strings(call, cur); if (ret) return ret; ctx->handle_command(ctx, name, blob_data(ctx->buf.head), call->vars); return 0; }
二、冷插拔源码走读
void procd_coldplug(void) { char *argv[] = { "udevtrigger", NULL }; unsigned int oldumask = umask(0); if (!is_container()) { umount2("/dev/pts", MNT_DETACH); umount2("/dev/", MNT_DETACH); mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755,size=512K"); mkdir("/dev/pts", 0755); mount("devpts", "/dev/pts", "devpts", MS_NOEXEC | MS_NOSUID, 0); } ignore(symlink("/tmp/shm", "/dev/shm")); umask(oldumask); udevtrigger.cb = udevtrigger_complete; udevtrigger.pid = fork(); if (!udevtrigger.pid) { execvp(argv[0], argv); ERROR("Failed to start coldplug: %m\n"); exit(EXIT_FAILURE); } if (udevtrigger.pid <= 0) { ERROR("Failed to start new coldplug instance: %m\n"); return; } uloop_process_add(&udevtrigger); DEBUG(4, "Launched coldplug instance, pid=%d\n", (int) udevtrigger.pid); }
参考
https://blog.csdn.net/weixin_38387929/article/details/119461967
暂无评论