在看 qemu_init_main_loop 函数的时候发现 qemu_signal_init 里竟然会调用 qemu_set_fd_handler 添加 ctx的io_handlers, 设置 fd对应的处理函数,于是看了下 qemu_signal_init ,原来是 信号线程化的,使信号可重入,使信号处理函数可以更复杂。
同时这里还使用了 signal_fd,原来现在内核里已实现了信号fd,以前都是用管道,用户态实现通知,qemu为了兼容,两种都实现了,下面分析代码:
qemu_init_main_loop 函数:
int qemu_init_main_loop(Error **errp)
{int ret;GSource *src;Error *local_error = NULL;init_clocks(qemu_timer_notify_cb);ret = qemu_signal_init(); 初始化信号处理函数if (ret) {return ret;}qemu_aio_context = aio_context_new(&local_error); //创建事件源if (!qemu_aio_context) {error_propagate(errp, local_error);return -EMFILE;}qemu_notify_bh = qemu_bh_new(notify_event_cb, NULL); //创建 底半部gpollfds = g_array_new(FALSE, FALSE, sizeof(GPollFD));src = aio_get_g_source(qemu_aio_context);g_source_set_name(src, "aio-context");g_source_attach(src, NULL); //事件源添加到上下文g_source_unref(src);src = iohandler_get_g_source();g_source_set_name(src, "io-handler");g_source_attach(src, NULL);g_source_unref(src);return 0;
}
qemu_signal_init, 先屏蔽信号,然后调用 qemu_signalfd 获得信号通知的 fd,qemu_signalfd是后面的重点。
static int qemu_signal_init(void)
{int sigfd;sigset_t set;/** SIG_IPI must be blocked in the main thread and must not be caught* by sigwait() in the signal thread. Otherwise, the cpu thread will* not catch it reliably.*/sigemptyset(&set);sigaddset(&set, SIG_IPI);sigaddset(&set, SIGIO);sigaddset(&set, SIGALRM);sigaddset(&set, SIGBUS);/* SIGINT cannot be handled via signalfd, so that ^C can be used* to interrupt QEMU when it is being run under gdb. SIGHUP and* SIGTERM are also handled asynchronously, even though it is not* strictly necessary, because they use the same handler as SIGINT.*/pthread_sigmask(SIG_BLOCK, &set, NULL);sigdelset(&set, SIG_IPI);sigfd = qemu_signalfd(&set);if (sigfd == -1) {fprintf(stderr, "failed to create signalfd\n");return -errno;}fcntl_setfl(sigfd, O_NONBLOCK);qemu_set_fd_handler(sigfd, sigfd_handler, NULL, (void *)(intptr_t)sigfd);return 0;
}
qemu_signalfd 如果定义了 CONFIG_SIGNALFD,则使用系统调用 syscall SYS_signalfd 获得对应的信号通知 fd,如果没有定义则进入qemu_signalfd_compat 函数,这个函数就是使用创建管道和线程sigwait_compat,返回管道 读端。
static int qemu_signalfd_compat(const sigset_t *mask)
{struct sigfd_compat_info *info;QemuThread thread;int fds[2];info = malloc(sizeof(*info));if (info == NULL) {errno = ENOMEM;return -1;}if (pipe(fds) == -1) {free(info);return -1;}qemu_set_cloexec(fds[0]);qemu_set_cloexec(fds[1]);memcpy(&info->mask, mask, sizeof(*mask));info->fd = fds[1];qemu_thread_create(&thread, "signalfd_compat", sigwait_compat, info,QEMU_THREAD_DETACHED);return fds[0];
}int qemu_signalfd(const sigset_t *mask)
{
#if defined(CONFIG_SIGNALFD)int ret;ret = syscall(SYS_signalfd, -1, mask, _NSIG / 8);if (ret != -1) {qemu_set_cloexec(ret);return ret;}
#endifreturn qemu_signalfd_compat(mask);
}
sigwait_compat 里使用 sigwait 获取pending的信号,并写入管道写端,从而实现通知管理读端,然后glib 循环获取事件,执行sigfd_handler 函数。
static void *sigwait_compat(void *opaque)
{struct sigfd_compat_info *info = opaque;while (1) {int sig;int err;err = sigwait(&info->mask, &sig);if (err != 0) {if (errno == EINTR) {continue;} else {return NULL;}} else {struct qemu_signalfd_siginfo buffer;size_t offset = 0;memset(&buffer, 0, sizeof(buffer));buffer.ssi_signo = sig;while (offset < sizeof(buffer)) {ssize_t len;len = write(info->fd, (char *)&buffer + offset,sizeof(buffer) - offset);if (len == -1 && errno == EINTR)continue;if (len <= 0) {return NULL;}offset += len;}}}
}