浏览代码

feat(pipe): 增强FIFO管道的读写逻辑,解决问题一:非阻塞模式下的无写端错误返回

xiaolin2004 4 月之前
父节点
当前提交
eb7e833d78

+ 59 - 7
kernel/src/ipc/pipe.rs

@@ -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,
@@ -8,7 +11,7 @@ use crate::{
         wait_queue::WaitQueue,
     },
     net::event_poll::{EPollEventType, EPollItem, EventPoll},
-    process::ProcessState,
+    process::{ProcessManager, ProcessState},
     sched::SchedMode,
     time::PosixTimeSpec,
 };
@@ -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;
 
@@ -59,6 +64,7 @@ pub struct InnerPipeInode {
     metadata: Metadata,
     reader: u32,
     writer: u32,
+    had_reader: bool,
     epitems: SpinLock<LinkedList<Arc<EPollItem>>>,
 }
 
@@ -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 {
@@ -278,15 +285,31 @@ 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() {
             return Err(SystemError::EACCES);
         }
-        if mode.contains(FileMode::O_RDONLY) {
+        else if accmode==FileMode::O_RDONLY.bits() {
             guard.reader += 1;
+            guard.had_reader = true;
+            println!(
+                "FIFO:     pipe try open in read mode with reader pid:{:?}",
+                ProcessManager::current_pid()
+            );
         }
-        if mode.contains(FileMode::O_WRONLY) {
+        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 {
+                if mode.contains(FileMode::O_NONBLOCK) {
+                    return Err(SystemError::ENXIO);
+                }
+            }
             guard.writer += 1;
         }
 
@@ -311,10 +334,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;
             // 如果已经没有写端了,则唤醒读端
@@ -325,7 +349,7 @@ impl IndexNode for LockedPipeInode {
         }
 
         // 读端关闭
-        if mode.contains(FileMode::O_RDONLY) {
+        if accmode==FileMode::O_RDONLY.bits() {
             assert!(guard.reader > 0);
             guard.reader -= 1;
             // 如果已经没有写端了,则唤醒读端
@@ -361,7 +385,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);
+                    }
+                }
+            }
         }
 
         // 如果管道空间不够

+ 20 - 0
user/apps/test_fifo_write/Makefile

@@ -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:

+ 37 - 0
user/apps/test_fifo_write/main.c

@@ -0,0 +1,37 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/stat.h>
+
+#define FIFO_PATH "/bin/test_fifo"
+
+int main() {
+    // 创建 FIFO
+    if (mkfifo(FIFO_PATH, 0666) == -1 && errno != EEXIST) {
+        perror("mkfifo failed");
+        exit(EXIT_FAILURE);
+    }
+
+    printf("Opening FIFO in write mode...\n");
+
+    // 尝试以非阻塞模式打开 FIFO 的写端
+    int fd = open(FIFO_PATH, O_WRONLY|O_NONBLOCK);
+    printf("fd: %d\n",fd);
+    if (fd == -1) {
+        if (errno == ENXIO) {
+            printf("Error: No readers (ENXIO).\n");
+        } else {
+            perror("Failed to open FIFO");
+        }
+    } else {
+        printf("FIFO opened successfully in write mode.\n");
+        close(fd);
+    }
+
+    // 删除 FIFO
+    unlink(FIFO_PATH);
+
+    return 0;
+}

+ 41 - 0
user/dadk/config/test_fifo_write_0_1_0.toml

@@ -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 = "/"
+
+# clean相关信息
+[clean]
+# (可选)清除命令
+clean-command = "make clean"