关键词:
一、概述
Linux(实际上是 Unix)的一个基本概念是 Unix/Linux 中的一切都是文件的规则。每个进程都有一个指向文件、套接字、设备和其他操作系统对象的文件描述符表。
与许多 IO 源一起工作的典型系统有一个初始化阶段,然后进入某种待机模式——等待任何客户端发送请求并响应它。
1、用户和内核模式
在任何现代操作系统中,CPU 实际上都在两种截然不同的模式下花费时间:
内核模式
在内核模式下,执行代码可以完全且不受限制地访问底层硬件。它可以执行任何 CPU 指令并引用任何内存地址。内核模式通常保留给操作系统的最低级别、最受信任的功能。内核模式下的崩溃是灾难性的;他们将停止整个 PC。
用户模式
在用户模式下,执行代码无法直接访问硬件或引用内存。在用户模式下运行的代码必须委托给系统 API 来访问硬件或内存。由于这种隔离提供的保护,用户模式下的崩溃总是可以恢复的。在您的计算机上运行的大部分代码都将在用户模式下执行。
2、进程切换
进程切换是操作系统调度程序从一个正在运行的程序更改为另一个。这需要保存当前执行程序的所有状态,包括它的寄存器状态、相关的内核状态以及它的所有虚拟内存配置。
一个进程切换将通过以下更改:
保存处理器上下文,包括程序计数器和其他寄存器
更新过程控制块 (PCB) 信息
将PCB的进程移动到相应的队列中,如就绪队列、事件块队列等队列
选择另一个进程执行并更新其 PCB
更新内存中的数据结构
恢复 PCB 上下文
3、阻塞进程
阻塞进程通常在等待一个事件,例如释放信号量或消息到达其消息队列。在多任务系统中,此类进程被期望通过系统调用通知调度程序它要等待,以便可以将它们从活动调度队列中删除,直到事件发生。在等待期间继续运行的进程(即,在紧密循环中不断轮询事件)被称为忙等待,这是不可取的,因为它浪费了可用于其他进程的时钟周期。当进程进入阻塞状态时,不会占用CPU资源。
4、缓冲 I/O
缓冲的输出流会将写入结果累积到一个中间缓冲区中,仅当累积了足够的数据(或请求了 flush())时才将其发送到 OS 文件系统。这减少了文件系统调用的数量。由于在大多数平台上文件系统调用可能很昂贵(与短 memcpy 相比),因此在执行大量小型写入时,缓冲输出是一种净赢。当您已经有大缓冲区要发送时,无缓冲输出通常会更好——复制到中间缓冲区不会进一步减少操作系统调用的数量,并且会引入额外的工作。数据在传输过程中需要进行数据复制操作,操作操作带来的CPU内存开销非常高。
5、文件描述符 (FD)
在 Unix 和相关计算机操作系统中,文件描述符(FD,不太常见的 fildes)是用于访问文件或其他输入/输出资源(例如管道或网络套接字)的抽象指示符(句柄)。文件描述符构成 POSIX 应用程序编程接口的一部分。它是一个非负的索引值,很多底层程序经常基于它。
当发生读取操作时,数据会经历两个阶段:
1、等待数据准备好
2、将数据从内核复制到进程
因为这两个阶段,Linux系统产生了以下5种网络模型:
阻塞 I/O
非阻塞 I/O
I/O 多路复用
信号驱动I/O
异步 I/O
其中I/O 多路复用貌似是最常用的方式。
二、使用 fork()
Fork()创建一个与其父进程同步运行的新子进程,Fork 比多线程效率低。在使用fork的服务器中,一个进程对应一个客户端,即使有性能空间,内存也会耗尽,响应性能明显下降。最终导致著名的 C10K 问题。
#include <arpa/inet.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
#define BACKLOG_SIZE 5
#define BUF_SIZE 1024
#define INF_TIME -1
#define DISABLE -1
int listen_fd;
void int_handle(int n)
close(listen_fd);
exit(EXIT_SUCCESS);
// wirte n byte
ssize_t write_n(int fd, char *ptr, size_t n)
ssize_t n_left = n, n_written;
while (n_left > 0)
if ((n_written = write(fd, ptr, n_left)) <= 0)
return n_written;
n_left -= n_written;
ptr += n_written;
return EXIT_SUCCESS;
int main(int argc, char **argv)
// Create listen socket
if ((listen_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
fprintf(stderr, "Error: socket\\n");
return EXIT_FAILURE;
// TCP port number
int port = 8080;
// Initialize server socket address
struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(port);
// Bind socket to an address
if (bind(listen_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) <
0)
fprintf(stderr, "Error: bind\\n");
return EXIT_FAILURE;
// Listen
if (listen(listen_fd, BACKLOG_SIZE) < 0)
fprintf(stderr, "Error: listen\\n");
return EXIT_FAILURE;
// Set INT signal handler
signal(SIGINT, int_handle);
fprintf(stderr, "listen on port %d\\n", port);
while (1)
// Check new connection
struct sockaddr_in client_addr;
socklen_t len_client = sizeof(client_addr);
int conn_fd;
if ((conn_fd = accept(listen_fd, (struct sockaddr *)&client_addr,
&len_client)) < 0)
fprintf(stderr, "Error: accept\\n");
return EXIT_FAILURE;
printf("Accept socket %d (%s : %hu)\\n", conn_fd,
inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
pid_t pid = fork();
if (pid < 0)
fprintf(stderr, "Error: fork\\n");
return EXIT_FAILURE;
if (pid == 0)
// child
char buf[BUF_SIZE];
close(listen_fd);
while (1)
ssize_t n = read(conn_fd, buf, BUF_SIZE);
if (n < 0)
fprintf(stderr, "Error: read from socket %d\\n", conn_fd);
close(conn_fd);
exit(-1);
else if (n == 0) // connection closed by client
printf("Close socket %d\\n", conn_fd);
close(conn_fd);
exit(0);
else
printf("Read %zu bytes from socket %d\\n", n, conn_fd);
write_n(conn_fd, buf, n);
else
// parent
close(conn_fd);
close(listen_fd);
return EXIT_SUCCESS;
三、I/O多路复用
1、select
计算复杂度为 O(n),因为必须线性搜索描述符(select函数仅仅知道有几个I/O事件发生了,但并不知道具体是哪几个socket连接有I/O事件,还需要轮询去查找,时间复杂度为O(n),处理的请求数越多,所消耗的时间越长。)。特点是可以管理的描述符数量有上限。
select() 只能监视小于 FD_SETSIZE (1024) 的文件描述符数量——对于许多现代应用程序来说,这是一个不合理的下限——并且这个限制不会改变。所有现代应用程序都应该使用 poll(2) 或 epoll(7 ),不受此限制。
参考代码
#include <arpa/inet.h>
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <unistd.h>
#define BACKLOG_SIZE 5
#define BUF_SIZE 1024
#define N_CLIENT 256
#define INF_TIME -1
#define DISABLE -1
int listen_fd;
void int_handle(int n)
close(listen_fd);
exit(EXIT_SUCCESS);
// wirte n byte
ssize_t write_n(int fd, char *ptr, size_t n)
ssize_t n_left = n, n_written;
while (n_left > 0)
if ((n_written = write(fd, ptr, n_left)) <= 0)
return n_written;
n_left -= n_written;
ptr += n_written;
return EXIT_SUCCESS;
int main(int argc, char **argv)
char buf[BUF_SIZE];
fd_set fds;
FD_ZERO(&fds);
int clients[N_CLIENT];
for (int i = 0; i < N_CLIENT; i++)
clients[i] = DISABLE;
memset(&fds, 0, sizeof(fds));
// Create listen socket
if ((listen_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
fprintf(stderr, "Error: socket\\n");
return EXIT_FAILURE;
// Set INT signal handler
signal(SIGINT, int_handle);
// TCP port number
int port = 8080;
// Initialize server socket address
struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(port);
// Bind socket to an address
if (bind(listen_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) <
0)
fprintf(stderr, "Error: bind\\n");
return EXIT_FAILURE;
// Listen
if (listen(listen_fd, BACKLOG_SIZE) < 0)
fprintf(stderr, "Error: listen\\n");
return EXIT_FAILURE;
fprintf(stderr, "listen on port %d\\n", port);
FD_SET(listen_fd, &fds);
int max_fd = listen_fd; // max fd
int max_i = 0; // max client into clients[] array
while (1)
FD_ZERO(&fds);
FD_SET(listen_fd, &fds);
for (int i = 0; i < N_CLIENT; i++)
if (clients[i] != DISABLE)
FD_SET(clients[i], &fds);
int res_select = select(max_fd + 1, &fds, NULL, NULL, NULL);
if (res_select < 0)
fprintf(stderr, "Error: select");
return EXIT_FAILURE;
// Check new connection
if (FD_ISSET(listen_fd, &fds))
struct sockaddr_in client_addr;
socklen_t len_client = sizeof(client_addr);
int connfd;
if ((connfd = accept(listen_fd, (struct sockaddr *)&client_addr,
&len_client)) < 0)
fprintf(stderr, "Error: accept\\n");
return EXIT_FAILURE;
printf("Accept socket %d (%s : %hu)\\n", connfd,
inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
// Save client socket into clients array
int i;
for (i = 0; i < N_CLIENT; i++)
if (clients[i] == DISABLE)
clients[i] = connfd;
break;
// No enough space in clients array
if (i == N_CLIENT)
fprintf(stderr, "Error: too many clients\\n");
close(connfd);
if (i > max_i)
max_i = i;
if (connfd > max_fd)
max_fd = connfd;
// Check all clients to read data
for (int i = 0; i <= max_i; i++)
int sock_fd;
if ((sock_fd = clients[i]) == DISABLE)
continue;
// If the client is readable or errors occur
ssize_t n = read(sock_fd, buf, BUF_SIZE);
if (n < 0)
fprintf(stderr, "Error: read from socket %d\\n", sock_fd);
close(sock_fd);
clients[i] = DISABLE;
else if (n == 0) // connection closed by client
printf("Close socket %d\\n", sock_fd);
close(sock_fd);
clients[i] = DISABLE;
else
printf("Read %zu bytes from socket %d\\n", n, sock_fd);
write_n(sock_fd, buf, n);
write_n(1, buf, n);
close(listen_fd);
return EXIT_SUCCESS;
2、poll
它和select的功能几乎一样,同样的计算量只需要O(n)。但是,它可以被认为是向上兼容的,因为对要管理的描述符没有限制。
参考代码
#include <arpa/inet.h>
#include <errno.h>
#include <poll.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
#define BACKLOG_SIZE 5
#define BUF_SIZE 1024
#define N_CLIENT 256
#define INF_TIME -1
#define DISABLE -1
int listen_fd;
void int_handle(int n)
close(listen_fd);
exit(EXIT_SUCCESS);
// wirte n byte
ssize_t write_n(int fd, char *ptr, size_t n)
ssize_t n_left = n, n_written;
while (n_left > 0)
if ((n_written = write(fd, ptr, n_left)) <= 0)
return n_written;
n_left -= n_written;
ptr += n_written;
return EXIT_SUCCESS;
int main(int argc, char **argv)
char buf[BUF_SIZE];
struct pollfd clients[N_CLIENT];
// Create listen socket
if ((listen_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
fprintf(stderr, "Error: socket\\n");
return EXIT_FAILURE;
// TCP port number
int port = 8080;
// Initialize server socket address
struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(port);
// Bind socket to an address
if (bind(listen_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) <
0)
fprintf(stderr, "Error: bind\\n");
return EXIT_FAILURE;
// Listen
if (listen(listen_fd, BACKLOG_SIZE) < 0)
fprintf(stderr, "Error: listen\\n");
return EXIT_FAILURE;
// Set INT signal handler
signal(SIGINT, int_handle);
fprintf(stderr, "listen on port %d\\n", port);
clients[0].fd = listen_fd;
clients[0].events = POLLIN;
for (int i = 1; i < N_CLIENT; i++)
clients[i].fd = DISABLE;
int max_i = 0; // max index into clients[] array
while (1)
int n_ready = poll(clients, max_i + 1, INF_TIME);
// Time out
if (n_ready == 0)
continue;
// Error poll
if (n_ready < 0)
fprintf(stderr, "Error: poll %d\\n", errno);
return errno;
// Check new connection
if (clients[0].revents & POLLIN)
struct sockaddr_in client_addr;
socklen_t len_client = sizeof(client_addr);
int connfd;
if ((connfd = accept(listen_fd, (struct sockaddr *)&client_addr,
&len_client)) < 0)
fprintf(stderr, "Error: accept\\n");
return EXIT_FAILURE;
printf("Accept socket %d (%s : %hu)\\n", connfd,
inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
// Save client socket into clients array
int i;
for (i = 0; i < N_CLIENT; i++)
if (clients[i].fd == DISABLE)
clients[i].fd = connfd;
break;
// No enough space in clients array
if (i == N_CLIENT)
fprintf(stderr, "Error: too many clients\\n");
close(connfd);
clients[i].events = POLLIN;
if (i > max_i)
max_i = i;
// Check all clients to read data
for (int i = 1; i <= max_i; i++)
int sock_fd;
if ((sock_fd = clients[i].fd) == DISABLE)
continue;
// If the client is readable or errors occur
if (clients[i].revents & (POLLIN | POLLERR))
ssize_t n = read(sock_fd, buf, BUF_SIZE);
if (n < 0)
fprintf(stderr, "Error: read from socket %d\\n", sock_fd);
close(sock_fd);
clients[i].fd = DISABLE;
else if (n == 0) // connection closed by client
printf("Close socket %d\\n", sock_fd);
close(sock_fd);
clients[i].fd = DISABLE;
else
printf("Read %zu bytes from socket %d\\n", n, sock_fd);
write_n(sock_fd, buf, n);
write_n(1, buf, n);
close(listen_fd);
return EXIT_SUCCESS;
3、epoll
select低效的另一个原因在于程序不知道哪些socket收到数据,只能一个个遍历。如果内核维护一个“就绪列表”,引用收到数据的socket,就能避免遍历。
由于描述符的状态是在内核空间监控的,所以直接返回修改后的描述符,所以计算量为O(1),可以高速处理。
系统调用帮助我们在内核中创建和管理上下文。我们将任务分为 3 个步骤:
#include <sys/epoll.h>
# 调用epoll_create()创建一个ep对象,即红黑树的根节点,返回一个文件句柄
int epoll_create(int size);
# 调用epoll_ctl()向这个ep对象(红黑树)中添加、删除、修改感兴趣的事件
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
# 调用epoll_wait()等待,当有事件发生时网卡驱动会调用fd上注册的函数并将该fd添加到rdlist中,解除阻塞
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
参考代码
#include <arpa/inet.h>
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/epoll.h>
#include <sys/socket.h>
#include <unistd.h>
#define BACKLOG_SIZE 5
#define BUF_SIZE 1024
#define N_CLIENT 256
#define INF_TIME -1
#define DISABLE -1
int listen_fd;
void int_handle(int n)
close(listen_fd);
exit(EXIT_SUCCESS);
// wirte n byte
ssize_t write_n(int fd, char *ptr, size_t n)
ssize_t n_left = n, n_written;
while (n_left > 0)
if ((n_written = write(fd, ptr, n_left)) <= 0)
return n_written;
n_left -= n_written;
ptr += n_written;
return EXIT_SUCCESS;
int main(int argc, char **argv)
char buf[BUF_SIZE];
// Create listen socket
if ((listen_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
fprintf(stderr, "Error: socket\\n");
return EXIT_FAILURE;
// TCP port number
int port = 8080;
// Initialize server socket address
struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(port);
// Bind socket to an address
if (bind(listen_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) <
0)
fprintf(stderr, "Error: bind\\n");
return EXIT_FAILURE;
// Listen
if (listen(listen_fd, BACKLOG_SIZE) < 0)
fprintf(stderr, "Error: listen\\n");
return EXIT_FAILURE;
// Set INT signal handler
signal(SIGINT, int_handle);
fprintf(stderr, "listen on port %d\\n", port);
// Create epoll
int epfd = epoll_create1(0);
if (epfd < 0)
fprintf(stderr, "Error: epoll create\\n");
close(listen_fd);
return EXIT_FAILURE;
struct epoll_event listen_ev;
memset(&listen_ev, 0, sizeof(listen_ev));
listen_ev.events = EPOLLIN;
listen_ev.data.fd = listen_fd;
if (epoll_ctl(epfd, EPOLL_CTL_ADD, listen_fd, &listen_ev) < 0)
fprintf(stderr, "Error: epoll ctl add listen\\n");
close(listen_fd);
return EXIT_FAILURE;
struct epoll_event evs[N_CLIENT];
while (1)
// Wait epoll listener
int n_fds = epoll_wait(epfd, evs, N_CLIENT, -1);
// Error epoll
if (n_fds < 0)
fprintf(stderr, "Error: epoll wait\\n");
close(listen_fd);
return EXIT_FAILURE;
for (int i = 0; i < n_fds; i++)
if (evs[i].data.fd == listen_fd) // Add epoll listener
struct sockaddr_in client_addr;
socklen_t len_client = sizeof(client_addr);
int conn_fd;
if ((conn_fd = accept(listen_fd, (struct sockaddr *)&client_addr,
&len_client)) < 0)
fprintf(stderr, "Error: accept\\n");
return EXIT_FAILURE;
printf("Accept socket %d (%s : %hu)\\n", conn_fd,
inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
struct epoll_event conn_ev;
memset(&conn_ev, 0, sizeof(listen_ev));
conn_ev.events = EPOLLIN;
conn_ev.data.fd = conn_fd;
if (epoll_ctl(epfd, EPOLL_CTL_ADD, conn_fd, &conn_ev) < 0)
fprintf(stderr, "Error: epoll ctl add listen\\n");
close(listen_fd);
return EXIT_FAILURE;
else if (evs[i].events & EPOLLIN) // Read data from client
int sock_fd = evs[i].data.fd;
ssize_t n = read(sock_fd, buf, BUF_SIZE);
if (n < 0)
fprintf(stderr, "Error: read from socket %d\\n", sock_fd);
close(sock_fd);
else if (n == 0) // connection closed by client
printf("Close socket %d\\n", sock_fd);
struct epoll_event sock_ev;
memset(&sock_ev, 0, sizeof(listen_ev));
sock_ev.events = EPOLLIN;
sock_ev.data.fd = sock_fd;
if (epoll_ctl(epfd, EPOLL_CTL_DEL, sock_fd, &sock_ev) < 0)
fprintf(stderr, "Error: epoll ctl dell\\n");
close(listen_fd);
return EXIT_FAILURE;
close(sock_fd);
else
printf("Read %zu bytes from socket %d\\n", n, sock_fd);
write_n(sock_fd, buf, n);
write_n(1, buf, n);
close(listen_fd);
return EXIT_SUCCESS;
四、io_uring
io_uring是由 Facebook 的 Jens Axboe 创建的用于 Linux 的新异步 I/O API。它旨在提供一个不受当前select、poll、epoll或aio系列系统调用限制的 API。
从linux kernel 5.1 开始提供一个新的异步 I/O API。处理是通过操纵两个环形缓冲区,提交队列(SQ)和完成队列(CQ )来执行的。例如,要执行从套接字接收消息,通过将操作码提交队列条目 (SQE) 注入 SQ 来异步执行处理。这些进程的完成通知也可以通过从CQ接收完成队列条目 (CQE) 来实现。
io_uring 的用户态 API
io_uring_setup(2)
io_uring_enter(2)
io_uring_register(2)
一个名为 liburing 的库为 io_uring 提供了一个简单的接口。
#include <arpa/inet.h>
#include <errno.h>
#include <liburing.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
#define BACKLOG_SIZE 5
#define BUF_SIZE 1024
#define N_CLIENT 256
#define N_ENTRY 2048
#define GID 1
int listen_fd;
enum
ACCEPT,
READ,
WRITE,
;
typedef struct UserData
__u32 fd;
__u16 type;
UserData;
void int_handle(int n)
close(listen_fd);
exit(EXIT_SUCCESS);
// wirte n byte
ssize_t write_n(int fd, char *ptr, size_t n)
ssize_t n_left = n, n_written;
while (n_left > 0)
if ((n_written = write(fd, ptr, n_left)) <= 0)
return n_written;
n_left -= n_written;
ptr += n_written;
return EXIT_SUCCESS;
int main(int argc, char **argv)
char buf[BUF_SIZE] = 0;
// Create listen socket
if ((listen_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
fprintf(stderr, "Error: socket\\n");
return EXIT_FAILURE;
// TCP port number
int port = 8080;
// Initialize server socket address
struct sockaddr_in server_addr, client_addr;
socklen_t client_len = sizeof(client_addr);
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(port);
// Bind socket to an address
if (bind(listen_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) <
0)
fprintf(stderr, "Error: bind\\n");
return EXIT_FAILURE;
// Listen
if (listen(listen_fd, BACKLOG_SIZE) < 0)
fprintf(stderr, "Error: listen\\n");
return EXIT_FAILURE;
// Set INT signal handler
signal(SIGINT, int_handle);
fprintf(stderr, "listen on port %d\\n", port);
// Initialize io_uring
struct io_uring ring;
struct io_uring_sqe *sqe;
struct io_uring_cqe *cqe;
int init_ret = io_uring_queue_init(N_ENTRY, &ring, 0);
if (init_ret < 0)
fprintf(stderr, "Error: init io_uring queue %d\\n", init_ret);
close(listen_fd);
return EXIT_FAILURE;
// Setup first accept
sqe = io_uring_get_sqe(&ring);
io_uring_prep_accept(sqe, listen_fd, (struct sockaddr *)&client_addr,
&client_len, 0);
io_uring_sqe_set_flags(sqe, 0);
UserData conn_info =
.fd = listen_fd,
.type = ACCEPT,
;
memcpy(&sqe->user_data, &conn_info, sizeof(conn_info));
while (1)
io_uring_submit(&ring);
io_uring_wait_cqe(&ring, &cqe);
struct UserData conn_info;
memcpy(&conn_info, &cqe->user_data, sizeof(conn_info));
int type = conn_info.type;
if (cqe->res == -ENOBUFS)
fprintf(stderr, "Error: no buffer %d\\n", cqe->res);
close(listen_fd);
return EXIT_FAILURE;
else if (type == ACCEPT)
int conn_fd = cqe->res;
printf("Accept socket %d \\n", conn_fd);
if (conn_fd >= 0) // no error
// Read from client
sqe = io_uring_get_sqe(&ring);
io_uring_prep_recv(sqe, conn_fd, buf, BUF_SIZE, 0);
UserData read_info =
.fd = conn_fd,
.type = READ,
;
memcpy(&sqe->user_data, &read_info, sizeof(read_info));
// Add new client
sqe = io_uring_get_sqe(&ring);
io_uring_prep_accept(sqe, listen_fd, (struct sockaddr *)&client_addr,
&client_len, 0);
io_uring_sqe_set_flags(sqe, 0);
UserData conn_info =
.fd = listen_fd,
.type = ACCEPT,
;
memcpy(&sqe->user_data, &conn_info, sizeof(conn_info));
else if (type == READ)
int n_byte = cqe->res;
if (cqe->res <= 0) // connection closed by client
printf("Close socket %d\\n", conn_info.fd);
close(conn_info.fd);
else
// Add Write
printf("Read %d bytes from socket %d\\n", n_byte, conn_info.fd);
sqe = io_uring_get_sqe(&ring);
io_uring_prep_send(sqe, conn_info.fd, buf, n_byte, 0);
write_n(1, buf, n_byte); // output stdout
io_uring_sqe_set_flags(sqe, 0);
UserData write_info =
.fd = conn_info.fd,
.type = WRITE,
;
memcpy(&sqe->user_data, &write_info, sizeof(write_info));
else if (type == WRITE)
// Add read
sqe = io_uring_get_sqe(&ring);
io_uring_prep_recv(sqe, conn_info.fd, buf, BUF_SIZE, 0);
UserData read_info =
.fd = conn_info.fd,
.type = READ,
;
memcpy(&sqe->user_data, &read_info, sizeof(read_info));
io_uring_cqe_seen(&ring, cqe);
close(listen_fd);
return EXIT_SUCCESS;
i/o模型演变
文章目录1BIO2NIO3select4epoll1BIO 在Linux系统早期,由于socket是阻塞的,用户进程需要阻塞等待请求到达,所以需要为每个socket开启一个进程。这时缺点显而易见,开启越多的进程则需要越多的内存,同时CPU的... 查看详情
i/o模型演变
文章目录1BIO2NIO3select4epoll1BIO 在Linux系统早期,由于socket是阻塞的,用户进程需要阻塞等待请求到达,所以需要为每个socket开启一个进程。这时缺点显而易见,开启越多的进程则需要越多的内存,同时CPU的... 查看详情
pile读书笔记_标准i/o
在学习和分析标准I/O库的同时,可以重点与Linux的I/O系统调用进行比较。stdin、stdout和stderr都是FILE类型的文件指针,是由C库静态定义的,直接与文件描述符0、1和2相关联,所以应用程序可以直接使用它们。其中,stdin是不可写的... 查看详情
网络编程笔记linux系统常见的网络编程i/o模型简述
1.典型的I/O模型根据”UnixNetworkProgrammingVolume1”一书第6.2节的说明,Linux系统支持的典型I/O模型包含下面5种:阻塞I/O(blockingI/O)非阻塞I/O(nonblockingI/O)I/O多路复用(I/Omultiplexing,e.g.selec 查看详情
linux操作系统原理—i/o处理流程
目录文章目录目录LinuxI/O接口read()和write()处理流程I/O性能优化机制I/Obuff/cachePageCacheBufferedI/O零拷贝技术LinuxI/O接口LinuxI/O接口可以分为以下几种类型:文件I/O接口:用于对文件进行读写操作的接口,包括open()、read()、write()、close()... 查看详情
linux运维进阶之路
...越摸不着头脑。到最后不了了之。在我看来,这些同学的学习方法和学习过程都是盲目的,没有目标,没有目的性,只是在随便的翻阅一些文档和笔记,没有制定自己的学习计划。以至于基础本身就没学完全,没搞透彻基础知识... 查看详情
linux运维进阶之路
...。到最后不了了之。 在我看来,这些同学的学习方法和学习过程都是盲目的,没有目标,没有目的性,只是在随便的翻阅一些文档和笔记,没有制定自己的学习计划。以至于基础本身就没学完全,没搞透彻基础知识... 查看详情
关于linux性能调优中网络i/o的一些笔记(代码片段)
写在前面和小伙伴分享一些Linux网络优化的笔记,内容很浅,可以用作入门博文内容结合《Linux性能优化》读书笔记整理涉及内容包括常用的优化工具(mii-tool,ethtool,ifconfig,ip,sar,iptraf,netstat)使用Demo及对应的输出解释具体的调优策... 查看详情
关于linux性能调优中网络i/o的一些笔记(代码片段)
写在前面和小伙伴分享一些Linux网络优化的笔记,内容很浅,可以用作入门博文内容结合《Linux性能优化》读书笔记整理涉及内容包括常用的优化工具(mii-tool,ethtool,ifconfig,ip,sar,iptraf,netstat)使用Demo及对应的输出解释具体的调优策... 查看详情
开发成长之路(13)--linux网络服务端编程(通识篇)(代码片段)
文章目录文件I/O进程线程SOCKET网络编程epoll线程池数据库专区文件I/O引用一句经典的话:“UNIX下一切皆文件”。文件是一种抽象机制,它提供了一种方式用来存储信息以及在后面进行读取。在创建一个文件后,它会给... 查看详情
linux学习笔记linux内核6.0.2目录结构(代码片段)
一、linux内核目录arch包含和硬件体系结构相关的代码,每种平台占一个相应的目录,如i386、arm、arm64、powerpc、mips等。Linux内核目前已经支持30种左右的体系结构。在arch目录下,存放的是各个平台以及各个平台的芯片... 查看详情
linux磁盘i/o是怎么工作的(下)
掌握了磁盘I/O的工作原理,怎么才能衡量磁盘的I/O性能?一、磁盘性能指标说到磁盘性能的衡量标准,必须要提到五个常见指标,也就是我们经常用到的,使用率、饱和度、IOPS、吞吐量以及响应时间等。这五个指标,是衡量... 查看详情
linux开发之libaio源码分析及应用(代码片段)
...at公司基于Linux内核的符号表封装了一套异步I/O(简称aio)的接口,并提供了一些新的接口用来简化上下文配置,开成一个库,命名为libaio。glibc后来有实现一套异步I/O,其实现是用多线程来封装同步的I/O以达到异步... 查看详情
linux学习笔记(第一周)
Linux笔记基本概念UNIX体系结构操作系统(内核):一种软件,控制计算机硬件资源,提供程序运行环境。内核的接口被称为系统调用(systemcall,图中的阴影部分)。公用函数库构建在系统调用接口之上,应用程序既可使用公用函... 查看详情
linux性能优化实战:linux磁盘i/o是怎么工作的(上)(24)
...态磁盘3、相同磁盘随机I/O比连续I/O慢很多4、最小单位5、接口6、RAID陈列卡7、网路存储二、通用块层1、概念2、第一功能3、第二功能4、I/O调度算法三、I/O栈1、Linux存储系统I/O栈全景图 2、全景图详解1、文件系统层2、通用块... 查看详情
java学习之路--i/o流
java基础学习总结——流一、JAVA流式输入/输出原理 流是用来读写数据的,java有一个类叫File,它封装的是文件的文件名,只是内存里面的一个对象,真正的文件是在硬盘上的一块空间,在这个文件里面存放着各种各... 查看详情
linux系统编程:基础io上简单复习c语言文件接口|学习系统文件接口|认识文件描述符|linux下,一切皆文件|重定向原理(代码片段)
...inux下,一切皆文件”二、系统文件I/O💦为什么要学习文件系统接口💦测试用例一💦测试用例二💦测试用例三💦标志位💦open的返回值💦重定向 查看详情
linux系统编程:基础io上简单复习c语言文件接口|学习系统文件接口|认识文件描述符|linux下,一切皆文件|重定向原理(代码片段)
...inux下,一切皆文件”二、系统文件I/O💦为什么要学习文件系统接口💦测试用例一💦测试用例二💦测试用例三💦标志位💦open的返回值💦重定向 查看详情