Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix:命名管道读行为不符合posix规范问题 #1061

Merged
merged 10 commits into from
Nov 28, 2024
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 57 additions & 9 deletions kernel/src/ipc/pipe.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
use core::sync::atomic::compiler_fence;

use crate::{
arch::ipc::signal::{SigCode, Signal},
filesystem::vfs::{
core::generate_inode_id, file::FileMode, syscall::ModeType, FilePrivateData, FileSystem,
FileType, IndexNode, Metadata,
Expand All @@ -8,7 +11,7 @@ use crate::{
wait_queue::WaitQueue,
},
net::event_poll::{EPollEventType, EPollItem, EventPoll},
process::ProcessState,
process::{ProcessManager, ProcessState},
sched::SchedMode,
time::PosixTimeSpec,
};
Expand All @@ -20,6 +23,8 @@ use alloc::{
};
use system_error::SystemError;

use super::signal_types::{SigInfo, SigType};

/// 我们设定pipe_buff的总大小为1024字节
const PIPE_BUFF_SIZE: usize = 1024;

Expand Down Expand Up @@ -59,6 +64,7 @@ pub struct InnerPipeInode {
metadata: Metadata,
reader: u32,
writer: u32,
had_reader: bool,
epitems: SpinLock<LinkedList<Arc<EPollItem>>>,
}

Expand Down Expand Up @@ -131,6 +137,7 @@ impl LockedPipeInode {
valid_cnt: 0,
read_pos: 0,
write_pos: 0,
had_reader: false,
data: [0; PIPE_BUFF_SIZE],

metadata: Metadata {
Expand Down Expand Up @@ -278,15 +285,27 @@ impl IndexNode for LockedPipeInode {
mut data: SpinLockGuard<FilePrivateData>,
mode: &crate::filesystem::vfs::file::FileMode,
) -> Result<(), SystemError> {
let accmode = mode.accmode();
let mut guard = self.inner.lock();
// 不能以读写方式打开管道
if mode.contains(FileMode::O_RDWR) {
if accmode == FileMode::O_RDWR.bits() {
Samuka007 marked this conversation as resolved.
Show resolved Hide resolved
return Err(SystemError::EACCES);
}
if mode.contains(FileMode::O_RDONLY) {
} else if accmode == FileMode::O_RDONLY.bits() {
guard.reader += 1;
}
if mode.contains(FileMode::O_WRONLY) {
guard.had_reader = true;
// println!(
// "FIFO: pipe try open in read mode with reader pid:{:?}",
// ProcessManager::current_pid()
// );
} else if accmode == FileMode::O_WRONLY.bits() {
// println!(
// "FIFO: pipe try open in write mode with {} reader, writer pid:{:?}",
// guard.reader,
// ProcessManager::current_pid()
// );
if guard.reader == 0 && mode.contains(FileMode::O_NONBLOCK) {
return Err(SystemError::ENXIO);
}
guard.writer += 1;
}

Expand All @@ -311,10 +330,11 @@ impl IndexNode for LockedPipeInode {
} else {
return Err(SystemError::EBADF);
}
let accmode = mode.accmode();
let mut guard = self.inner.lock();

// 写端关闭
if mode.contains(FileMode::O_WRONLY) {
if accmode == FileMode::O_WRONLY.bits() {
assert!(guard.writer > 0);
guard.writer -= 1;
// 如果已经没有写端了,则唤醒读端
Expand All @@ -325,7 +345,7 @@ impl IndexNode for LockedPipeInode {
}

// 读端关闭
if mode.contains(FileMode::O_RDONLY) {
if accmode == FileMode::O_RDONLY.bits() {
assert!(guard.reader > 0);
guard.reader -= 1;
// 如果已经没有写端了,则唤醒读端
Expand Down Expand Up @@ -361,7 +381,35 @@ impl IndexNode for LockedPipeInode {
let mut inode = self.inner.lock();

if inode.reader == 0 {
// TODO: 如果已经没有读端存在了,则向写端进程发送SIGPIPE信号
if !inode.had_reader {
// 如果从未有读端,直接返回 ENXIO,无论是否阻塞模式
return Err(SystemError::ENXIO);
} else {
// 如果曾经有读端,现在已关闭
match mode.contains(FileMode::O_NONBLOCK) {
true => {
// 非阻塞模式,直接返回 EPIPE
return Err(SystemError::EPIPE);
}
false => {
let sig = Signal::SIGPIPE;
let mut info = SigInfo::new(
sig,
0,
SigCode::Kernel,
SigType::Kill(ProcessManager::current_pid()),
);
compiler_fence(core::sync::atomic::Ordering::SeqCst);

let _retval = sig
.send_signal_info(Some(&mut info), ProcessManager::current_pid())
.map(|x| x as usize);

compiler_fence(core::sync::atomic::Ordering::SeqCst);
return Err(SystemError::EPIPE);
}
}
}
}

// 如果管道空间不够
Expand Down
20 changes: 20 additions & 0 deletions user/apps/test_fifo_write/Makefile
Original file line number Diff line number Diff line change
@@ -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_fifo_write main.c

.PHONY: install clean
install: all
mv test_fifo_write $(DADK_CURRENT_BUILD_DIR)/test_fifo_write

clean:
rm test_fifo_write *.o

fmt:
137 changes: 137 additions & 0 deletions user/apps/test_fifo_write/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <sys/stat.h>
#include <sys/wait.h>

#define FIFO_PATH "/bin/test_fifo" // 使用 /tmp 目录避免权限问题

// 信号处理函数
void sigpipe_handler(int signo) {
if (signo == SIGPIPE) {
printf("Received SIGPIPE signal. Write operation failed.\n");
}
}

void test_fifo_write(const char *scenario_desc, int nonblocking) {
int fd;
char *data = "Hello, FIFO!";
printf("\n--- Testing: %s (nonblocking=%d) ---\n", scenario_desc, nonblocking);

// 设置写模式和非阻塞模式标志
int flags = O_WRONLY;
if (nonblocking) {
flags |= O_NONBLOCK;
}

// 打开 FIFO 写端
fd = open(FIFO_PATH, flags);
if (fd == -1) {
if (errno == ENXIO) {
printf("Result: Failed to open FIFO for writing (ENXIO: No readers).\n");
} else {
perror("Failed to open FIFO for writing");
}
return;
}

// 写入数据
ssize_t bytes_written = write(fd, data, sizeof(data));
if (bytes_written == -1) {
if (errno == EPIPE) {
printf("Result: Write failed with EPIPE (no readers available).\n");
} else if (errno == ENXIO) {
printf("Result: Write failed with ENXIO (FIFO never had readers).\n");
} else if (errno == EAGAIN) {
printf("Result: Write failed with EAGAIN (nonblocking write, pipe full or no readers).\n");
} else {
perror("Write failed with an unexpected error");
}
} else {
printf("Result: Write succeeded. Bytes written: %zd\n", bytes_written);
}

// 关闭 FIFO 写端
close(fd);
}

void test_case1(int nonblocking) {
// Case 1: No readers (FIFO never had readers)
test_fifo_write("No readers (FIFO never had readers)", nonblocking);
}

void test_case2(int nonblocking) {
pid_t reader_pid;

// Case 2: Reader exists but disconnects
reader_pid = fork();
if (reader_pid == 0) {
// 子进程充当读端
int reader_fd = open(FIFO_PATH, O_RDONLY);
if (reader_fd == -1) {
perror("Reader failed to open FIFO");
exit(EXIT_FAILURE);
}
sleep(1); // 模拟读端短暂存在
close(reader_fd);
exit(EXIT_SUCCESS);
}

sleep(5); // 确保读端已打开
test_fifo_write("Reader exists but disconnects", nonblocking);
waitpid(reader_pid, NULL, 0); // 等待读端子进程退出
}

void test_case3(int nonblocking) {
pid_t reader_pid;

// Case 3: Active reader exists
reader_pid = fork();
if (reader_pid == 0) {
// 子进程充当读端
int reader_fd = open(FIFO_PATH, O_RDONLY);
if (reader_fd == -1) {
perror("Reader failed to open FIFO");
exit(EXIT_FAILURE);
}
sleep(5); // 保持读端存在
close(reader_fd);
exit(EXIT_SUCCESS);
}

sleep(1); // 确保读端已打开
test_fifo_write("Active reader exists", nonblocking);
waitpid(reader_pid, NULL, 0); // 等待读端子进程退出
}

int main() {
// 设置 SIGPIPE 信号处理
signal(SIGPIPE, sigpipe_handler);

// 创建 FIFO
if (mkfifo(FIFO_PATH, 0666) == -1 && errno != EEXIST) {
perror("mkfifo failed");
exit(EXIT_FAILURE);
}

// 测试阻塞模式下的三种情况
// printf("========== Testing Blocking Mode ==========\n");
// test_case1(0); // 阻塞模式下没有读端
// test_case2(0); // 阻塞模式下读端断开
// test_case3(0); // 阻塞模式下读端存在

// 测试非阻塞模式下的三种情况
printf("\n========== Testing Nonblocking Mode ==========\n");
test_case1(1); // 非阻塞模式下没有读端
test_case2(1); // 非阻塞模式下读端断开
test_case3(1); // 非阻塞模式下读端存在

// 删除 FIFO
unlink(FIFO_PATH);

printf("\nAll tests completed.\n");
return 0;
}
41 changes: 41 additions & 0 deletions user/dadk/config/test_fifo_write_0_1_0.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# 用户程序名称
name = "test_fifo_write"
# 版本号
version = "0.1.0"
# 用户程序描述信息
description = "一个用来测试fifo_write行为的app"

# (可选)默认: false 是否只构建一次,如果为true,DADK会在构建成功后,将构建结果缓存起来,下次构建时,直接使用缓存的构建结果
build-once = false
# (可选) 默认: false 是否只安装一次,如果为true,DADK会在安装成功后,不再重复安装
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_fifo_write"

# 构建相关信息
[build]
# (可选)构建命令
build-command = "make install"

# 安装相关信息
[install]
# (可选)安装到DragonOS的路径
in-dragonos-path = "/bin"

# clean相关信息
[clean]
# (可选)清除命令
clean-command = "make clean"