提示:
一、阻塞IO与非阻塞IO
对于这两个概念,我是这样的理解的,IO就好比通道,那么阻塞IO就是阻塞的通道,那如果通道被阻塞了,车辆只能停下来休息,那么非阻塞正好相反。
阻塞IO:当资源不可获取时,程序会进入到休眠状态,让出cpu资源,直到资源可以获取才被唤醒
非阻塞IO:不管资源是否可以获取都会立即返回
二、IO多路复用
I/O 多路复用技术是为了解决:在并发式 I/O 场景中进程或线程阻塞到某个 I/O 系统调用而出现的技术,使进程不阻塞于某个特定的I/O 系统调用。监视多路IO
1.select
系统调用 select()可用于执行 I/O 多路复用操作,调用 select()会一直阻塞,直到某一个或多个文件描述
符成为就绪态(可以读或写)
#include <sys/select.h>
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
args:
nfds:最大文件描述符+1
readfds:读文件描述符集合
writefds:写文件描述符集合
exceptfds:异常文件描述符检测
timeout:超时时间
return:
-1:错误
0:超时
正数:有一个或多个文件描述符以达到就绪态
#将fd从set中移除
void FD_CLR(int fd, fd_set *set);
int FD_ISSET(int fd, fd_set *set);
#将fd添加进set
void FD_SET(int fd, fd_set *set);
#将set集合清空
void FD_ZERO(fd_set *set);
2.poll
与select类似,在select中有三个fd_set集合,poll中需要构造一个struct pollfd的数组,每个数组指定一个文件描述符
#include <poll.h>
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
args:
fds:指向struct pollfd类型数组
nfds:指定fds中元素个数,数据类型nfds_t实际为无符号整型
timeout:超时
return:
-1:错误,设置errno
0:超时
正数:表示fds数组中返回的revents变量不为0的struct pollfd对象的数量
三、异步IO
在IO多路复用中,进程通过系统调用select()或者poll()主动查询文件描述符是否可以执行IO操作,而异步IO,当文件描述符可以执行IO操作时,进程可以请求内核为自己发送一个信号。
要使用异步IO,需要满足一下条件:
1 指定O_NONBLOCK标志
2 指定O_ASYNC标志 flag | = O_ASYNC
3 设置异步IO事件的接收进程 fcntl(fd, F_SETOWN, getpid());
4 设置一个信号处理函数 SIGIO
但事实上SIGIO是是非排队信号,它不支持信号排队机制,比如当前正在执行SIGIO信号处理函数,此时内核又发送多次SIGIO信号给进程,这些信号将会被阻塞,只有当前信号执行完毕之后才会传递给进程,并且只能传递一次,而后续信号都会丢失
使用实时信号替换默认的SIGIO信号
#设置实时信号
fcntl(fd, F_SETSIG, SIGRTMIN);
#绑定信号处理函数
signal(SIGRTMIN, sigrtmin_handler);
使用sigaction()函数注册信号处理函数
#include<signal.h>
int sigaction(int signum,const struct sigaction *act ,struct sigaction *oldact);
struct sigaction {
void (*sa_handler)(int);
void (*sa_sigaction)(int, siginfo_t *, void *);
sigset_t sa_mask;
int sa_flags; //此参数指定为SA_SIGINFO,表示使用void (*sa_sigaction)(int, siginfo_t *, void *);作为处理函数
void (*sa_restorer)(void);
};
struct siginfo_t {
int si_signo; //引发处理函数被调用的信号。这个值与信号处理函数的第一个参数一致。
int si_fd; //表示发生异步 I/O 事件的文件描述符;
int si_code; //表示文件描述符 si_fd 发生了什么事件,读就绪态、写就绪态或者是异常事件等。
int si_band; //是一个位掩码, 其中包含的值与系统调用 poll()中返回的 revents 字段中的值相同。
};
si_code si_band掩码值 描述/说明
POLL_IN POLLIN | POLLRDNORM 可读取数据
POLL_OUT POLLOUT | POLLWRNORM | POLLWRBAND 可写入数据
POLL_MSG POLLIN | POLLRDNORM | POLLMSG 不使用
POLL_ERR POLLERR I/O错误
POLL_PRI POLLPRI | POLLRDNORM 可读取高优先级数据
POLL_HUP POLLHUP | POLLERR 出现宕机
四、存储映射IO
存储映射IO是一种基于内存区域的高级IO操作,它可以将一个文件映射到进程地址空间的一块内存区域。
#include <sys/mman.h>
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
args:
addr:映射起始地址 (系统页大小的整数倍)
length:映射内存长度
prot:映射区的保护要求
PROT_EXEC: 映射区可执行
PROT_READ: 映射区可读
PROT_WRITE: 映射区可写
PROT_NONE: 映射区不可访问
flags:映射区的属性
MAP_SHARED: 此标志指定当对映射区写入数据时,数据会写入到文件中,也就是会将写入到映射区中的数据更新到文件中,并且允许其它进程共享
MAP_PRIVATE: 此标志指定当对映射区写入数据时,会创建映射文件的一个私人副本(copy-onwrite),对映射区的任何操作都不会更新到文件中,仅仅只是对文件副本进行读写
MAP_ANONYMOUS: 建立匿名映射, 此时会忽略参数 fd 和 offset,不涉及文件,而且映射区域无法和其它进程共享
MAP_LOCKED: 对映射区域进行上锁
fd:文件描述符,要映射到内存区域的文件
offset:文件映射的偏移量 0表示从文件头部开始映射 (系统页大小的整数倍)
int munmap(void *addr, size_t length);
#可以更改映射区的保护要求
int mprotect(void *addr, size_t len, int prot);
#刷新内存数据到存储器上
int msync(void *addr, size_t length, int flags);
args:
flags:MS_ASYNC: 以异步方式进行同步操作。调用 msync()函数之后,并不会等待数据完全写入磁盘之后才返回
MS_SYNC: 以同步方式进行同步操作。调用 msync()函数之后,需等待数据全部写入磁盘之后才返回
MS_INVALIDATE: 是一个可选标志, 请求使同一文件的其它映射无效