#define _GNU_SOURCE #include #include #include #include #include #include #include static double time_diff_sec(struct timespec a, struct timespec b) { return (b.tv_sec - a.tv_sec) + (b.tv_nsec - a.tv_nsec) / 1000000000.0; } static int test_single_file_perf(const char *path) { // size_t io_size = 128 * 1024; size_t io_size = 4096; size_t max_size = 2ULL * 1024 * 1024 * 1024; /* 最大 2GB,循环覆写 */ size_t max_count = max_size / io_size; int test_sec = 10; int direct = 0; printf("\n=== test_single_file_perf ===\n"); printf("Path : %s\n", path); printf("IO size : %zu KB\n", io_size / 1024); printf("Max file: %zu MB\n", max_size / 1024 / 1024); printf("Duration: %d sec\n", test_sec); unlink(path); char *buf = aligned_alloc(4096, io_size); if (!buf) { perror("aligned_alloc"); return 1; } memset(buf, 'A', io_size); struct timespec t1, t2, now; /* ================= WRITE ================= */ int fd = open(path, O_CREAT | O_RDWR | direct, 0644); if (fd < 0) { perror("open write"); free(buf); return 1; } clock_gettime(CLOCK_MONOTONIC, &t1); size_t wcount = 0; size_t wpos = 0; /* 当前写位置(以块为单位) */ do { /* 超过最大文件大小,seek 回头循环覆写 */ if (wpos >= max_count) { lseek(fd, 0, SEEK_SET); wpos = 0; } if (write(fd, buf, io_size) != (ssize_t)io_size) { perror("write"); close(fd); free(buf); return 2; } wcount++; wpos++; clock_gettime(CLOCK_MONOTONIC, &now); } while (time_diff_sec(t1, now) < test_sec); clock_gettime(CLOCK_MONOTONIC, &t2); close(fd); double wsec = time_diff_sec(t1, t2); double wmb = (double)(wcount * io_size) / (1024.0 * 1024.0); printf("\nWRITE:\n"); printf(" total : %.1f MB\n", wmb); printf(" time : %.3f sec\n", wsec); printf(" IOPS : %.0f ops/sec\n", wcount / wsec); printf(" BW : %.2f MB/s\n", wmb / wsec); /* ================= READ ================= */ fd = open(path, O_RDONLY | direct); if (fd < 0) { perror("open read"); free(buf); return 3; } clock_gettime(CLOCK_MONOTONIC, &t1); size_t rcount = 0; do { ssize_t r = read(fd, buf, io_size); if (r <= 0) { lseek(fd, 0, SEEK_SET); continue; } rcount++; clock_gettime(CLOCK_MONOTONIC, &now); } while (time_diff_sec(t1, now) < test_sec); clock_gettime(CLOCK_MONOTONIC, &t2); close(fd); double rsec = time_diff_sec(t1, t2); double rmb = (double)(rcount * io_size) / (1024.0 * 1024.0); printf("\nREAD:\n"); printf(" total : %.1f MB\n", rmb); printf(" time : %.3f sec\n", rsec); printf(" IOPS : %.0f ops/sec\n", rcount / rsec); printf(" BW : %.2f MB/s\n", rmb / rsec); unlink(path); free(buf); return 0; } /* ------------------------------------------------------------------ */ /* Test 1: 原有基础测试 */ /* ------------------------------------------------------------------ */ static int test_basic(const char *path) { printf("\n=== test_basic ===\n"); printf("open: %s\n", path); int fd = open(path, O_CREAT | O_RDWR | O_TRUNC, 0644); if (fd < 0) { perror("open"); return 1; } const char *msg = "ABCDEFGHIJKL"; ssize_t w = write(fd, msg, strlen(msg)); if (w < 0) { perror("write"); return 2; } printf("write: %zd\n", w); const char *msg2 = "MNOPQRSTUVWXYZ"; ssize_t w2 = write(fd, msg2, strlen(msg2)); if (w2 < 0) { perror("write"); return 2; } printf("write: %zd\n", w2); close(fd); fd = open(path, O_RDONLY); if (fd < 0) { perror("open R"); return 3; } char buf[10]; memset(buf, 0, sizeof(buf)); ssize_t r = read(fd, buf, sizeof(buf)); if (r < 0) { perror("read"); return 4; } printf("read: %zd bytes: %.*s\n", r, (int)r, buf); char buf2[512]; memset(buf2, 0, sizeof(buf2)); ssize_t r2 = read(fd, buf2, sizeof(buf2)); if (r2 < 0) { perror("read"); return 4; } printf("read: %zd bytes: %.*s\n", r2, (int)r2, buf2); close(fd); if (unlink(path) != 0) { perror("unlink"); return 5; } printf("unlink: ok\n"); return 0; } /* ------------------------------------------------------------------ */ /* Test 2: lseek */ /* ------------------------------------------------------------------ */ static int test_lseek(const char *path) { printf("\n=== test_lseek ===\n"); int fd = open(path, O_CREAT | O_RDWR | O_TRUNC, 0644); if (fd < 0) { perror("open"); return 1; } /* 写入 26 个字母 */ const char *alpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; if (write(fd, alpha, 26) != 26) { perror("write"); return 2; } printf("write 26 bytes: %s\n", alpha); /* SEEK_SET: 跳到第 0 字节,读 5 个 */ off_t pos = lseek(fd, 0, SEEK_SET); printf("lseek SEEK_SET 0 -> %ld\n", (long)pos); char buf[32] = {0}; ssize_t r = read(fd, buf, 5); printf("read 5 bytes: %.*s (expect: ABCDE)\n", (int)r, buf); /* SEEK_CUR: 从当前位置(5)再向前跳 3 个,应读到第 8 字节 'I' */ pos = lseek(fd, 3, SEEK_CUR); printf("lseek SEEK_CUR +3 -> %ld\n", (long)pos); memset(buf, 0, sizeof(buf)); r = read(fd, buf, 5); printf("read 5 bytes: %.*s (expect: IJKLM)\n", (int)r, buf); /* SEEK_END: 从文件尾往回 5 字节,读到最后 5 个字母 */ pos = lseek(fd, -5, SEEK_END); printf("lseek SEEK_END -5 -> %ld\n", (long)pos); memset(buf, 0, sizeof(buf)); r = read(fd, buf, 10); /* 最多只剩 5 字节 */ printf("read %zd bytes: %.*s (expect: VWXYZ)\n", r, (int)r, buf); /* SEEK_SET 超过文件末尾:写一个字节,制造"空洞" */ pos = lseek(fd, 30, SEEK_SET); printf("lseek SEEK_SET 30 -> %ld\n", (long)pos); if (write(fd, "!", 1) != 1) { perror("write hole"); return 3; } /* 重新读回空洞区域,[26,29] 应为 0x00 */ lseek(fd, 26, SEEK_SET); memset(buf, 0xAA, sizeof(buf)); r = read(fd, buf, 5); printf("read hole+1: %zd bytes, hole[0]=%02X hole[1]=%02X hole[2]=%02X " "hole[3]=%02X last='%c' (expect: 00 00 00 00 '!')\n", r, (unsigned char)buf[0], (unsigned char)buf[1], (unsigned char)buf[2], (unsigned char)buf[3], buf[4]); close(fd); unlink(path); return 0; } /* ------------------------------------------------------------------ */ /* Test 3: 同一文件被两个 fd 同时打开(一读一写) */ /* ------------------------------------------------------------------ */ static int test_dual_open_same_file(const char *path) { printf("\n=== test_dual_open_same_file ===\n"); /* 先创建并写入初始内容 */ int fd_init = open(path, O_CREAT | O_RDWR | O_TRUNC, 0644); if (fd_init < 0) { perror("open init"); return 1; } const char *init = "0123456789"; if (write(fd_init, init, 10) != 10) { perror("write init"); return 2; } close(fd_init); /* 用写句柄和读句柄分别打开同一文件 */ int fd_w = open(path, O_WRONLY); if (fd_w < 0) { perror("open W"); return 3; } int fd_r = open(path, O_RDONLY); if (fd_r < 0) { perror("open R"); return 4; } printf("fd_w=%d fd_r=%d\n", fd_w, fd_r); /* 通过写句柄覆写前 5 字节 */ if (write(fd_w, "HELLO", 5) != 5) { perror("write"); return 5; } printf("write via fd_w: HELLO (overwrite first 5 bytes)\n"); /* 通过读句柄从头读回全部内容 */ char buf[32] = {0}; lseek(fd_r, 0, SEEK_SET); ssize_t r = read(fd_r, buf, sizeof(buf)); printf("read via fd_r: %zd bytes: %.*s (expect: HELLO56789)\n", r, (int)r, buf); /* 继续用写句柄追加 */ lseek(fd_w, 0, SEEK_END); if (write(fd_w, "!!!", 3) != 3) { perror("write append"); return 6; } printf("write append via fd_w: !!!\n"); /* 读句柄读追加部分 */ lseek(fd_r, 10, SEEK_SET); memset(buf, 0, sizeof(buf)); r = read(fd_r, buf, sizeof(buf)); printf("read appended via fd_r: %zd bytes: %.*s (expect: !!!)\n", r, (int)r, buf); close(fd_w); close(fd_r); unlink(path); return 0; } /* ------------------------------------------------------------------ */ /* Test 4: 两个不同文件同时打开,分别读写 */ /* ------------------------------------------------------------------ */ static int test_two_files(const char *path_a, const char *path_b) { printf("\n=== test_two_files ===\n"); int fd_a = open(path_a, O_CREAT | O_RDWR | O_TRUNC, 0644); if (fd_a < 0) { perror("open A"); return 1; } int fd_b = open(path_b, O_CREAT | O_RDWR | O_TRUNC, 0644); if (fd_b < 0) { perror("open B"); return 2; } printf("fd_a=%d fd_b=%d\n", fd_a, fd_b); /* 分别写入不同内容 */ const char *data_a = "File-A: Hello World!"; const char *data_b = "File-B: Goodbye World!"; if (write(fd_a, data_a, strlen(data_a)) < 0) { perror("write A"); return 3; } if (write(fd_b, data_b, strlen(data_b)) < 0) { perror("write B"); return 4; } printf("write A: %s\n", data_a); printf("write B: %s\n", data_b); /* 各自 seek 回头读取,验证内容互不干扰 */ lseek(fd_a, 0, SEEK_SET); lseek(fd_b, 0, SEEK_SET); char buf_a[64] = {0}; char buf_b[64] = {0}; ssize_t r_a = read(fd_a, buf_a, sizeof(buf_a)); ssize_t r_b = read(fd_b, buf_b, sizeof(buf_b)); printf("read A: %zd bytes: %.*s\n", r_a, (int)r_a, buf_a); printf("read B: %zd bytes: %.*s\n", r_b, (int)r_b, buf_b); int ok = 1; if (strncmp(buf_a, data_a, strlen(data_a)) != 0) { printf("FAIL: A content mismatch!\n"); ok = 0; } if (strncmp(buf_b, data_b, strlen(data_b)) != 0) { printf("FAIL: B content mismatch!\n"); ok = 0; } if (ok) printf("PASS: both files read back correctly\n"); /* 交叉写:向 A 追加,向 B 中段覆写,再各自读回验证 */ lseek(fd_a, 0, SEEK_END); write(fd_a, "[A-TAIL]", 8); lseek(fd_b, 8, SEEK_SET); /* "File-B: " 之后 */ write(fd_b, "Hi! ", 7); /* 覆写 "Goodbye" */ lseek(fd_a, 0, SEEK_SET); lseek(fd_b, 0, SEEK_SET); memset(buf_a, 0, sizeof(buf_a)); memset(buf_b, 0, sizeof(buf_b)); r_a = read(fd_a, buf_a, sizeof(buf_a)); r_b = read(fd_b, buf_b, sizeof(buf_b)); printf("after cross-write:\n"); printf(" A: %.*s\n", (int)r_a, buf_a); printf(" B: %.*s\n", (int)r_b, buf_b); close(fd_a); close(fd_b); unlink(path_a); unlink(path_b); return 0; } static int test_write_file(const char *path) { printf("\n=== test_write_file ===\n"); int fd = open(path, O_CREAT | O_RDWR, 0644); if (fd < 0) { perror("open"); return 1; } printf("open: %s fd=%d\n", path, fd); const char *msg = "Hello, zvfs!"; ssize_t w = write(fd, msg, strlen(msg)); if (w < 0) { perror("write"); close(fd); return 2; } printf("write: %zd bytes: %s\n", w, msg); close(fd); printf("close: ok\n"); return 0; } static int test_read_delete_file(const char *path) { printf("\n=== test_read_delete_file ===\n"); int fd = open(path, O_RDONLY); if (fd < 0) { perror("open"); return 1; } printf("open: %s fd=%d\n", path, fd); char buf[256] = {0}; ssize_t r = read(fd, buf, sizeof(buf)); if (r < 0) { perror("read"); close(fd); return 2; } printf("read: %zd bytes: %.*s\n", r, (int)r, buf); close(fd); printf("close: ok\n"); if (unlink(path) != 0) { perror("unlink"); return 3; } printf("unlink: ok\n"); return 0; } /* ------------------------------------------------------------------ */ /* main */ /* ------------------------------------------------------------------ */ int main(int argc, char **argv) { int rc = 0; char path[256]; char path_a[256]; char path_b[256]; if(argc >= 2){ sprintf(path, "%s/file.dat", argv[1]); sprintf(path_a, "%s/file_a.dat", argv[1]); sprintf(path_b, "%s/file_a.dat", argv[1]); }else { sprintf(path, "/tmp/test.dat"); } if(argc == 3){ int choose = atoi(argv[2]); if(choose == 0){ rc = test_write_file(path); }else if(choose == 1){ rc = test_read_delete_file(path); } return rc; } // printf("argv[0]: %s\n", argv[0]); // printf("argv[1]: %s\n", argv[1]); // printf("path_a: %s\n", path_a); // printf("path_b: %s\n", path_b); // rc |= test_basic(path); // rc |= test_lseek(path); // rc |= test_dual_open_same_file(path); // rc |= test_two_files(path_a, path_b); rc |= test_single_file_perf(path); printf("\n=== all tests %s ===\n", rc == 0 ? "PASSED" : "FAILED"); return rc; }