qemu 信号线程化

本文分析了QEMU中信号处理的实现方式,包括信号线程化、信号fd的使用及兼容性处理。介绍了qemu_init_main_loop函数如何初始化信号处理,并详细解释了qemu_signal_init函数的具体操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在看 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;
            }
        }
    }
}

 

<think>我们正在讨论QEMU与RISC-V的配置和使用,涉及模拟、虚拟化或开发环境搭建。根据用户提供的引用,他们可能在构建嵌入式Linux系统,并涉及Qt库移植、busybox移植以及sshd服务配置等。用户的问题集中在QEMURISC-V的配置和使用上。结合引用内容,我们可以推断用户正在尝试在RISC-V架构上构建嵌入式Linux系统,并使用QEMU进行模拟。因此,回答应该围绕如何使用QEMU来模拟RISC-V平台,并运行一个Linux系统。回答将包括以下部分:1.安装QEMU(确保支持RISC-V)2.获取RISC-VLinux镜像或自行构建(内核和根文件系统)3.配置QEMU运行RISC-V虚拟机4.常见用法(如网络配置、设备透传等)同时,考虑到用户之前可能的问题(构建嵌入式Linux系统),我们将提供一些构建内核和根文件系统的提示。根据引用[1]和[3],用户可能已经进行了一些交叉编译的工作(如Qt库和openssh),所以我们在回答中可以提到如何将这些应用程序集成到根文件系统中。另外,引用[2]提到busybox的移植,这是构建根文件系统的关键组件。因此,回答结构如下:一、安装QEMU(支持RISC-V)二、获取或构建RISC-VLinux内核三、构建根文件系统(包含busybox等工具)四、运行QEMURISC-V虚拟机五、常见配置(网络、SSH等)六、其他提示(如应用程序移植,参考用户给出的引用)由于用户要求生成相关问题,在最后我们将生成几个相关问题。注意:我们参考了用户提供的引用,但用户的实际需求是QEMURISC-V的配置和使用,所以我们将聚焦于此。开始回答:</think> ### QEMU RISC-V 配置与使用指南 #### 1. QEMU RISC-V环境安装与配置 **核心组件安装**: ```bash # 安装QEMU(确保版本≥6.0) sudo apt install qemu-system-riscv64 # 验证RISC-V支持 qemu-system-riscv64 -machine help | grep sifive ``` **配置文件示例**(参照引用[1]交叉编译思路): ```makefile # qemu_riscv.cfg [device] name = "virt" machine_type = "sifive_u" [memory] size = "1024M" [processor] type = "rv64gc" smp = "4" ``` #### 2. RISC-V系统模拟启动流程 **启动命令**: ```bash qemu-system-riscv64 \ -machine virt \ -kernel Image \ -append "root=/dev/vda console=ttyS0" \ -drive file=rootfs.ext4,format=raw,id=hd0 \ -device virtio-blk-device,drive=hd0 \ -netdev user,id=net0 \ -device virtio-net-device,netdev=net0 ``` **关键参数说明**: - `-machine virt`:选择通用RISC-V虚拟平台 - `-drive file`:挂载根文件系统(参考引用[3]构建的文件系统) - `-netdev user`:启用用户模式网络(支持SSH配置,引用[3]) #### 3. 开发环境搭建实践 **交叉编译工具链配置**: ```bash # 设置工具链前缀(参照引用[1]) export CROSS_PREFIX=riscv64-unknown-linux-gnu- # 编译内核示例 make ARCH=riscv CROSS_COMPILE=${CROSS_PREFIX} defconfig ``` **BusyBox集成**(参考引用[2]): 1. 修改init进程信号接管: ```c // init.c signal(SIGTERM, handle_poweroff); // 处理关机信号 ``` 2. 构建最小文件系统: ```bash mkdir rootfs && busybox --install rootfs/bin ``` #### 4. 外围设备扩展 **虚拟外设映射**: ```bash # 添加串口设备 -device virtio-serial-device # 挂载PCIe设备(需VFIO支持) -device vfio-pci,host=01:00.0 ``` **硬件加速配置**: ```bash -enable-kvm -cpu host # KVM虚拟化加速 -accel tcg,thread=multi # 多线程TCG模拟 ``` #### 5. 调试与性能优化 **GDB调试支持**: ```bash -s -S # 启动GDB服务器 riscv64-unknown-linux-gnu-gdb vmlinux (gdb) target remote :1234 ``` **性能监控**: ```bash # 启用性能计数器 -monitor telnet::4444,server,nowait telnet localhost 4444 info registers info qtree ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值