QEMU源码编译实战:可控构建、安全加固与性能调优
1. 为什么非得从源码编译QEMU——不是懒是真有硬需求很多人第一次接触虚拟化工具时第一反应是sudo apt install qemu或brew install qemu。装完就能跑确实省事。但我在某跨平台系统开发项目里连续踩了三次坑才彻底明白预编译包是“能用”源码编译才是“敢用”。第一次是调试一个ARM64设备驱动模拟器用Ubuntu官方源的qemu-system-arm发现它默认禁用了KVM加速即使宿主机支持查qemu-system-arm --version只显示4.2.1 (Debian 1:4.2.1-2ubuntu1)连编译参数都看不到。第二次是需要启用--enable-debug-info生成带完整符号的二进制结果apt包连debuginfo子包都不提供gdb进去全是??。第三次最致命——客户环境要求QEMU必须链接静态musl libc而所有发行版包全依赖glibc一运行就报错/lib/ld-musl-aarch64.so.1: No such file or directory。这三件事让我彻底放弃包管理器。源码编译不是炫技而是解决三个刚性问题可控的依赖链、可验证的功能开关、可追溯的构建路径。比如你打开QEMU的configure脚本会看到超过200个--enable-*和--disable-*选项其中--enable-kvm控制硬件加速开关--enable-virtfs决定是否支持9p文件共享--enable-debug影响日志粒度——这些在二进制包里全被发行版维护者“帮你选好了”而你根本不知道他选的是什么。更关键的是安全审计。去年某实验室在审计一款金融行业定制QEMU时发现其预编译镜像中混入了未声明的--enable-spice模块该模块依赖的Cyrus SASL库存在已知CVE-2023-3750漏洞。而源码编译时你只要在./configure命令里明确写上--disable-spice这个攻击面就彻底消失。这不是理论风险是真实发生的供应链污染案例。所以当你看到标题里“从源码编译安装QEMU”请先问自己你的场景是否涉及以下任意一项需要精确控制CPU指令集支持如仅启用sve扩展用于AI模拟必须满足FIPS 140-2加密合规要求需禁用OpenSSL改用mbedTLS要在无root权限的容器内运行需--static链接需要patch内核级virtio驱动行为必须同步修改hw/virtio/目录如果答案是肯定的那接下来的每一步都是在为你自己的生产环境筑墙而不是在别人的地基上搭积木。2. 源码编译全流程拆解从git clone到qemu-system-x86_64可执行文件2.1 环境准备——别让依赖成为第一道墙QEMU的编译依赖比想象中复杂。我见过太多人卡在meson build/这一步报错ERROR: Dependency pixman-1 not found其实根本原因不是没装pixman而是装了错误的版本。QEMU 8.2.0要求pixman 0.40.0但Ubuntu 20.04默认源只有0.38.4。这里给出经过实测的最小依赖清单以x86_64宿主机为例# Ubuntu/Debian系注意必须用apt而非snap安装 sudo apt update sudo apt install -y \ git build-essential python3-pip python3-setuptools \ libglib2.0-dev libpixman-1-dev libfdt-dev \ libusb-1.0-0-dev libvdeplug-dev libspice-server-dev \ libsdl2-dev libgtk-3-dev libncurses5-dev \ zlib1g-dev liblz4-dev libzstd-dev # CentOS/RHEL系重点禁用dnf的模块流用传统包 sudo dnf groupinstall -y Development Tools sudo dnf install -y \ git glib2-devel pixman-devel dtc-devel \ usbredir-devel spice-server-devel sdl2-devel \ gtk3-devel ncurses-devel zlib-devel lz4-devel zstd-devel提示libspice-server-dev和libsdl2-dev是可选依赖但强烈建议安装。前者提供远程桌面协议支持调试GUI应用必备后者让QEMU能直接渲染窗口避免VNC连接延迟。如果你确定只用headless模式可删掉这两项但后续创建虚拟机时会失去图形界面调试能力。最关键的一步常被忽略Python环境隔离。QEMU的meson构建系统会调用python3 -m pip install安装临时依赖如果系统Python被conda或pyenv污染可能触发ModuleNotFoundError: No module named mesonbuild。我的解决方案是创建干净的venvpython3 -m venv ~/qemu-build-env source ~/qemu-build-env/bin/activate pip install --upgrade pip meson ninja这样确保meson和ninja版本可控QEMU 8.2.0要求meson 0.63.0。2.2 获取源码与版本选择——稳定版还是主线分支QEMU官网qemu.org提供两种源码获取方式稳定发布版Stable Releases如qemu-8.2.0.tar.xz适合生产环境每3个月发布一次经过充分测试主线Git仓库git://git.qemu.org/qemu.git包含最新功能但可能有未修复的regression我的经验是新项目起步用最新稳定版已有项目升级用git bisect定位问题。比如你遇到qemu-system-x86_64: -device virtio-blk-pci: Property .indirect_desc not found这类报错大概率是QEMU版本与guest kernel不兼容此时用git checkout到特定commit比等下一个稳定版更快。下载并解压稳定版以8.2.0为例wget https://download.qemu.org/qemu-8.2.0.tar.xz tar -xf qemu-8.2.0.tar.xz cd qemu-8.2.0注意不要用git clone https://git.qemu.org/qemu.git后直接git checkout v8.2.0因为官方tarball包含自动生成的VERSION和config-host.h文件而git仓库需要额外运行./configure生成容易遗漏。2.3 configure阶段——200个开关里你真正需要调的只有7个QEMU的./configure脚本堪称Linux世界最复杂的配置系统之一。它会自动探测系统能力但自动探测有时会出错。比如在某些云服务器上configure可能误判KVM可用性导致后续无法启用硬件加速。因此我坚持手动指定关键开关./configure \ --prefix$HOME/qemu-install \ --target-listx86_64-softmmu,aarch64-softmmu \ --enable-kvm \ --enable-virtfs \ --enable-debug \ --enable-werror \ --disable-werror \ --static逐项解释这些参数的实战意义--prefix$HOME/qemu-install绝对不要用/usr/local。我曾因权限问题把QEMU装到系统目录结果make install时覆盖了系统原有的qemu-img导致Docker构建失败。用户目录安装可避免权限冲突且便于多版本共存如$HOME/qemu-7.2和$HOME/qemu-8.2--target-list...指定要编译的机器类型。x86_64-softmmu是x86虚拟机aarch64-softmmu是ARM64虚拟机。切勿用--target-listall它会编译全部30种架构耗时增加3倍且产生大量无用二进制--enable-kvm显式启用KVM加速。虽然configure会自动检测但加上此参数可强制启用并在config.log中确认kvm yes--enable-virtfs启用9p文件共享这是实现host-guest无缝文件传输的核心比scp快10倍--enable-debug生成带调试符号的二进制gdb qemu-system-x86_64时能看到函数名和行号--disable-werror关键禁用“警告即错误”。QEMU源码在不同GCC版本下会产生新警告如-Wstringop-truncation若启用-Werror会导致编译中断。生产环境可保留但首次编译务必关闭--static静态链接所有依赖。这是解决musl libc兼容性的终极方案但会使二进制体积增大至200MB需权衡运行./configure后务必检查输出末尾的SummaryInstall prefix /home/user/qemu-install BIOS directory ${prefix}/share/qemu binary directory ${prefix}/bin library directory ${prefix}/lib Include directory ${prefix}/include Config directory ${prefix}/etc Local state dir ${prefix}/var Manual directory ${prefix}/share/man Doc directory ${prefix}/share/doc/qemu Build system x86_64-pc-linux-gnu Host system x86_64-pc-linux-gnu Target list x86_64-softmmu aarch64-softmmu ... KVM support yes VirtFS support yes Debug support yes如果KVM support显示no立即检查/dev/kvm权限和内核模块ls -l /dev/kvm # 应显示 crw-rw---- 1 root kvm sudo modprobe kvm-intel # Intel CPU sudo modprobe kvm-amd # AMD CPU2.4 编译与安装——时间管理与资源分配技巧make -j$(nproc)是标准操作但实际中我发现编译时间不取决于CPU核心数而取决于内存带宽。在32GB内存的机器上-j16比-j32快15%因为过多并发导致内存页交换。我的黄金公式是-j$(($(nproc) / 2 1))。更关键的是磁盘IO优化。QEMU编译会产生大量临时文件build/目录可达15GB如果放在机械硬盘上make会卡在[100%] Linking target/x86_64-softmmu/qemu-system-x86_64阶段。解决方案是将构建目录挂载到tmpfssudo mount -t tmpfs -o size20G tmpfs /tmp/qemu-build cd /tmp/qemu-build /path/to/qemu-8.2.0/configure --prefix$HOME/qemu-install ... make -j$(($(nproc)/21))编译完成后安装只需一步make install此时检查安装结果$HOME/qemu-install/bin/qemu-system-x86_64 --version # 输出QEMU emulator version 8.2.0 (v8.2.0-1-g1a2b3c4d)注意qemu-system-x86_64是主程序qemu-img用于磁盘管理qemu-nbd用于网络块设备——这三个是日常使用频率最高的二进制确保它们都在$HOME/qemu-install/bin/下。3. 创建第一个QEMU虚拟机从零开始的完整链路3.1 磁盘镜像准备——qcow2格式的隐藏优势很多人用dd if/dev/zero ofdisk.img bs1G count20创建原始镜像但这会立即占用20GB磁盘空间。QEMU推荐的qcow2格式采用按需分配copy-on-write初始文件仅几KB随数据写入动态增长。更重要的是它原生支持快照、压缩和加密。创建一个20GB的qcow2镜像$HOME/qemu-install/bin/qemu-img create -f qcow2 ubuntu22.04.qcow2 20G参数详解-f qcow2指定格式。qcow2比raw多出快照能力比vmdk兼容性更好20G虚拟磁盘大小。注意这是逻辑大小物理占用初始为du -h ubuntu22.04.qcow2≈ 192K提示qcow2镜像可设置backing file实现差分镜像。例如先创建基础镜像base.qcow2再创建dev.qcow2指向它这样多个开发环境共享同一基础系统节省90%磁盘空间。3.2 ISO安装介质——如何让QEMU识别UEFI固件现代Linux发行版Ubuntu 22.04、Fedora 38默认使用UEFI启动。若直接用-cdrom ubuntu-22.04.isoQEMU会以传统BIOS模式启动导致安装失败。解决方案是加载OVMF固件# 下载OVMF固件需单独安装 sudo apt install ovmf # Ubuntu/Debian sudo dnf install edk2-ovmf # CentOS/RHEL # 创建UEFI启动的QEMU命令 $HOME/qemu-install/bin/qemu-system-x86_64 \ -bios /usr/share/OVMF/OVMF_CODE.fd \ -drive ifpflash,formatraw,readonlyon,file/usr/share/OVMF/OVMF_VARS.fd \ -cdrom ubuntu-22.04-live-server-amd64.iso \ -drive fileubuntu22.04.qcow2,formatqcow2 \ -m 4G -smp 2 \ -netdev user,idnet0,hostfwdtcp::2222-:22 \ -device e1000,netdevnet0关键参数解析-bios ...指定UEFI固件代码这是启动的核心-drive ifpflash,...挂载UEFI变量存储区必须设为readonlyon否则每次启动都会重置固件设置-netdev user,...启用用户模式网络hostfwdtcp::2222-:22将宿主机2222端口映射到guest的22端口实现SSH直连注意OVMF固件路径因发行版而异。Ubuntu在/usr/share/OVMF/CentOS在/usr/share/edk2/ovmf/。若提示Could not load firmware用find /usr -name OVMF_CODE.fd定位。3.3 启动安装过程——交互式操作的避坑指南运行上述命令后QEMU窗口会弹出UEFI Shell。此时不要慌按Esc键进入启动菜单选择Boot from EFI DVD/CDROM。安装过程与物理机一致但有三个关键点磁盘分区选择在Ubuntu安装器中选择Erase disk and install Ubuntu后务必点击Advanced features→ 勾选Use LVM with the new Ubuntu installation。LVM能让你后续轻松调整根分区大小避免No space left on device悲剧。SSH服务启用安装时勾选Install OpenSSH server。这是后续免密登录的基础否则要手动进console敲密码。网络配置陷阱安装器默认使用DHCP但QEMU的user模式网络会分配10.0.2.15/24地址。安装完成后在guest中执行ip addr show | grep inet 10\.0\.2\. # 应看到类似inet 10.0.2.15/24 brd 10.0.2.255 scope global dynamic ens3安装完成后关机并移除ISO$HOME/qemu-install/bin/qemu-system-x86_64 \ -bios /usr/share/OVMF/OVMF_CODE.fd \ -drive ifpflash,formatraw,readonlyon,file/usr/share/OVMF/OVMF_VARS.fd \ -drive fileubuntu22.04.qcow2,formatqcow2 \ -m 4G -smp 2 \ -netdev user,idnet0,hostfwdtcp::2222-:22 \ -device e1000,netdevnet0此时可通过SSH登录ssh -p 2222 ubuntulocalhost提示首次登录后立即执行sudo systemctl enable ssh防止重启后SSH服务关闭。4. 生产级虚拟机配置超越基础启动的深度优化4.1 CPU与内存调优——避免“虚拟机比物理机还慢”默认的-smp 2和-m 4G只是起点。真实场景中CPU和内存配置直接影响性能。QEMU提供精细的拓扑控制# 模拟双路CPU每路2核超线程开启 -smp 4,sockets2,cores2,threads1 \ # 内存大页支持需宿主机配置 -object memory-backend-memfd,idmem,size4G,shareon \ -numa node,memdevmem \ # CPU特性透传让guest看到真实CPU能力 -cpu host,kvmon,vmx,ssse3,sse4.2,popcnt,aes,avx,avx2,fma,rdtscp,xsave,xsaveopt,xgetbv1,xsaves,clflushopt,sha_ni,rdrand,rdseed,adx,smap,pt,ibt,stibp,spec-ctrl,arch-capabilities,invtsc,tsc-deadline,hypervisor,tsc-adjust,umip,md-clear,stibp,spec-ctrl,arch-capabilities,invtsc,tsc-deadline,hypervisor,tsc-adjust,umip,md-clear这段配置的实战价值sockets2,cores2让guest看到2个物理CPU插槽对Oracle数据库等绑定socket的软件至关重要memory-backend-memfd启用memfd内存后端比传统-m参数减少内存拷贝次数vmx等CPU特性列表确保guest能使用AES-NI指令加速加密AVX2加速科学计算注意开头的特性需宿主机CPU真实支持。用grep -E (vmx|svm) /proc/cpuinfo确认硬件虚拟化开启lscpu | grep Flags查看支持的指令集。4.2 存储性能革命——从virtio-blk到virtio-scsi默认的-drive file...使用IDE控制器IOPS不足5000。升级到virtio-scsi可提升至15万IOPS# 卸载原有IDE驱动添加virtio-scsi控制器 -device virtio-scsi-pci,idscsi0 \ -drive ifnone,idhd0,formatqcow2,fileubuntu22.04.qcow2 \ -device scsi-hd,busscsi0.0,drivehd0 \ # 同时启用多队列每个vCPU一个队列 -device virtio-scsi-pci,idscsi1,num_queues4 \在guest中验证# Ubuntu中执行 lsmod | grep virtio_scsi # 应显示加载 cat /sys/block/sda/device/num_queues # 应输出4提示virtio-scsi需要guest内核支持CONFIG_SCSI_VIRTIOy。Ubuntu 22.04默认启用但旧版CentOS 7需手动编译内核。4.3 网络零延迟——vhost-user与DPDK的取舍对于网络密集型应用如NFV、实时音视频user模式网络的延迟高达50ms。两个优化方向方案Avhost-user推荐# 启用vhost-net内核模块 sudo modprobe vhost_net # QEMU参数 -netdev tap,idnet0,vhoston,ifnametap0,scriptno,downscriptno \ -device virtio-net-pci,netdevnet0,mac52:54:00:12:34:56方案BDPDK用户态栈极致性能需额外编译DPDK库配置复杂但延迟可压至10μs。适用于电信级场景普通开发不推荐。实测数据在相同压力下iperf3 10Gbpsuser模式网络CPU占用率45%vhost-user降至12%DPDK降至3%。但DPDK要求guest完全接管网卡牺牲通用性。4.4 安全加固——SELinux/AppArmor与QEMU沙箱在生产环境中QEMU进程本身是高危攻击面。必须启用沙箱# 启用seccomp过滤禁用危险系统调用 -seccomp on \ # 启用chroot沙箱需提前创建目录 -chroot /var/lib/qemu/chroot \ # 启用capsicumFreeBSD或landlockLinux 5.13 -rtc driftfixslew \在Linux上landlock是更现代的选择# 创建受限目录 sudo mkdir -p /var/lib/qemu/restricted sudo chown -R $USER:$USER /var/lib/qemu/restricted # QEMU参数 -landlockon \注意-chroot要求所有依赖库如libglib-2.0.so.0复制到chroot目录操作繁琐。-landlock更轻量但需内核5.13。5. 故障排查实战那些让你抓狂的QEMU报错真相5.1 “Could not access KVM kernel module”——权限与模块的双重检查这个报错看似简单实则有五个层级原因层级检查命令修复方案1. 设备节点权限ls -l /dev/kvmsudo usermod -aG kvm $USER; newgrp kvm2. 内核模块未加载lsmodgrep kvm3. BIOS中禁用VT-x/AMD-Vdmesggrep -i kvm4. Hyper-V冲突Windows WSL2systeminfo | find Hyper-V Requirements在WSL2中禁用/etc/wsl.conf的[wsl2] nestedVirtualizationtrue5. 容器内运行限制cat /proc/sys/kernel/unprivileged_userns_cloneecho 1 /proc/sys/kernel/unprivileged_userns_clone最隐蔽的是第4层在Windows WSL2中运行QEMU即使宿主机开启VT-xWSL2默认禁用嵌套虚拟化。需在/etc/wsl.conf中添加[wsl2] nestedVirtualizationtrue然后重启WSLwsl --shutdown。5.2 “qemu-system-x86_64: -drive filexxx: Could not open xxx: Permission denied”——SELinux的无声拦截在CentOS/RHEL上即使ls -l xxx显示权限正常SELinux仍可能阻止访问。检查方法ausearch -m avc -ts recent | grep qemu # 若输出类似typeAVC msgaudit(1698765432.123:456): avc: denied { read } for pid1234 commqemu-system-x86 nameubuntu22.04.qcow2 devsda1 ino789012 tclassfile临时放行sudo setsebool -P qemu_use_fusefs 1 sudo chcon -t svirt_image_t ubuntu22.04.qcow2提示svirt_image_t是QEMU专用的SELinux类型比unconfined_t更安全。5.3 “Guest has not initialized the display (yet)”——图形界面黑屏的七种可能当添加-display gtk或-vga std后屏幕全黑常见原因缺少SDL2库ldd $(which qemu-system-x86_64) | grep sdl应有输出X11转发未启用在SSH中运行需ssh -X userhostWayland会话冲突在GNOME Wayland下加-display gtk,gloff禁用OpenGLGPU驱动不匹配NVIDIA驱动需export __GLX_VENDOR_LIBRARY_NAMEnvidia分辨率不支持加-vga qxl -spice port5930,addr127.0.0.1,disable-ticketing用SPICE替代UEFI固件缺失BIOS模式下-vga std有效UEFI模式需-vga qxl内存不足-vga qxl需至少128MB显存加-vga qxl -global qxl-vga.vram_size_mb256我最常用的是第5种SPICE协议比VNC更高效且支持剪贴板共享和USB重定向。5.4 “qemu-img convert: error while converting”——磁盘格式转换的原子性保障当需要将VMware的vmdk转为qcow2时qemu-img convert可能因磁盘损坏中断。正确做法是# 先校验源镜像 qemu-img check -r all windows10.vmdk # 若报告错误用-v选项修复 qemu-img check -r all -v windows10.vmdk # 转换时启用缓存和进度显示 qemu-img convert -p -O qcow2 -o cluster_size2M,preallocationmetadata windows10.vmdk win10.qcow2参数说明-p显示进度条-o cluster_size2M设置簇大小为2MB比默认64KB更适合大文件-o preallocationmetadata仅预分配元数据避免转换时占满磁盘经验转换100GB镜像前确保目标磁盘剩余空间150GB。qcow2转换过程会临时创建同大小的中间文件。6. 自动化运维用Ansible和Terraform管理QEMU集群6.1 Ansible角色设计——标准化QEMU部署手动执行./configure make make install不可持续。我设计的Ansible角色结构如下roles/qemu-build/ ├── defaults/main.yml # 版本号、安装路径等变量 ├── tasks/main.yml # 主任务链 ├── handlers/main.yml # 重启服务等 └── templates/ └── qemu-start.j2 # 生成启动脚本模板defaults/main.yml关键变量qemu_version: 8.2.0 qemu_prefix: /opt/qemu-{{ qemu_version }} qemu_targets: [x86_64-softmmu, aarch64-softmmu] qemu_configure_flags: - --enable-kvm - --enable-virtfs - --enable-debug - --disable-werrortasks/main.yml核心步骤- name: Download QEMU source get_url: url: https://download.qemu.org/qemu-{{ qemu_version }}.tar.xz dest: /tmp/qemu-{{ qemu_version }}.tar.xz - name: Extract source unarchive: src: /tmp/qemu-{{ qemu_version }}.tar.xz dest: /tmp/ remote_src: yes - name: Run configure command: ./configure {{ qemu_configure_flags | join( ) }} args: chdir: /tmp/qemu-{{ qemu_version }} creates: /tmp/qemu-{{ qemu_version }}/build.ninja # meson生成的文件 - name: Compile with ninja command: ninja -C build args: chdir: /tmp/qemu-{{ qemu_version }}提示用ninja替代make可提速40%因meson的构建图更精准。6.2 Terraform Provider——声明式创建虚拟机QEMU本身无Terraform Provider但可通过null_resource调用shell脚本。更优雅的方案是使用libvirtProviderQEMU的底层驱动provider libvirt { uri qemu:///system } resource libvirt_volume ubuntu_disk { name ubuntu22.04.qcow2 base_volume_id data.libvirt_volume.ubuntu_base.id pool libvirt_pool.pool.name } resource libvirt_domain vm { name ubuntu-dev memory 4096 vcpu 2 network_interface { network_name default } console { type pty target_port 0 target_type serial } graphics { type spice listen_type address } }此配置实现自动从基础镜像克隆差分卷分配4GB内存和2vCPU连接libvirt默认网络启用SPICE图形控制台注意需先sudo systemctl enable libvirtd sudo systemctl start libvirtd并确保当前用户在libvirt组。6.3 监控告警体系——PrometheusNode Exporter指标采集QEMU进程本身不暴露metrics但libvirt提供完整的监控接口。在Prometheus中添加# prometheus.yml scrape_configs: - job_name: libvirt static_configs: - targets: [localhost:9177] # libvirt exporter端口关键指标查询libvirt_domain_state{domainubuntu-dev}虚拟机状态1running, 0shut offlibvirt_domain_memory_usage_bytes{domainubuntu-dev}内存使用量libvirt_domain_block_read_bytes_total{domainubuntu-dev,devicevda}磁盘读取字节数告警规则示例内存超90%- alert: QEMU_VM_Memory_High expr: 100 * libvirt_domain_memory_usage_bytes{domain~.} / libvirt_domain_memory_maximum_bytes 90 for: 5m labels: severity: warning annotations: summary: QEMU VM {{ $labels.domain }} memory usage high提示libvirt_exporter需单独安装GitHub搜索prometheus-libvirt-exporter获取二进制。7. 性能基准测试量化你的QEMU配置效果7.1 CPU性能测试——SPEC CPU2017的简化方案SPEC CPU2017过于庞大我们用sysbench做轻量级测试# 在guest中安装sysbench sudo apt install sysbench # CPU测试单线程 sysbench cpu --cpu-max-prime20000 run --time60 --threads1 # 多线程对比关键 for t in 1 2 4 8; do echo Threads: $t sysbench cpu --cpu-max-prime20000 run --time60 --threads$t done关注events per second值。理想情况下8线程应接近1线程的8倍。若只有3倍说明存在锁竞争或CPU绑定问题。7.2 磁盘IOPS测试——fio配置的黄金参数# 随机读写4K IOPS fio --namerandread --ioenginelibaio --rwrandread --bs4k --direct1 \ --size2G --runtime60 --time_based --group_reporting --filenametestfile # 随机写IOPS更考验qcow2性能 fio --namerandwrite --ioenginelibaio --rwrandwrite --bs4k --direct1 \ --size2G --runtime60 --time_based --group_reporting --filenametestfile关键参数解读--direct1绕过page cache测真实磁盘性能--runtime60固定时间而非固定数据量避免小文件测试偏差--group_reporting汇总所有线程结果注意测试前用fio --nametrim --ioenginelibaio --rwtrim --bs4k --filenametestfile清理SSD缓存。7.3 网络延迟测试——iperf3的隐藏参数# Server端guest中 iperf3 -s