1. 文件描述符-非阻塞模式
fcntl(fd, F_SETFL, fcntl(fd, F_GETFD, 0) | O_NONBLOCK);
代码解释:
上面这句代码,先查询文件描述符 fd 当前的标志,然后将 O_NONBLOCK 标志加入,并通过 F_SETFL 更新文件描述符,最终实现将该文件描述符切换为非阻塞模式。
O_NONBLOCK 设置在文件描述符上时,后续对该文件描述符的 I/O 操作(读写等)会变为非阻塞模式。
在非阻塞模式下,如果 I/O 操作不能立即完成(例如,因为没有数据可读 或 写缓冲区满),系统不会让调用进程阻塞等待,而是立即返回一个错误(通常为 EAGAIN 或 EWOULDBLOCK)。
这样,进程可以避免因等待 I/O 完成而被长时间阻塞,实现更高的响应性和并发性。
非阻塞模式—应用场景举例:
int accept(int sockfd, struct sockaddr* addr, socklen_t* len)
accept()函数,用于接受客户端的连接请求。默认情况下,accept() 是阻塞的。这意味着,如果没有待处理的连接请求(即没有客户端尝试连接到服务器),accept() 会一直阻塞,直到有新的连接请求到达或发生其他特定条件(如超时)为止。
当 accept() 处于阻塞状态时,它所在的线程是不能去做其他事情的。
若要改变这种行为,可以采用以下方法:
-
非阻塞模式:如前所述,可以使用
fcntl()
函数将套接字设置为非阻塞模式(使用O_NONBLOCK
标志)。在这种模式下,accept()
调用将不会阻塞,而是立即返回。如果此时没有待处理的连接请求,accept()
将返回一个错误(通常为EAGAIN
或EWOULDBLOCK
)。这样,线程可以在没有可用连接时执行其他任务,然后在适当的时候再次尝试accept()
。 -
异步 I/O:如 Linux 中的
io_uring
或 Windows 中的 overlapped I/O,可以异步地执行accept()
,使得线程在发起accept()
请求后可以继续执行其他任务,然后通过回调、事件通知等方式获知accept()
的结果。
2. mmap() 和 munmap()
mmap() 和 munmap() 是用于内存映射操作的系统调用函数。
mmap() 允许将文件或其他对象直接映射到进程的虚拟地址空间中,从而实现高效的数据访问。
void* mmap(void* addr, size_t length, int prot, int flags, int fd, off_t offset);
-
void* addr
:- 指定映射开始的期望虚拟地址。如果设为
NULL
,内核将自动选择一个合适的地址。
- 指定映射开始的期望虚拟地址。如果设为
-
size_t length
:- 指定映射区域的长度。必须是页面大小的整数倍。
-
int prot
:- 控制映射区域的访问权限:
PROT_READ
:允许读取。PROT_WRITE
:允许写入。PROT_NONE
:禁止访问。
- 控制映射区域的访问权限:
-
int flags
:- 控制映射行为和映射内容的处理方式:
MAP_SHARED
:创建一个可共享的映射,对映射区域的修改会影响到所有映射此区域的进程,并且可能会同步回文件。MAP_PRIVATE
:创建一个私有映射,对映射区域的修改仅影响当前进程,不会改变底层文件。
- 控制映射行为和映射内容的处理方式:
-
int fd
:- 用于指定要映射的文件。如果使用匿名映射,可以传入
-1
或者一个未打开的文件描述符。
- 用于指定要映射的文件。如果使用匿名映射,可以传入
-
off_t offset
:- 文件中的偏移量,映射从该位置开始。通常应是文件系统块大小的整数倍。
返回值:
- 成功时返回映射区域的起始地址。
- 出现错误时返回
MAP_FAILED
(通常是一个负值,如(void*) -1
)。
int munmap(void* addr, size_t length);
void* addr
:- 指定要解除映射的内存区域的起始地址,该地址应是之前
mmap()
调用返回的地址。
- 指定要解除映射的内存区域的起始地址,该地址应是之前
size_t length
:- 指定解除映射的区域长度,应与对应的
mmap()
调用中的length
参数一致。
- 指定解除映射的区域长度,应与对应的
返回值: 成功时返回 0
。出现错误时返回 -1
。
mmap()
常用于以下场景:
- 高效文件访问:将文件直接映射到内存,避免了常规的文件I/O操作,对于大数据文件或频繁访问的文件尤其高效。
- 进程间通信(IPC):通过映射同一份共享内存,不同进程可以直接通过内存地址来交换数据,无需通过管道、套接字等传统IPC机制。
例如,映射一个已打开文件的部分内容到内存中:
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <cerrno>
int main() {
const char* filename = "/path/to/file";
int fd = open(filename, O_RDONLY);
if (fd == -1) {
perror("open");
return 1;
}
struct stat sb;
if (fstat(fd, &sb) == -1) {
perror("fstat");
close(fd);
return 1;
}
// 映射整个文件
void* mapped_region = mmap(nullptr, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
if (mapped_region == MAP_FAILED) {
perror("mmap");
close(fd);
return 1;
}
// 使用映射区域...
// ...
// 解除映射
if (munmap(mapped_region, sb.st_size) == -1) {
perror("munmap");
close(fd);
return 1;
}
close(fd);
return 0;
}
在WebServer中,使用方法也类似;下面对上面的代码做简单的分析:
int stat(const char *path, struct stat *buf);
stat() 是一个标准C库函数,通常在 <sys/stat.h> 头文件中声明,用于获取文件或目录的状态信息。
const char *path
: 一个指向包含文件路径的指针。可以是绝对路径,也可以是相对于当前工作目录的相对路径。struct stat *buf
: 一个指向struct stat
结构体的指针,用于接收由stat()
函数填充的关于指定路径对象的信息,例如:
– 文件大小(st_size
)
– 最近访问、修改和状态更改时间(st_atime
,st_mtime
,st_ctime
)
– 权限位(st_mode
)
函数返回值:
- 如果成功,返回0;否则,返回一个非零值(通常是错误代码),表示发生了某种错误(如权限不足、路径不存在等)。
3. io[]
4. 服务器如何发送响应
5. 服务器对连接的处理流程
- 读取缓冲区的信息(http请求)
- 解析请求
- 根据解析内容,服务器端进行可能的操作(如在数据库内进行增删改查)
- 生成响应