非阻塞I/O
(1). open
时指定O_NONBLOCK
.
(2). 对已经打开描述符,调用fcntl
打开O_NONBLOCK
文件标志。
非阻塞描述符执行io
操作,无论操作是否立即完成,均会立即返回.需通过返回结果获知完成情况.
记录/文件范围锁
fcntl记录锁
int fcntl(int fd,
// F_GETLK/F_SETLK/F_SETLKW
// cmd为F_GETLK,参数3-flock*是一个值-结果参数
int cmd, ...);
struct flock {
// F_RDLCK/F_WRLCK/F_UNLCK
short l_type;
// SEEK_SET/SEEK_CUR/SEEK_END
short l_whence;
// start offset
off_t l_start;
off_t l_len;
// 返回持有此锁的进程的ID
pid_t l_pid;
};
调用进程已经持有文件A
区间A
的一个锁后,希望再对文件A
区间A
加另一个锁,处理方式是直接用新锁替换旧锁(进程对文件A
区间A
只加一个锁,锁为最后调用fcntl
施加的那个).
锁的隐含继承和释放
文件上一个锁信息中包含持有此锁的进程信息
(1). 一个进程终止时,其所持有的文件锁全部释放.
(2). 一个描述符关闭,此进程中基于此描述符所引用文件上的锁自动被释放(注意一个文件即使在同一进程也可被多个描述符引用,这里直接指的是描述符引用的文件).
(3). fork
产生的子进程不会继承父进程持有的文件锁,exec
产生的有新的执行环境的进程中对之前进程中未关闭的描述符关联的锁会继承.(描述符的执行时关闭标志,意味着exec
时,原进程有此标志的描述符会自动关闭,再执行新的执行环境的进程).
FreeBSD
记录锁通过文件+
进程来标识.
上述在父进程中,关闭fd1,fd2
或fd3
任一个,都将释放由父进程设置的写锁.
在文件尾端加锁
上面假设首个write
执行时,文件位置在尾部,如希望解除追加的第一个字节的锁,un_lock
应在参数2
传入-1
来找到正确位置(文件尾/当前位置随着文件读写而动态变化).
建议性锁和强制性锁
(1). 所谓建议性锁指的是,我们通过fcntl
对文件A
区域A1
进行加锁.
后续其他进程对文件A
区域A1
进行fcntl
时,会参考此区域已经加锁信息来决定操作是否可行.其他进程若未执行fcntl
,直接对文件A
区域A1
执行read/write
则,此区域已经加锁信息,对read/write
无影响.
(2). 所谓强制性锁指的是,我们通过fcntl
对文件A
区域A1
进行加锁.
后续其他进程对文件A
区域A1
进行read/write/open/fcntl
时均要参考此区域已经加锁信息来决定操作是否可行.
(3). 强制性锁对应于互斥锁
适合于所有使用锁定文件的用户均遵循一致的规则,即访问文件前,先进行锁定,再访问,访问后释放锁定的模式来进行进程间资源共享.
I/O多路转接
select
// < 0/=0/>0 数值是3个集合中准备好描述符个数之和
int select(
// 三个描述符集合中最大描述符+1
int maxfdp1, fd_set *restrict readfds,
fd_set *restrict writefds, fd_set *restrict exceptfds,
// (1).NULL:
// 直到某些描述符已经准备好
// (2).字段tv_sec,tv_usec均为0:
// 立即返回
// (3).有效数值:
// 指定等待时间
struct timeval *restrict tvptr);// 秒和微秒
int FD_ISSET(int fd, fd_set* fdset);
void FD_CLR(int fd, fd_set* fdset);
void FD_SET(int fd, fd_set* fdset);
void FD_ZERO(fd_set* fdset);
基于比特位管理,若windows
上一般只支持数值在1024
之内的描述符使用.
poll
int poll(
struct pollfd fdarray[],
nfds_t nfds,
// -1/0/>0
int nsecs);
struct pollfd
{
int fd;
short events;
short revents;
};
基于数组.
异步I/O
POSIX异步I/O
// 异步I/O控制块
// 异步I/O操作必须显式地指定偏移量
// 异步I/O接口并不影响由操作系统维护的文件偏移量
// 如果用异步I/O向以追加模式打开的文件写数据,aio_offset被忽略
struct aiocb
{
int aio_fildes;
off_t aio_offset;
volatile void *aio_buf;
size_t aio_nbytes;
// priority
int aio_reqprio;
// 在I/O事件完成后,如何通知应用。
struct sigevent aio_sigevent;
int aio_lio_opcode;
};
struct sigevent
{
/*SIGEV_NONE:不通知进程*/
/*SIGEV_SIGNAL:产生由sigev_signo字段指定的信号。
如支持排队信号,且建立信号处理时指定了SA_SIGINFO,则信号入队,
信号处理程序传送一个siginfo结构,字段si_value为sigev_value。*/
/*SIGEV_THREAD:sigev_notify_function指定的函数被调用,sigev_value作为参数传给函数。
除非sigev_notify_attributes字段被设为pthread属性结构的地址,
且该结构指定了一个另外的线程属性,否则该函数将在分离状态下的一个单独线程中执行。*/
int sigev_notify;
int sigev_signo;
union sigval sigev_value;
void (*sigev_notify_function)(union sigval);
pthread_attr_t *sigev_notify_attributes;
};
int aio_read(struct aiocb *aiocb);
int aio_write(struct aiocb *aiocb);
// op设为O_DSYNC,则类似fdatasync
// op设为O_SYNC,则类似fsync
int aio_fsync(int op, struct aiocb* aiocb);
// 0 异步操作完成,需调用aio_return获取返回值
// -1 对aio_error调用失败。errno告知原因。
// EINPROGRESS 异步读,写或同步操作仍在等待
// 其他
int aio_error(const struct aiocb* aiocb);
ssize_t aio_return(const struct aiocb* aiocb);
int aio_suspend(const struct aiocb *const list[], int nent, const struct timespec *timeout);
int aio_cancel(int fd, struct aiocb *aiocb);
int lio_listio(
// 如为LIO_WAIT,在所有指定的I/O操作完成后返回。
// 这时,sigev将被忽略。
// 如为LIO_NOWAIT,立即返回。
// 进程将在所有I/O操作完成后,按sigev参数指定的,被异步地通知。
// 如不想被通知,可把sigev设为NULL。
// 每个AIO控制块本身也可能启用了在各自操作完成时的异步通知。
// AIO控制块中,aio_lio_opcode定操作类型LIO_READ/LIO_WRITE/LIO_NOP
int mode, struct aiocb *restrict const list[restrict],
int nent, struct sigevent *restrict sigev);
readv和writev
ssize_t readv(int fd, const struct iovec *iov, int iovcnt);
ssize_t writev(int fd, const struct iovec *iov, int iovcnt);
struct iovec
{
// 缓存开始地址
void *iov_base;
// 缓存大小
size_t iov_len;
};
存储映射I/O
void* mmap(void* addr, size_t len,
// PROT_READ/PROT_WRITE/PROT_EXEC/PROT_NONE.但保护要求不能超过文件open模式访问权限
int prot,
// MAP_FIXED,返回值需要等于addr。未指定时,即使addr有值,也仅是一个建议值
// MAP_SHARED/MAP_PRIVATE
int flag, int fd, off_t off);
int mprotect(
// 需为系统页长倍数
void *addr, size_t len, int prot);
// 共享映射的内存区域对虚拟内存区域的写直接写到关联的磁盘文件.
// 对所有共享映射进程可见.此调用让内存修改页立即写回磁盘
int msync(
void *addr, size_t len,
// 二选一
// MS_ASYNC
// MS_SYNC
int flags);
int munmap(void *addr, size_t len);