1、低速系统调用是可能会使进程永远阻塞的一类系统调用,包括:
(1)管道、终端设备和网络设备等文件的数据不存在,读操作可能会使调用者永远阻塞。
(2)数据不能被相同的文件类型立即接受(如管道中无空间、网络流控制),写操作可能会使调用者永远阻塞。
(3)在某些条件之前打开某些文件类型可能会发生阻塞。
….
2、非阻塞IO使我们可以发出open、read和write这样的IO操作,并使这些操作不会永远阻塞(可能因为其他进程操作相同文件)。不能操作则立即出错返回,不会死等阻塞。
3、对于给定的文件描述符,两种方法指定非阻塞IO的方法:
(1)如果调用open获得描述符,则可指定O_NONBLOCK标志
(2)对于已经打开的文件描述符,调用fcntl,由该函数打开O_NONBLOCK标志。
4、记录锁:当第一个进程正在读或修改文件的某个部分时,使用记录锁可以阻止其他进程修改同一个文件区。
#include <fcntl.h>
//cmd:FGETLK测试能否建立一把锁;F_SETLK根据第三个参数建锁,不能建则出错返回;F_SETLKW不能建则休眠
int fcntl(int fd, int cmd, struct flock *flockptr);
struct flock{
short l_type;//读锁、写锁、无锁
short l_where;//参考位置在哪:头、当前、尾
off_t l_start;//偏移,以字节为单位
off_t l_len;
pid_t l_pid;
//对整个文件加锁,设置l_where、l_start指向文件起始位置,l_len=0
}
进程对文件再次上锁会替换上次的锁。
若第100-199字节是加锁区,解锁中间区域,则内核维持两把锁。如若再对中间加锁则合并为一把锁。
5、锁的继承和释放
当一个进程终止时,它所建立的锁全部释放;文件关闭时,它上面的锁也全部释放;fork产生的子进程不继承父进程设置的锁。
fd1 = open(pathname,...);
write_lock(fd1, 0, SEEK_SET, 1);
if((pid = fork()) > 0){
fd2 = dup(fd1);
fd3 = open(pathname,...);
}else if(pid == 0){
read_lock(fd1, 1, SEEK_SET, 1);
}
//在父进程中,关闭fd1、fd2或fd3中任意一个都将释放父进程设置的写锁
6、强制性锁:让内核检测每一个open、read、write,验证调用进程是否违背了正在访问的文件上的一把锁。
7、IO多路转接
先构造一张我们感兴趣的描述符列表,然后调用一个函数,直到这些描述符中的一个已经准备进行IO时,该函数才返回。
#include <sys/select.h>
//第一个参数指定描述符最大搜索范围,0-maxfdpl;后面分别为读集、写集、异常集
//返回三个集合中准备好的描述符个数和
//异常条件包括:网络连接上到达带外的数据或者处于数据包模式的伪终端上发生了某些条件
int select(int maxfdpl, fd_set *restrict readfds, fd_set *restrict writefds, fd_set *restrict exceptfds, struct timecal *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);//清零整个集合
//poll功能和select相同接口不同
#include <poll.h>
//nfds指明数组元素的个数,每个元素针对一个描述符
int poll(struct pollfd fdarray[], nfds_t nfds, int timeout);
struct pollfd{
int fd;
short events;
short revents;
}
7、异步IO:当一个异步过程调用发出后,调用者不能立刻得到结果。实际处理这个调用的部件在完成后,通过状态、通知和回调来通知调用者。在一个CPU密集型的应用中,有一些需要处理的数据可能放在磁盘上。预先知道这些数 据的位置,所以预先发起异步IO读请求。等到真正需要用到这些数据的时候,再等待异步IO完成。使用了异步IO,在发起IO请求到实际使用数据这段时间 内,程序还可以继续做其他事情
BSD派生的系统中,SIGIO是通用异步IO信号,SIGURG则是来通知进程网络连接上的带外数据已经到达。
8、POSIX异步IO
异步IO接口使用AIO控制块来描述IO操作。
struct aiocb{
int aio_fildes;//文件描述符
off_t aio_offset;//偏移量
//读,数据会复制到缓冲区;写,数据从缓冲区复制出来
volatile void *aio_buf;
size_t aio_nbytes;//要读写的字节数
int aio_reqprio;
struct sigevent aio_sigevent;//IO事件完成后如何通知应用程序
int aio_lio_opcode;
}
#include <aio.h>
int aio_read(struct aiocb *aiocb);//异步读
int aio_write(struct aiocb *aiocb);//异步写
//让异步操作不等待而写入持久化的存储器中
int aio_fsync(int op, struct aiocb *aiocb );
//获取异步读、写或者同步操作完成的状态
//返回值:0 异步操作完成,此时需要调用aio_return获取操作返回值;-1 该函数调用失败;EINPROGRESS 异步操作仍在等待
int aio_error(const struct aiocb *aiocb);
//一旦调用了该函数,OS会释放掉包含IO操作返回值的记录
ssize_t aio_return(const struct aiocb *aiocb);
//nent是数组元素数目
//挂起使背地里等待异步操作完成,完成就返回
int aio_suspend(const struct aiocb *const list[], int nent, const struct timespec *timeout);
//尝试取消异步IO
//若aiocb为NULL,则系统尝试取消该文件所有异步IO
int aio_cancel(int fd, struct aiocb *aiocb);
//mode决定异步还是同步,如果是同步则最后一个参数无意义,否则其指定异步IO完成后的通知方式
int lio_listio(int mode, struct aiocb *restrict const list[], int nent, struct sigevent *restrict sigev);
9、readv、writev
//对多个非连续的缓冲区读、写
ssize_t readv(int fd, const struct iovec *iov, int iovcnt);
ssize_t writev(int fd, const struct iovec *iov, int iovcnt);
//iov指向结构体数组,每个元素包括一个缓冲区地址和大小
struct iovec{
void *iov_base;
size_t iov_len
}
10、管道、FIFO、终端和网络设备read、write操作返回的数据可能小于要读写的数据。可能是因为内核输出缓冲区满等情况。
11、存储器IO映射:将一个磁盘文件映射到存储空间中的一个缓冲区上,那么此时操作该文件就和操作该缓冲区相对应。与映射区相关的信号:SIGSEGV,SIGBUS。SIGSEGV信号用于指示进程试图访问对他不可用的存储区。SIGBUS用于映射区某个部分在访问时已经不存在。
//建立文件和存储区的映射关系
//addr存储区起始地址;off映射字节在文件中的起始偏移量
//成功返回映射区的起始地址
void *mmap(void *addr, size_t len, int prot, int flag, int fd, off_t off);
//更改一个现有映射的权限
int mprotect(void *addr, size_t len, int prot);
//将修改后的内容冲洗到被映射的文件中
int msync(void *addr, size_t len, int flags);
//解除映射区,当进程关闭时会自动解除存储区的映射
int munmap(void *addr, size_t len);
在输入缓冲区src取数据,内核自动读输入文件;将数据存入输出缓冲区dst,内核自动将数据写到输出文件。
mmap+memcpy类似于read+write。不同在于,read+write,是将数据从内核缓冲区复制到应用缓冲区(read);然后在把数据从应用缓冲区复制到内核缓冲区。mmap+memcpy则直接把数据从映射到地址空间的一个内核缓冲区复制到另一个内核缓冲区。一般前者快一些。
//存储器映射IO复制文件,类似于cp
#define COPYINCR 1024*1024*1024
int main(int argc, char *argv[])
{
int fdin, fdout;
void *src, *dst;
size_t copysz;
struct stat sbuf;
off_t fsz=0;
fdin=open(argv[1], O_RDONLY);
fdout=open(argv[2], O_RDWR | O_CREAT | O_TRUNC | FILE_MODE);
fstat(fdin, &sbuf);
ftruncate(fdout, sbuf.st_size);
while(fsz < sbuf.st_size){
if((sbuf.st_size - fsz)>COPYINCR )
copysz=COPYINCR ;
else
copysz=sbuf.st_size - fsz;
src = mmap(0, copysz, PROT_READ, MAP_SHARED, fdin, fsz);
dst = mmap(0, copysz, PROT_READ | PROT_WRITE, MAP_SHARED, fdin, fsz);
memcpy(des,src,copysz);
munmap(src, copysz);
munmap(dst, copysz);
fsz += copysz;
}
exit(0);
}