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 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:librbd 简单学习例子
下一篇:信号处理,信号队列

发表评论

最新留言

很好
[***.229.124.182]2024年03月11日 12时45分56秒