feat: 实现poll系统调用实现并修复相关bug (#1098)

feat: 实现poll系统调用实现并修复相关bug

- 实现poll系统调用,增加对EPollEvent的处理逻辑
- 修复LockedPipeInode中epitems的锁管理问题
- 添加RestartBlock支持,处理系统调用重启逻辑
- 修复EventPoll中epoll_wait的超时处理逻辑
- 新增test_poll测试程序,验证poll功能

Signed-off-by: longjin <longjin@DragonOS.org>
This commit is contained in:
LoGin
2025-03-14 10:48:06 +08:00
committed by GitHub
parent 65f6119c9f
commit 488718dc2e
17 changed files with 743 additions and 158 deletions

1
user/apps/test_poll/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
test_poll

View File

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

151
user/apps/test_poll/main.c Normal file
View File

@ -0,0 +1,151 @@
#include <errno.h>
#include <poll.h>
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <unistd.h>
int pipe_fd[2]; // 管道文件描述符数组
int child_can_exit = 0; // 子进程是否可以退出的标志
int signal_pid = 0;
int poll_errno; // poll错误码
#define WRITE_WAIT_SEC 3
#define POLL_TIMEOUT_SEC 5
#define EXPECTED_MESSAGE "Data is ready!\n"
#define POLL_DELTA_MS 1000
#define min(a, b) ((a) < (b) ? (a) : (b))
// 信号处理函数
void signal_handler(int signo) {
printf("[PID: %d, TID: %lu] Signal %d received.\n", getpid(), pthread_self(),
signo);
}
// 线程函数用于在n秒后向管道写入数据
void *writer_thread(void *arg) {
int seconds = WRITE_WAIT_SEC;
for (int i = 0; i < seconds; i++) {
printf("[PID: %d, TID: %lu] Waiting for %d seconds...\n", getpid(),
pthread_self(), seconds - i);
sleep(1);
kill(signal_pid, SIGUSR1); // 发送信号
}
const char *message = EXPECTED_MESSAGE;
write(pipe_fd[1], message, strlen(message)); // 写入管道
printf("[PID: %d, TID: %lu] Data written to pipe.\n", getpid(),
pthread_self());
close(pipe_fd[1]); // 关闭写端
printf("[PID: %d, TID: %lu] Pipe write end closed.\n", getpid(),
pthread_self());
while (child_can_exit == 0) {
printf("[PID: %d, TID: %lu] Waiting for main to finish...\n", getpid(),
pthread_self());
sleep(1);
}
return NULL;
}
int main() {
pthread_t tid;
struct pollfd fds[1];
int ret;
int test_passed = 1; // 假设测试通过
// 创建管道
if (pipe(pipe_fd) == -1) {
perror("pipe");
exit(EXIT_FAILURE);
}
// 设置信号处理函数
struct sigaction sa;
sa.sa_handler = signal_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
if (sigaction(SIGUSR1, &sa, NULL) == -1) {
perror("sigaction");
exit(EXIT_FAILURE);
}
signal_pid = getpid(); // 设置信号接收进程ID
// 创建写线程
if (pthread_create(&tid, NULL, writer_thread, NULL) != 0) {
perror("pthread_create");
exit(EXIT_FAILURE);
}
// 设置poll监视的文件描述符
fds[0].fd = pipe_fd[0]; // 监视管道的读端
fds[0].events = POLLIN; // 监视是否有数据可读
printf("[PID: %d, TID: %lu] Waiting for data...\n", getpid(), pthread_self());
// 在 poll 调用前后添加时间统计
struct timeval start_time, end_time;
gettimeofday(&start_time, NULL); // 记录 poll 开始时间
ret = poll(fds, 1, POLL_TIMEOUT_SEC * 1000); // 调用 poll
poll_errno = errno;
gettimeofday(&end_time, NULL); // 记录 poll 结束时间
// 计算 poll 的总耗时(单位:毫秒)
long poll_duration_ms = (end_time.tv_sec - start_time.tv_sec) * 1000 +
(end_time.tv_usec - start_time.tv_usec) / 1000;
if (abs((int)poll_duration_ms -
min(POLL_TIMEOUT_SEC, WRITE_WAIT_SEC) * 1000) >= POLL_DELTA_MS) {
printf("Poll duration: %ld ms, expected: %d ms, errno: %s\n",
poll_duration_ms, POLL_TIMEOUT_SEC * 1000, strerror(poll_errno));
test_passed = 0; // 测试失败(如果 poll 耗时与预期相差较大,认为测试未通过)
}
if (test_passed == 0) {
} else if (ret == -1) {
printf("poll errno: %s\n", strerror(poll_errno));
test_passed = 0; // 测试失败
} else if (ret == 0) {
printf("Timeout! No data available.\n");
test_passed = 0; // 测试失败
} else {
if (fds[0].revents & POLLIN) {
char buffer[1024];
ssize_t count = read(pipe_fd[0], buffer, sizeof(buffer)); // 读取数据
if (count > 0) {
printf("Data received: %s", buffer);
// 检查读取的数据是否与预期一致
if (strcmp(buffer, EXPECTED_MESSAGE) != 0) {
printf("Unexpected data received.\n");
test_passed = 0; // 测试失败
}
} else {
printf("No data read from pipe.\n");
test_passed = 0; // 测试失败
}
} else {
printf("Unexpected event on pipe.\n");
test_passed = 0; // 测试失败
}
}
child_can_exit = 1; // 允许子进程退出
// 等待写线程结束
pthread_join(tid, NULL);
close(pipe_fd[0]); // 关闭读端
if (test_passed) {
printf("Test passed!\n");
} else {
printf("Test failed!\n");
}
printf("Program finished.\n");
return test_passed ? 0 : 1; // 返回0表示测试通过返回1表示测试失败
}