非阻塞 I/O(Input/Output)是一种在进行文件和套接字操作时不阻塞进程的机制。在 Linux 中,非阻塞 I/O 可以通过设置文件描述符(File Descriptor)为非阻塞模式来实现。
fcntl
函数:#include <fcntl.h>
int flags = fcntl(fd, F_GETFL, 0);
flags |= O_NONBLOCK;
fcntl(fd, F_SETFL, flags);
上述代码将文件描述符 fd
设置为非阻塞模式。
ioctl
函数:#include <sys/ioctl.h>
ioctl(fd, FIONBIO, &arg); // 其中 arg 为 0 表示阻塞,为 1 表示非阻塞
这将 fd
设置为非阻塞(arg
为 1)或阻塞(arg
为 0)。
ssize_t n = read(fd, buf, sizeof(buf));
if (n == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
// 没有数据可读,可以稍后再试或进行其他操作
} else {
// 读取成功,处理数据
}
ssize_t n = write(fd, buf, sizeof(buf));
if (n == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
// 写入缓冲区已满,可以稍后再试或进行其他操作
} else {
// 写入成功,继续写入或进行其他操作
}
select
和 poll
函数select
函数:#include <sys/select.h>
fd_set read_fds;
FD_ZERO(&read_fds);
FD_SET(fd, &read_fds);
struct timeval timeout;
timeout.tv_sec = 5; // 设置超时时间为 5 秒
timeout.tv_usec = 0;
int ready = select(fd + 1, &read_fds, NULL, NULL, &timeout);
if (ready == -1) {
// 处理错误
} else if (ready == 0) {
// 超时,没有数据可读
} else {
// 文件描述符 `fd` 上有数据可读
}
poll
函数:#include <poll.h>
struct pollfd fds[1];
fds[0].fd = fd;
fds[0].events = POLLIN;
int ready = poll(fds, 1, 5000); // 设置超时时间为 5000 毫秒
if (ready == -1) {
// 处理错误
} else if (ready == 0) {
// 超时,没有数据可读
} else {
// 文件描述符 `fd` 上有数据可读
}
这些函数和方法允许在进行 I/O 操作时检查文件描述符的状态,从而避免阻塞。非阻塞 I/O 对于需要实时响应的应用程序非常有用,例如网络通信、事件驱动的系统等。
非阻塞 I/O(Non-blocking I/O)是一种在进行文件和套接字操作时,应用程序可以在操作未完成的情况下继续执行其他任务的机制。以下是非阻塞 I/O 的优点和缺点:
非阻塞 I/O 可以通过设置文件描述符为非阻塞模式,以及使用适当的系统调用来实现并发读取。下面是一个简单的示例,演示如何使用非阻塞 I/O 实现并发读取:
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
int set_nonblocking(int fd) {
int flags = fcntl(fd, F_GETFL, 0);
if (flags == -1) {
perror("fcntl");
return -1;
}
flags |= O_NONBLOCK;
if (fcntl(fd, F_SETFL, flags) == -1) {
perror("fcntl");
return -1;
}
return 0;
}
int main() {
int fd = open("example.txt", O_RDONLY);
if (fd == -1) {
perror("open");
return 1;
}
// 设置文件描述符为非阻塞模式
if (set_nonblocking(fd) == -1) {
return 1;
}
char buffer[1024];
ssize_t n;
// 进行并发读取
while (1) {
n = read(fd, buffer, sizeof(buffer));
if (n == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
// 没有数据可读,可以执行其他任务或等待
printf("No data available for now.\n");
} else {
perror("read");
break;
}
} else if (n == 0) {
// 文件读取完成
break;
} else {
// 处理读取的数据
printf("Read %zd bytes: %.*s\n", n, (int)n, buffer);
}
}
close(fd);
return 0;
}
上述代码中的 set_nonblocking
函数用于将文件描述符设置为非阻塞模式。在主循环中,通过不断尝试非阻塞读取,可以实现并发读取的效果。如果当前没有数据可读,则 read
函数会返回 -1,并且 errno
会被设置为 EAGAIN
或 EWOULDBLOCK
,表示当前没有数据可用,可以执行其他任务或等待。这样,程序可以不阻塞地进行其他操作,实现并发性。
总体而言,非阻塞 I/O 适用于需要高并发、实时响应性的场景,如网络服务器、事件驱动应用等。在正确使用的情况下,非阻塞 I/O 能够提高系统的性能和吞吐量。
在 Linux 中,阻塞 I/O(Input/Output)是一种默认的 I/O 操作方式,即当应用程序发起 I/O 操作时,如果操作无法立即完成,应用程序将被阻塞(暂停执行)直到操作完成。
#include <unistd.h>
char buf[1024];
ssize_t n = read(fd, buf, sizeof(buf));
if (n == -1) {
// 处理读取错误
} else {
// 处理读取成功的数据
}
如果文件描述符 fd
上没有可用的数据,read
操作将阻塞等待,直到有数据可读或发生错误。
#include <unistd.h>
char buf[1024];
ssize_t n = write(fd, buf, sizeof(buf));
if (n == -1) {
// 处理写入错误
} else {
// 处理写入成功
}
如果文件描述符 fd
上的写入缓冲区已满,write
操作将阻塞等待,直到有空间可用或发生错误。
尽管阻塞 I/O 在某些场景下表现良好,但在需要处理大量并发请求、实时响应的场景中,非阻塞 I/O 或异步 I/O 更常被使用。非阻塞 I/O 允许应用程序在 I/O 操作完成之前继续执行其他任务,从而提高系统的响应性。
阻塞 I/O(Blocking I/O)是一种默认的 I/O 操作方式,其优点和缺点如下:
总体而言,阻塞 I/O 适用于一些简单的应用场景,但在面对高并发、实时性要求高的场景时,非阻塞 I/O 或者异步 I/O 更为常见。这些模型可以更好地利用系统资源,提高系统的并发性和响应性。