qemu 信号线程化
发布日期:2021-09-16 04:36:44
浏览次数:16
分类:技术文章
本文共 4112 字,大约阅读时间需要 13 分钟。
在看 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; }#endif return 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; } } }}
转载地址:https://blog.csdn.net/wllabs/article/details/109173957 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!
发表评论
最新留言
很好
[***.229.124.182]2024年03月11日 12时45分56秒
关于作者
喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!
推荐文章
漫画:越挫越勇
2019-04-21
我的2018总结
2019-04-21
WinRAR存在严重的安全漏洞影响5亿用户
2019-04-21
Dockerfile参考
2019-04-21
吐槽Javascript系列二:数组中的splice和slice方法
2019-04-21
django开发-django和tornado的不同
2019-04-21
行内元素有哪些?块级元素有哪些? 空(void)元素有那些?
2019-04-21
220. Contains Duplicate III
2019-04-21
vue+node全栈移动商城【8】-vant新建注册页面
2019-04-21
JavaScript五十问——对比来说CSS的Grid与FlexBox(下篇)
2019-04-21
枚举的使用示例
2019-04-21
如何导入golang.org的包
2019-04-21
猴子数据让你深刻了解微信富媒体
2019-04-21
浅谈深拷贝和浅拷贝
2019-04-21
区块链技术特点之去中心化特性
2019-04-21
柯里化
2019-04-21
Spring Cloud Gateway 使用 Token 验证
2019-04-21
Spring源码分析:声明式事务梳理
2019-04-21
记一次前端面试试水笔记
2019-04-21
为什么大家觉得前端开发很难做下去?关于我的一些思考
2019-04-21