本文最后更新于 26 天前,其中的信息可能已经有所发展或是发生改变。
关于被注册的进程的输出
笔者注:在openwrt架构的实际开发过程中常常会遇到自己添加的模块中的printf输出看不到的情况,故查看procd源码希望可以找到问题原因
关键函数调用栈
rc
->_rc
->add_initd
->q_initd_run
->ustream_fd_init
->ustream_fd_set_uloop
->uloop_fd_add
被监听进程的标准输出,标准错误的重定向
q_initd_run函数
static void q_initd_run(struct runqueue *q, struct runqueue_task *t)
{
struct initd *s = container_of(t, struct initd, proc.task);
int pipefd[2];
pid_t pid;
clock_gettime(CLOCK_MONOTONIC_RAW, &s->ts_start);
DEBUG(2, "start %s %s \n", s->file, s->param);
if (pipe(pipefd) == -1) {
ERROR("Failed to create pipe: %m\n");
return;
}
pid = fork();
if (pid < 0)
return;
if (pid) {
close(pipefd[1]);
fcntl(pipefd[0], F_SETFD, FD_CLOEXEC);
s->fd.stream.string_data = true,
s->fd.stream.notify_read = pipe_cb,
runqueue_process_add(q, &s->proc, pid);
ustream_fd_init(&s->fd, pipefd[0]);
return;
}
close(pipefd[0]);
int devnull = open("/dev/null"c, O_RDONLY);
dup2(devnull, STDIN_FILENO);
dup2(pipefd[1], STDOUT_FILENO);
dup2(pipefd[1], STDERR_FILENO);
if (devnull > STDERR_FILENO)
close(devnull);
execlp(s->file, s->file, s->param, NULL);
exit(1);
}
void ustream_fd_init(struct ustream_fd *sf, int fd)
{
...
sf->fd.fd = fd;
...
}
核心思想
q_initd_run函数首先创建了一个管道,并通过fork创建了新的进程
- 父进程:关闭了管道的写端;初始化了部分关键参数,并将进程添加到运行队列中去
- 子进程:关闭了管道的读端,将进程的标准错误和标准输出绑定到管道的写端上去;接着通过
execlp函数执行指定的命令 - 通过
ustream_fd_init函数将管道的输出端绑定到s->fd->fd.fd上
其中父进程的配置参数,其在内存中的分别情况如图;需要注意的是,传入运行队列的是s->proc部分,初始化的是s->fd部分;

管道输出端的最终使用
在q_initd_run函数中通过fork创建了新的进程执行待运行的命令,又通过管道将新进程的标准输出和标准错误重定向
ustream_fd_init函数
笔者注:已省略部分逻辑
static void ustream_fd_set_uloop(struct ustream *s, bool write)
{
struct ustream_fd *sf = container_of(s, struct ustream_fd, stream);
...
uloop_fd_add(&sf->fd, flags);
}
void ustream_fd_init(struct ustream_fd *sf, int fd)
{
struct ustream *s = &sf->stream;
...
sf->fd.fd = fd;
...
ustream_fd_set_uloop(s, false);
}
核心思想
管道输出端的句柄最终是通过uloop_fd_add添加到uloop中;uloop中用epoll来监听这个句柄