#include #include #include #include #include #include #include #include #define BUFFER_SIZE 1024 void print_usage(const char *prog_name) { fprintf(stderr, "使用方法: %s \n", prog_name); fprintf(stderr, " mode: 1 - 阻塞方式读取\n"); fprintf(stderr, " 0 - 非阻塞方式读取\n"); fprintf(stderr, " device: 字符设备路径 (例如: /dev/cash0)\n"); fprintf(stderr, "示例:\n"); fprintf(stderr, " %s 1 /dev/cash0 # 阻塞方式读取\n", prog_name); fprintf(stderr, " %s 0 /dev/cash0 # 非阻塞方式读取\n", prog_name); } int set_nonblocking(int fd) { int flags = fcntl(fd, F_GETFL, 0); if (flags == -1) { perror("fcntl F_GETFL"); return -1; } if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) { perror("fcntl F_SETFL"); return -1; } return 0; } int set_blocking(int fd) { int flags = fcntl(fd, F_GETFL, 0); if (flags == -1) { perror("fcntl F_GETFL"); return -1; } if (fcntl(fd, F_SETFL, flags & ~O_NONBLOCK) == -1) { perror("fcntl F_SETFL"); return -1; } return 0; } // 使用 select 实现阻塞读取 void blocking_read(int fd) { fd_set read_fds; char buffer[BUFFER_SIZE]; int ret; printf("阻塞模式:等待数据...\n"); while (1) { FD_ZERO(&read_fds); FD_SET(fd, &read_fds); // NULL 表示无限等待,直到有数据可读 ret = select(fd + 1, &read_fds, NULL, NULL, NULL); if (ret == -1) { if (errno == EINTR) { continue; // 被信号中断,继续等待 } perror("select error"); break; } if (FD_ISSET(fd, &read_fds)) { memset(buffer, 0, sizeof(buffer)); ssize_t n = read(fd, buffer, sizeof(buffer) - 1); if (n > 0) { buffer[n] = '\0'; printf("读到 %zd 字节: %s\n", n, buffer); // 如果读到 "quit" 或 "exit" 则退出 if (strncmp(buffer, "quit", 4) == 0 || strncmp(buffer, "exit", 4) == 0) { printf("收到退出命令\n"); break; } } else if (n == 0) { printf("设备已关闭 (EOF)\n"); break; } else { if (errno != EAGAIN && errno != EWOULDBLOCK) { perror("read error"); break; } } } } } // 使用 select 实现非阻塞读取(带超时检测) void nonblocking_read(int fd) { fd_set read_fds; struct timeval timeout; char buffer[BUFFER_SIZE]; int ret; printf("非阻塞模式:\n"); while (1) { memset(buffer, 0, sizeof(buffer)); ssize_t n = read(fd, buffer, sizeof(buffer) - 1); if (n > 0) { printf("读到 %zd 字节: %s\n", n, buffer); } sleep(1); } } int main(int argc, char *argv[]) { int mode; const char *device_path; int fd; int open_flags; // 检查参数 if (argc != 3) { print_usage(argv[0]); return 1; } // 解析模式 mode = atoi(argv[1]); if (mode != 0 && mode != 1) { fprintf(stderr, "错误: mode 必须是 0 或 1\n"); print_usage(argv[0]); return 1; } device_path = argv[2]; // 打开设备 // 注意:select 对常规文件总是返回可读,但对字符设备通常能正确工作 // 为了更好的演示,我们以读写方式打开 open_flags = O_RDWR; fd = open(device_path, open_flags); if (fd < 0) { perror("打开设备失败"); fprintf(stderr, "设备: %s\n", device_path); return 1; } printf("成功打开设备: %s\n", device_path); // 根据模式设置阻塞/非阻塞 if (mode == 0) { // 非阻塞模式:设置 O_NONBLOCK 标志 if (set_nonblocking(fd) < 0) { close(fd); return 1; } printf("设置为非阻塞模式\n"); nonblocking_read(fd); } else { // 阻塞模式:确保清除 O_NONBLOCK 标志 if (set_blocking(fd) < 0) { close(fd); return 1; } printf("设置为阻塞模式\n"); blocking_read(fd); } close(fd); printf("程序退出\n"); return 0; }