Merge branch 'master' into feat-network-rebuild

This commit is contained in:
2025-03-18 20:43:41 +08:00
committed by GitHub
73 changed files with 14648 additions and 312 deletions

View File

@ -1,115 +1,540 @@
/**
* @file main.c
* @author xiaoyez (xiaoyez@zju.edu.cn)
* @brief 测试kvm的程序
* @version 0.1
* @date 2023-07-13
*
* @copyright Copyright (c) 2023
*
*/
/**
* 测试kvm命令的方法:
* 1.在DragonOS的控制台输入 exec bin/test_kvm.elf
*
*/
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <fcntl.h>
//#include <linux/kvm.h>
#define KVM_CREATE_VCPU 0x00
#define KVM_SET_USER_MEMORY_REGION 0x01
typedef __signed__ char __s8;
typedef unsigned char __u8;
#define KVM_RUN 0x00
#define KVM_GET_REGS 0x01
#define KVM_SET_REGS 0x02
typedef __signed__ short __s16;
typedef unsigned short __u16;
struct kvm_userspace_memory_region {
uint32_t slot; // 要在哪个slot上注册内存区间
// flags有两个取值KVM_MEM_LOG_DIRTY_PAGES和KVM_MEM_READONLY用来指示kvm针对这段内存应该做的事情。
// KVM_MEM_LOG_DIRTY_PAGES用来开启内存脏页KVM_MEM_READONLY用来开启内存只读。
uint32_t flags;
uint64_t guest_phys_addr; // 虚机内存区间起始物理地址
uint64_t memory_size; // 虚机内存区间大小
uint64_t userspace_addr; // 虚机内存区间对应的主机虚拟地址
typedef __signed__ int __s32;
typedef unsigned int __u32;
#ifdef __GNUC__
__extension__ typedef __signed__ long long __s64;
__extension__ typedef unsigned long long __u64;
#else
typedef __signed__ long long __s64;
typedef unsigned long long __u64;
#endif
//from linux/kvm.h
#define KVM_CREATE_VM _IO(KVMIO, 0x01) /* returns a VM fd */
#define KVM_CREATE_VCPU _IO(KVMIO, 0x41)
#define KVM_GET_VCPU_MMAP_SIZE _IO(KVMIO, 0x04) /* in bytes */
#define KVM_RUN _IO(KVMIO, 0x80)
#define KVM_GET_REGS _IOR(KVMIO, 0x81, struct kvm_regs)
#define KVM_SET_REGS _IOW(KVMIO, 0x82, struct kvm_regs)
#define KVM_GET_SREGS _IOR(KVMIO, 0x83, struct kvm_sregs)
#define KVM_SET_SREGS _IOW(KVMIO, 0x84, struct kvm_sregs)
#define KVMIO 0xAE
#define KVM_SET_USER_MEMORY_REGION _IOW(KVMIO, 0x46, \
struct kvm_userspace_memory_region)
/* Architectural interrupt line count. */
#define KVM_NR_INTERRUPTS 256
struct kvm_hyperv_exit {
#define KVM_EXIT_HYPERV_SYNIC 1
#define KVM_EXIT_HYPERV_HCALL 2
#define KVM_EXIT_HYPERV_SYNDBG 3
__u32 type;
__u32 pad1;
union {
struct {
__u32 msr;
__u32 pad2;
__u64 control;
__u64 evt_page;
__u64 msg_page;
} synic;
struct {
__u64 input;
__u64 result;
__u64 params[2];
} hcall;
struct {
__u32 msr;
__u32 pad2;
__u64 control;
__u64 status;
__u64 send_page;
__u64 recv_page;
__u64 pending_page;
} syndbg;
} u;
};
struct kvm_debug_exit_arch {
__u32 exception;
__u32 pad;
__u64 pc;
__u64 dr6;
__u64 dr7;
};
/* for KVM_SET_USER_MEMORY_REGION */
struct kvm_userspace_memory_region {
__u32 slot;
__u32 flags;
__u64 guest_phys_addr;
__u64 memory_size; /* bytes */
__u64 userspace_addr; /* start of the userspace allocated memory */
};
struct kvm_xen_exit {
#define KVM_EXIT_XEN_HCALL 1
__u32 type;
union {
struct {
__u32 longmode;
__u32 cpl;
__u64 input;
__u64 result;
__u64 params[6];
} hcall;
} u;
};
/* for KVM_GET_REGS and KVM_SET_REGS */
struct kvm_regs {
/* out (KVM_GET_REGS) / in (KVM_SET_REGS) */
uint64_t rax, rbx, rcx, rdx;
uint64_t rsi, rdi, rsp, rbp;
uint64_t r8, r9, r10, r11;
uint64_t r12, r13, r14, r15;
uint64_t rip, rflags;
__u64 rax, rbx, rcx, rdx;
__u64 rsi, rdi, rsp, rbp;
__u64 r8, r9, r10, r11;
__u64 r12, r13, r14, r15;
__u64 rip, rflags;
};
struct my_kvm_segment {
__u64 base;
__u32 limit;
__u16 selector;
__u8 type;
__u8 present, dpl, db, s, l, g, avl;
__u8 unusable;
__u8 padding;
};
struct kvm_dtable {
__u64 base;
__u16 limit;
__u16 padding[3];
};
/* for KVM_GET_SREGS and KVM_SET_SREGS */
struct kvm_sregs {
/* out (KVM_GET_SREGS) / in (KVM_SET_SREGS) */
struct my_kvm_segment cs, ds, es, fs, gs, ss;
struct my_kvm_segment tr, ldt;
struct kvm_dtable gdt, idt;
__u64 cr0, cr2, cr3, cr4, cr8;
__u64 efer;
__u64 apic_base;
__u64 interrupt_bitmap[(KVM_NR_INTERRUPTS + 63) / 64];
};
int guest_code(){
while (1)
{
// printf("guest code\n");
__asm__ __volatile__ (
"mov %rax, 0\n\t"
"mov %rcx, 0\n\t"
"cpuid\n\t"
);
}
/* for KVM_GET/SET_VCPU_EVENTS */
struct kvm_vcpu_events {
struct {
__u8 injected;
__u8 nr;
__u8 has_error_code;
__u8 pending;
__u32 error_code;
} exception;
struct {
__u8 injected;
__u8 nr;
__u8 soft;
__u8 shadow;
} interrupt;
struct {
__u8 injected;
__u8 pending;
__u8 masked;
__u8 pad;
} nmi;
__u32 sipi_vector;
__u32 flags;
struct {
__u8 smm;
__u8 pending;
__u8 smm_inside_nmi;
__u8 latched_init;
} smi;
__u8 reserved[27];
__u8 exception_has_payload;
__u64 exception_payload;
};
/* kvm_sync_regs struct included by kvm_run struct */
struct kvm_sync_regs {
/* Members of this structure are potentially malicious.
* Care must be taken by code reading, esp. interpreting,
* data fields from them inside KVM to prevent TOCTOU and
* double-fetch types of vulnerabilities.
*/
struct kvm_regs regs;
struct kvm_sregs sregs;
struct kvm_vcpu_events events;
};
/* for KVM_RUN, returned by mmap(vcpu_fd, offset=0) */
struct kvm_run {
/* in */
__u8 request_interrupt_window;
__u8 immediate_exit;
__u8 padding1[6];
/* out */
__u32 exit_reason;
__u8 ready_for_interrupt_injection;
__u8 if_flag;
__u16 flags;
/* in (pre_kvm_run), out (post_kvm_run) */
__u64 cr8;
__u64 apic_base;
#ifdef __KVM_S390
/* the processor status word for s390 */
__u64 psw_mask; /* psw upper half */
__u64 psw_addr; /* psw lower half */
#endif
union {
/* KVM_EXIT_UNKNOWN */
struct {
__u64 hardware_exit_reason;
} hw;
/* KVM_EXIT_FAIL_ENTRY */
struct {
__u64 hardware_entry_failure_reason;
__u32 cpu;
} fail_entry;
/* KVM_EXIT_EXCEPTION */
struct {
__u32 exception;
__u32 error_code;
} ex;
/* KVM_EXIT_IO */
struct {
#define KVM_EXIT_IO_IN 0
#define KVM_EXIT_IO_OUT 1
__u8 direction;
__u8 size; /* bytes */
__u16 port;
__u32 count;
__u64 data_offset; /* relative to kvm_run start */
} io;
/* KVM_EXIT_DEBUG */
struct {
struct kvm_debug_exit_arch arch;
} debug;
/* KVM_EXIT_MMIO */
struct {
__u64 phys_addr;
__u8 data[8];
__u32 len;
__u8 is_write;
} mmio;
/* KVM_EXIT_HYPERCALL */
struct {
__u64 nr;
__u64 args[6];
__u64 ret;
__u32 longmode;
__u32 pad;
} hypercall;
/* KVM_EXIT_TPR_ACCESS */
struct {
__u64 rip;
__u32 is_write;
__u32 pad;
} tpr_access;
/* KVM_EXIT_S390_SIEIC */
struct {
__u8 icptcode;
__u16 ipa;
__u32 ipb;
} s390_sieic;
/* KVM_EXIT_S390_RESET */
#define KVM_S390_RESET_POR 1
#define KVM_S390_RESET_CLEAR 2
#define KVM_S390_RESET_SUBSYSTEM 4
#define KVM_S390_RESET_CPU_INIT 8
#define KVM_S390_RESET_IPL 16
__u64 s390_reset_flags;
/* KVM_EXIT_S390_UCONTROL */
struct {
__u64 trans_exc_code;
__u32 pgm_code;
} s390_ucontrol;
/* KVM_EXIT_DCR (deprecated) */
struct {
__u32 dcrn;
__u32 data;
__u8 is_write;
} dcr;
/* KVM_EXIT_INTERNAL_ERROR */
struct {
__u32 suberror;
/* Available with KVM_CAP_INTERNAL_ERROR_DATA: */
__u32 ndata;
__u64 data[16];
} internal;
/*
* KVM_INTERNAL_ERROR_EMULATION
*
* "struct emulation_failure" is an overlay of "struct internal"
* that is used for the KVM_INTERNAL_ERROR_EMULATION sub-type of
* KVM_EXIT_INTERNAL_ERROR. Note, unlike other internal error
* sub-types, this struct is ABI! It also needs to be backwards
* compatible with "struct internal". Take special care that
* "ndata" is correct, that new fields are enumerated in "flags",
* and that each flag enumerates fields that are 64-bit aligned
* and sized (so that ndata+internal.data[] is valid/accurate).
*/
struct {
__u32 suberror;
__u32 ndata;
__u64 flags;
__u8 insn_size;
__u8 insn_bytes[15];
} emulation_failure;
/* KVM_EXIT_OSI */
struct {
__u64 gprs[32];
} osi;
/* KVM_EXIT_PAPR_HCALL */
struct {
__u64 nr;
__u64 ret;
__u64 args[9];
} papr_hcall;
/* KVM_EXIT_S390_TSCH */
struct {
__u16 subchannel_id;
__u16 subchannel_nr;
__u32 io_int_parm;
__u32 io_int_word;
__u32 ipb;
__u8 dequeued;
} s390_tsch;
/* KVM_EXIT_EPR */
struct {
__u32 epr;
} epr;
/* KVM_EXIT_SYSTEM_EVENT */
struct {
#define KVM_SYSTEM_EVENT_SHUTDOWN 1
#define KVM_SYSTEM_EVENT_RESET 2
#define KVM_SYSTEM_EVENT_CRASH 3
__u32 type;
__u64 flags;
} system_event;
/* KVM_EXIT_S390_STSI */
struct {
__u64 addr;
__u8 ar;
__u8 reserved;
__u8 fc;
__u8 sel1;
__u16 sel2;
} s390_stsi;
/* KVM_EXIT_IOAPIC_EOI */
struct {
__u8 vector;
} eoi;
/* KVM_EXIT_HYPERV */
struct kvm_hyperv_exit hyperv;
/* KVM_EXIT_ARM_NISV */
struct {
__u64 esr_iss;
__u64 fault_ipa;
} arm_nisv;
/* KVM_EXIT_X86_RDMSR / KVM_EXIT_X86_WRMSR */
struct {
__u8 error; /* user -> kernel */
__u8 pad[7];
#define KVM_MSR_EXIT_REASON_INVAL (1 << 0)
#define KVM_MSR_EXIT_REASON_UNKNOWN (1 << 1)
#define KVM_MSR_EXIT_REASON_FILTER (1 << 2)
__u32 reason; /* kernel -> user */
__u32 index; /* kernel -> user */
__u64 data; /* kernel <-> user */
} msr;
/* KVM_EXIT_XEN */
struct kvm_xen_exit xen;
/* Fix the size of the union. */
char padding[256];
};
/* 2048 is the size of the char array used to bound/pad the size
* of the union that holds sync regs.
*/
#define SYNC_REGS_SIZE_BYTES 2048
/*
* shared registers between kvm and userspace.
* kvm_valid_regs specifies the register classes set by the host
* kvm_dirty_regs specified the register classes dirtied by userspace
* struct kvm_sync_regs is architecture specific, as well as the
* bits for kvm_valid_regs and kvm_dirty_regs
*/
__u64 kvm_valid_regs;
__u64 kvm_dirty_regs;
union {
struct kvm_sync_regs regs;
char padding[SYNC_REGS_SIZE_BYTES];
} s;
};
int kvm(uint8_t code[], size_t code_len)
{
// step 1, open /dev/kvm
int kvmfd = open("/dev/kvm", O_RDWR | O_CLOEXEC);
if (kvmfd == -1)
{
printf("failed to open /dev/kvm\n");
return 0;
}
// step 2, create VM
int vmfd = ioctl(kvmfd, KVM_CREATE_VM, 0);
printf("vmfd %d\n", vmfd);
// step 3, set up user memory region
size_t mem_size = 0x100000; // size of user memory you want to assign
void *mem = mmap(0, mem_size, PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_ANONYMOUS, -1, 0);
printf("map mem %p\n", mem);
int user_entry = 0x0;
memcpy((void *)((size_t)mem + user_entry), code, code_len);
struct kvm_userspace_memory_region region = {
.slot = 0,
.flags = 0,
.guest_phys_addr = 0,
.memory_size = mem_size,
.userspace_addr = (size_t)mem};
ioctl(vmfd, KVM_SET_USER_MEMORY_REGION, &region);
/* end of step 3 */
// step 4, create vCPU
int vcpufd = ioctl(vmfd, KVM_CREATE_VCPU, 0);
printf("create vcpu,fd: %p\n", vcpufd);
// step 5, set up memory for vCPU
size_t vcpu_mmap_size = ioctl(kvmfd, KVM_GET_VCPU_MMAP_SIZE, NULL);
struct kvm_run *run = (struct kvm_run *)mmap(0, vcpu_mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED, vcpufd, 0);
// step 6, set up vCPU's registers
/* standard registers include general-purpose registers and flags */
struct kvm_regs regs;
ioctl(vcpufd, KVM_GET_REGS, &regs);
regs.rip = user_entry;
regs.rsp = 0x200000; // stack address
regs.rflags = 0x2; // in x86 the 0x2 bit should always be set
ioctl(vcpufd, KVM_SET_REGS, &regs); // set registers
/* special registers include segment registers */
struct kvm_sregs sregs;
ioctl(vcpufd, KVM_GET_SREGS, &sregs);
sregs.cs.base = sregs.cs.selector = 0; // let base of code segment equal to zero
ioctl(vcpufd, KVM_SET_SREGS, &sregs);
ioctl(vcpufd, KVM_GET_SREGS, &sregs);
// step 7, execute vm and handle exit reason
#define KVM_EXIT_UNKNOWN 0
#define KVM_EXIT_EXCEPTION 1
#define KVM_EXIT_IO 2
#define KVM_EXIT_HYPERCALL 3
#define KVM_EXIT_DEBUG 4
#define KVM_EXIT_HLT 5
#define KVM_EXIT_MMIO 6
#define KVM_EXIT_IRQ_WINDOW_OPEN 7
#define KVM_EXIT_SHUTDOWN 8
#define KVM_EXIT_FAIL_ENTRY 9
#define KVM_EXIT_INTR 10
#define KVM_EXIT_SET_TPR 11
#define KVM_EXIT_TPR_ACCESS 12
#define KVM_EXIT_S390_SIEIC 13
#define KVM_EXIT_S390_RESET 14
#define KVM_EXIT_DCR 15 /* deprecated */
#define KVM_EXIT_NMI 16
#define KVM_EXIT_INTERNAL_ERROR 17
#define KVM_EXIT_OSI 18
#define KVM_EXIT_PAPR_HCALL 19
#define KVM_EXIT_S390_UCONTROL 20
#define KVM_EXIT_WATCHDOG 21
#define KVM_EXIT_S390_TSCH 22
#define KVM_EXIT_EPR 23
#define KVM_EXIT_SYSTEM_EVENT 24
#define KVM_EXIT_S390_STSI 25
#define KVM_EXIT_IOAPIC_EOI 26
#define KVM_EXIT_HYPERV 27
#define KVM_EXIT_ARM_NISV 28
#define KVM_EXIT_X86_RDMSR 29
#define KVM_EXIT_X86_WRMSR 30
#define KVM_EXIT_DIRTY_RING_FULL 31
#define KVM_EXIT_AP_RESET_HOLD 32
#define KVM_EXIT_X86_BUS_LOCK 33
#define KVM_EXIT_XEN 34
while (1)
{
ioctl(vcpufd, KVM_RUN, NULL);
ioctl(vcpufd, KVM_GET_SREGS, &sregs);
printf("Guest CR3: 0x%llx\n", sregs.cr3);
switch (run->exit_reason)
{
case KVM_EXIT_HLT:
fputs("KVM_EXIT_HLT \n", stderr);
return 0;
case KVM_EXIT_IO:
/* TODO: check port and direction here */
putchar(*(((char *)run) + run->io.data_offset));
printf("KVM_EXIT_IO: run->io.port = %lx \n",
run->io.port);
break;
case KVM_EXIT_FAIL_ENTRY:
printf("KVM_EXIT_FAIL_ENTRY: hardware_entry_failure_reason = 0x%lx",
run->fail_entry.hardware_entry_failure_reason);
return 0;
case KVM_EXIT_INTERNAL_ERROR:
printf("KVM_EXIT_INTERNAL_ERROR: suberror = 0x%x",
run->internal.suberror);
return 0;
case KVM_EXIT_SHUTDOWN:
printf("KVM_EXIT_SHUTDOWN");
return 0;
default:
printf("Unhandled reason: %d", run->exit_reason);
return 0;
}
}
}
/*汇编指令解释
0xB0 0x61 (mov al, 0x61)
解释:将立即数 0x61ASCII 字符 'a')加载到 AL 寄存器中。
0xBA 0x17 0x02 (mov dx, 0x0217)
Linux: ilen = 3 外中断和EPT_VIOLATION
解释:将立即数 0x0217 加载到 DX 寄存器中。
0xEE (out dx, al)
解释:将 AL 寄存器的值输出到 DX 寄存器指定的端口。
0xB0 0x0A (mov al, 0x0A)
解释:将立即数 0x0A换行符加载到 AL 寄存器中。
0xEE (out dx, al)
解释:将 AL 寄存器的值输出到 DX 寄存器指定的端口。
0xF4 (hlt)
解释:执行 hlt 指令,使处理器进入休眠状态,直到下一个外部中断到来。*/
int main()
{
printf("Test kvm running...\n");
printf("Open /dev/kvm\n");
int kvm_fd = open("/dev/kvm", O_RDWR|O_CLOEXEC);
int vmfd = ioctl(kvm_fd, 0x01, 0);
printf("vmfd=%d\n", vmfd);
/*
__asm__ __volatile__ (
"mov %rax, 0\n\t"
"mov %rcx, 0\n\t"
"cpuid\n\t"
);
*/
const uint8_t code[] = {
0xba, 0xf8, 0x03, /* mov $0x3f8, %dx */
0x00, 0xd8, /* add %bl, %al */
0x04, '0', /* add $'0', %al */
0xee, /* out %al, (%dx) */
0xb0, '\n', /* mov $'\n', %al */
0xee, /* out %al, (%dx) */
0xf4, /* hlt */
};
size_t mem_size = 0x4000; // size of user memory you want to assign
printf("code=%p\n", code);
// void *mem = mmap(0, mem_size, 0x7, -1, 0);
// memcpy(mem, code, sizeof(code));
struct kvm_userspace_memory_region region = {
.slot = 0,
.flags = 0,
.guest_phys_addr = 0,
.memory_size = mem_size,
.userspace_addr = (size_t)code
};
ioctl(vmfd, KVM_SET_USER_MEMORY_REGION, &region);
int vcpufd = ioctl(vmfd, KVM_CREATE_VCPU, 0);
printf("vcpufd=%d\n", vcpufd);
int user_entry = 0x0;
struct kvm_regs regs = {0};
regs.rip = user_entry;
regs.rsp = 0x3000; // stack address
regs.rflags = 0x2; // in x86 the 0x2 bit should always be set
ioctl(vcpufd, KVM_SET_REGS, &regs); // set registers
ioctl(vcpufd, KVM_RUN, 0);
return 0;
//uint8_t code[] = "\xB0\x61\xBA\x17\x02\xEE\xB0\n\xEE\xF4";
//uint8_t code[] = "\xB0\x61\xBA\x17\x02\xEE\xF4";
uint8_t code[] = "\xB0\x61\xF4";
kvm(code, sizeof(code));
return 0;
}

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表示测试失败
}

View File

@ -24,7 +24,7 @@ source = "git"
source-path = "https://git.mirrors.dragonos.org.cn/DragonOS-Community/NovaShell.git"
# git标签或分支
# 注意: branch和revision只能二选一且source要设置为"git"
revision = "feaebefaef"
revision = "d7d2136c5a"
# 构建相关信息
[build]
# (可选)构建命令

View File

@ -0,0 +1,46 @@
# 用户程序名称
name = "test_poll"
# 版本号
version = "0.1.0"
# 用户程序描述信息
description = "test_poll"
# (可选)默认: 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_poll"
# 构建相关信息
[build]
# (可选)构建命令
build-command = "make install"
# 安装相关信息
[install]
# 可选安装到DragonOS的路径
in-dragonos-path = "/bin"
# 清除相关信息
[clean]
# (可选)清除命令
clean-command = "make clean"
# (可选)依赖项
# 注意:如果没有依赖项,忽略此项,不允许只留一个[[depends]]
# [[depends]]
# name = "depend1"
# version = "0.1.1"
# (可选)环境变量
# 注意:如果没有环境变量,忽略此项,不允许只留一个[[envs]]
# [[envs]]
# key = "PATH"
# value = "/usr/bin"