feat:添加sigprocmask系统调用 (#1046)

* 添加sigprocmask系统调用

---------

Signed-off-by: sparkzky <sparkhhhhhhhhh@outlook.com>
Co-authored-by: longjin <longjin@DragonOS.org>
This commit is contained in:
火花 2024-12-07 16:36:55 +08:00 committed by GitHub
parent 4f8f269baf
commit 6e85059fbc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 407 additions and 22 deletions

View File

@ -12,7 +12,7 @@ use crate::{
},
exception::InterruptArch,
ipc::{
signal::set_current_sig_blocked,
signal::set_current_blocked,
signal_types::{SaHandlerType, SigInfo, Sigaction, SigactionType, SignalArch},
},
mm::MemoryManagementArch,
@ -511,7 +511,7 @@ impl SignalArch for X86_64SignalArch {
return trap_frame.rax;
}
let mut sigmask: SigSet = unsafe { (*frame).context.oldmask };
set_current_sig_blocked(&mut sigmask);
set_current_blocked(&mut sigmask);
// 从用户栈恢复sigcontext
if !unsafe { &mut (*frame).context }.restore_sigcontext(trap_frame) {
error!("unable to restore sigcontext");

View File

@ -441,18 +441,46 @@ pub(super) fn do_sigaction(
return Ok(());
}
/// 设置当前进程的屏蔽信号 (sig_block),待引入 [sigprocmask](https://man7.org/linux/man-pages/man2/sigprocmask.2.html) 系统调用后要删除这个散装函数
///
/// ## 参数
///
/// - `new_set` 新的屏蔽信号bitmap的值
pub fn set_current_sig_blocked(new_set: &mut SigSet) {
let to_remove: SigSet =
<Signal as Into<SigSet>>::into(Signal::SIGKILL) | Signal::SIGSTOP.into();
new_set.remove(to_remove);
//TODO 把这个散装函数用 sigsetops 替换掉
let pcb = ProcessManager::current_pcb();
/// https://code.dragonos.org.cn/xref/linux-6.6.21/include/uapi/asm-generic/signal-defs.h#72
/// 对应SIG_BLOCKSIG_UNBLOCKSIG_SETMASK
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SigHow {
Block = 0,
Unblock = 1,
SetMask = 2,
}
impl TryFrom<i32> for SigHow {
type Error = SystemError;
fn try_from(value: i32) -> Result<Self, Self::Error> {
match value {
0 => Ok(SigHow::Block),
1 => Ok(SigHow::Unblock),
2 => Ok(SigHow::SetMask),
_ => Err(SystemError::EINVAL),
}
}
}
fn __set_task_blocked(pcb: &Arc<ProcessControlBlock>, new_set: &SigSet) {
//todo 还有一个对线程组是否为空的判断,进程组、线程组实现之后,需要更改这里。
if pcb.has_pending_signal() {
let mut newblocked = *new_set;
let guard = pcb.sig_info_irqsave();
newblocked.remove(*guard.sig_block());
drop(guard);
// 从主线程开始去遍历
if let Some(group_leader) = pcb.threads_read_irqsave().group_leader() {
retarget_shared_pending(group_leader, newblocked);
}
}
*pcb.sig_info_mut().sig_block_mut() = *new_set;
recalc_sigpending();
}
fn __set_current_blocked(new_set: &SigSet) {
let pcb = ProcessManager::current_pcb();
/*
pcb的sig_blocked和新的相等
sig_blocked字段不能被其他进程修改
@ -460,12 +488,97 @@ pub fn set_current_sig_blocked(new_set: &mut SigSet) {
if pcb.sig_info_irqsave().sig_block().eq(new_set) {
return;
}
let guard = pcb.sig_struct_irqsave();
// todo: 当一个进程有多个线程后在这里需要设置每个线程的block字段并且 retarget_shared_pending虽然我还没搞明白linux这部分是干啥的
// 设置当前进程的sig blocked
*pcb.sig_info_mut().sig_block_mut() = *new_set;
recalc_sigpending();
__set_task_blocked(&pcb, new_set);
drop(guard);
}
fn retarget_shared_pending(pcb: Arc<ProcessControlBlock>, which: SigSet) {
let retarget = pcb.sig_info_irqsave().sig_shared_pending().signal();
retarget.intersects(which);
if retarget.is_empty() {
return;
}
// 对于线程组中的每一个线程都要执行的函数
let thread_handling_function = |pcb: Arc<ProcessControlBlock>, retarget: &SigSet| {
if retarget.is_empty() {
return;
}
if pcb.flags().contains(ProcessFlags::EXITING) {
return;
}
let blocked = pcb.sig_info_irqsave().sig_shared_pending().signal();
if retarget.difference(blocked).is_empty() {
return;
}
retarget.intersects(blocked);
if !pcb.has_pending_signal() {
let guard = pcb.sig_struct_irqsave();
signal_wake_up(pcb.clone(), guard, false);
}
// 之前的对retarget的判断移动到最前面因为对于当前线程的线程的处理已经结束对于后面的线程在一开始判断retarget为空即可结束处理
// debug!("handle done");
};
// 暴力遍历每一个线程找到相同的tgid
let tgid = pcb.tgid();
for &pid in pcb.children_read_irqsave().iter() {
if let Some(child) = ProcessManager::find(pid) {
if child.tgid() == tgid {
thread_handling_function(child, &retarget);
}
}
}
// debug!("retarget_shared_pending done!");
}
/// 设置当前进程的屏蔽信号 (sig_block)
///
/// ## 参数
///
/// - `new_set` 新的屏蔽信号bitmap的值
pub fn set_current_blocked(new_set: &mut SigSet) {
let to_remove: SigSet =
<Signal as Into<SigSet>>::into(Signal::SIGKILL) | Signal::SIGSTOP.into();
new_set.remove(to_remove);
__set_current_blocked(new_set);
}
/// 设置当前进程的屏蔽信号 (sig_block)
///
/// ## 参数
///
/// - `how` 设置方式
/// - `new_set` 新的屏蔽信号bitmap的值
pub fn set_sigprocmask(how: SigHow, set: SigSet) -> Result<SigSet, SystemError> {
let pcb: Arc<ProcessControlBlock> = ProcessManager::current_pcb();
let guard = pcb.sig_info_irqsave();
let oset = *guard.sig_block();
let mut res_set = oset;
drop(guard);
match how {
SigHow::Block => {
// debug!("SIG_BLOCK\tGoing to insert is: {}", set.bits());
res_set.insert(set);
}
SigHow::Unblock => {
res_set.remove(set);
}
SigHow::SetMask => {
// debug!("SIG_SETMASK\tGoing to set is: {}", set.bits());
res_set = set;
}
}
__set_current_blocked(&res_set);
Ok(oset)
}

View File

@ -35,6 +35,7 @@ use crate::{
use super::{
pipe::{LockedPipeInode, PipeFsPrivateData},
shm::{ShmCtlCmd, ShmFlags, ShmId, ShmKey},
signal::{set_sigprocmask, SigHow},
signal_types::{
SaHandlerType, SigInfo, SigType, Sigaction, SigactionType, UserSigaction, USER_SIG_DFL,
USER_SIG_ERR, USER_SIG_IGN,
@ -504,4 +505,66 @@ impl Syscall {
ShmCtlCmd::Default => Err(SystemError::EINVAL),
}
}
/// # SYS_SIGPROCMASK系统调用函数用于设置或查询当前进程的信号屏蔽字
///
/// ## 参数
///
/// - `how`: 指示如何修改信号屏蔽字
/// - `nset`: 新的信号屏蔽字
/// - `oset`: 旧的信号屏蔽字的指针由于可以是NULL所以用Option包装
/// - `sigsetsize`: 信号集的大小
///
/// ## 返回值
///
/// 成功0
/// 失败:错误码
///
/// ## 说明
/// 根据 https://man7.org/linux/man-pages/man2/sigprocmask.2.html 传进来的oldset和newset都是指针类型这里选择传入usize然后转换为u64的指针类型
pub fn rt_sigprocmask(
how: i32,
newset: usize,
oldset: usize,
sigsetsize: usize,
) -> Result<usize, SystemError> {
// 对应oset传进来一个NULL的情况
let oset = if oldset == 0 { None } else { Some(oldset) };
let nset = if newset == 0 { None } else { Some(newset) };
if sigsetsize != size_of::<SigSet>() {
return Err(SystemError::EFAULT);
}
let sighow = SigHow::try_from(how)?;
let mut new_set = SigSet::default();
if let Some(nset) = nset {
let reader = UserBufferReader::new(
VirtAddr::new(nset).as_ptr::<u64>(),
core::mem::size_of::<u64>(),
true,
)?;
let nset = reader.read_one_from_user::<u64>(0)?;
new_set = SigSet::from_bits_truncate(*nset);
// debug!("Get Newset: {}", &new_set.bits());
let to_remove: SigSet =
<Signal as Into<SigSet>>::into(Signal::SIGKILL) | Signal::SIGSTOP.into();
new_set.remove(to_remove);
}
let oldset_to_return = set_sigprocmask(sighow, new_set)?;
if let Some(oldset) = oset {
// debug!("Get Oldset to return: {}", &oldset_to_return.bits());
let mut writer = UserBufferWriter::new(
VirtAddr::new(oldset).as_ptr::<u64>(),
core::mem::size_of::<u64>(),
true,
)?;
writer.copy_one_to_user::<u64>(&oldset_to_return.bits(), 0)?;
}
Ok(0)
}
}

View File

@ -3,7 +3,7 @@ use system_error::SystemError;
use crate::{
arch::ipc::signal::SigSet,
filesystem::vfs::file::FileMode,
ipc::signal::set_current_sig_blocked,
ipc::signal::set_current_blocked,
mm::VirtAddr,
syscall::{
user_access::{UserBufferReader, UserBufferWriter},
@ -96,7 +96,7 @@ impl Syscall {
sigmask: &mut SigSet,
) -> Result<usize, SystemError> {
// 设置屏蔽的信号
set_current_sig_blocked(sigmask);
set_current_blocked(sigmask);
let wait_ret = Self::epoll_wait(epfd, epoll_event, max_events, timespec);

View File

@ -1061,6 +1061,14 @@ impl ProcessControlBlock {
fn exit_files(&self) {
self.basic.write_irqsave().set_fd_table(None);
}
pub fn children_read_irqsave(&self) -> RwLockReadGuard<Vec<Pid>> {
self.children.read_irqsave()
}
pub fn threads_read_irqsave(&self) -> RwLockReadGuard<ThreadInfo> {
self.thread.read_irqsave()
}
}
impl Drop for ProcessControlBlock {
@ -1092,6 +1100,12 @@ pub struct ThreadInfo {
group_leader: Weak<ProcessControlBlock>,
}
impl Default for ThreadInfo {
fn default() -> Self {
Self::new()
}
}
impl ThreadInfo {
pub fn new() -> Self {
Self {

View File

@ -879,8 +879,11 @@ impl Syscall {
}
SYS_RT_SIGPROCMASK => {
warn!("SYS_RT_SIGPROCMASK has not yet been implemented");
Ok(0)
let how = args[0] as i32;
let nset = args[1];
let oset = args[2];
let sigsetsize = args[3];
Self::rt_sigprocmask(how, nset, oset, sigsetsize)
}
SYS_TKILL => {

1
user/apps/test-sigprocmask/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
test-sigprocmask

View File

@ -0,0 +1,20 @@
ifeq ($(ARCH), x86_64)
CROSS_COMPILE=x86_64-linux-musl-
else ifeq ($(ARCH), riscv64)
CROSS_COMPILE=riscv64-linux-musl-
endif
CC=$(CROSS_COMPILE)gcc
.PHONY: all
all: main.c
$(CC) -static -o test-sigprocmask main.c
.PHONY: install clean
install: all
mv test-sigprocmask $(DADK_CURRENT_BUILD_DIR)/test-sigprocmask
clean:
rm test-sigprocmask *.o
fmt:

View File

@ -0,0 +1,130 @@
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define TEST_ASSERT(left, right, success_msg, fail_msg) \
do { \
if ((left) == (right)) { \
printf("[PASS] %s\n", success_msg); \
} else { \
printf("[FAIL] %s: Expected 0x%lx, but got 0x%lx\n", \
fail_msg, \
(unsigned long)(right), \
(unsigned long)(left)); \
} \
} while (0)
static int signal_received = 0;
void signal_handler(int signo) {
if (signo == SIGINT) {
printf("\nReceived SIGINT (Ctrl+C)\n");
signal_received = 1;
}
}
void print_signal_mask(const char *msg, const sigset_t *mask) {
printf("%s: ", msg);
for (int signo = 1; signo < NSIG; ++signo) {
if (sigismember(mask, signo)) {
printf("%d ", signo);
}
}
printf("\n");
}
// 获取当前屏蔽字的函数
unsigned long get_signal_mask() {
sigset_t sigset;
if (sigprocmask(SIG_BLOCK, NULL, &sigset) == -1) {
perror("sigprocmask");
return -1; // 返回错误标记
}
// 将信号集编码为位掩码
unsigned long mask = 0;
for (int i = 1; i < NSIG; i++) {
if (sigismember(&sigset, i)) {
mask |= 1UL << (i - 1);
}
}
return mask;
}
int main() {
sigset_t new_mask, old_mask;
sigemptyset(&old_mask);
// 注册 SIGINT 的信号处理函数
if (signal(SIGINT, signal_handler) == SIG_ERR) {
perror("signal");
exit(EXIT_FAILURE);
}
signal_received = 0;
kill(getpid(), SIGINT);
TEST_ASSERT(signal_received, 1, "SIGINT was received", "SIGINT was not received");
signal_received = 0;
// 初始化新的信号集,并将 SIGINT 添加到其中
sigemptyset(&new_mask);
sigaddset(&new_mask, SIGINT);
// 打印 new_mask 的值
print_signal_mask("new_mask", &new_mask);
// 屏蔽 SIGINT
if (sigprocmask(SIG_BLOCK, &new_mask, &old_mask) < 0) {
perror("sigprocmask - SIG_BLOCK");
exit(EXIT_FAILURE);
}
// 打印 old_mask 的值
print_signal_mask("old_mask", &old_mask);
// 检查 SIGINT 是否被屏蔽
unsigned long actual_mask = get_signal_mask();
unsigned long expected_mask = (1UL << (SIGINT - 1));
TEST_ASSERT(actual_mask,
expected_mask,
"Signal mask is as expected",
"Signal mask mismatch");
printf("SIGINT is now blocked.\n");
signal_received = 0;
// 向当前进程发送 SIGINT
kill(getpid(), SIGINT);
// 等待 5 秒,以便测试 SIGINT 是否被屏蔽
sleep(5);
TEST_ASSERT(signal_received, 0, "SIGINT was blocked", "SIGINT was not blocked");
signal_received = 0;
// 恢复原来的信号屏蔽字
if (sigprocmask(SIG_SETMASK, &old_mask, &old_mask) < 0) {
perror("sigprocmask - SIG_SETMASK");
exit(EXIT_FAILURE);
}
print_signal_mask("old_mask returned", &old_mask);
// 检查 SIGINT 是否被解除屏蔽
actual_mask = get_signal_mask();
expected_mask = 0;
TEST_ASSERT(actual_mask,
expected_mask,
"Signal mask is as expected",
"Signal mask mismatch");
printf("SIGINT is now unblocked.\n");
signal_received = 0;
kill(getpid(), SIGINT);
// 等待 5 秒,以便测试 SIGINT 是否解除屏蔽
sleep(5);
TEST_ASSERT(signal_received, 1, "SIGINT was received", "SIGINT was not received");
printf("Exiting program.\n");
return 0;
}

View File

@ -0,0 +1,41 @@
# 用户程序名称
name = "test_sigprocmask"
# 版本号
version = "0.1.0"
# 用户程序描述信息
description = "一个用来测试sigprocmask能够正常运行的app"
# (可选)默认: false 是否只构建一次如果为trueDADK会在构建成功后将构建结果缓存起来下次构建时直接使用缓存的构建结果
build-once = false
# (可选) 默认: false 是否只安装一次如果为trueDADK会在安装成功后不再重复安装
install-once = false
# 目标架构
# 可选值:"x86_64", "aarch64", "riscv64"
target-arch = ["x86_64"]
# 任务源
[task-source]
# 构建类型
# 可选值:"build-from_source", "install-from-prebuilt"
type = "build-from-source"
# 构建来源
# "build_from_source" 可选值:"git", "local", "archive"
# "install_from_prebuilt" 可选值:"local", "archive"
source = "local"
# 路径或URL
source-path = "user/apps/test-sigprocmask"
# 构建相关信息
[build]
# (可选)构建命令
build-command = "make install"
# 安装相关信息
[install]
# 可选安装到DragonOS的路径
in-dragonos-path = "/bin"
# 清除相关信息
[clean]
# (可选)清除命令
clean-command = "make clean"
# (可选)依赖项
# 注意:如果没有依赖项,忽略此项,不允许只留一个[[depends]]
# 由于原文件中依赖项为空,此处省略[[depends]]部分
# (可选)环境变量
# 由于原文件中环境变量为空,此处省略[[envs]]部分