unix 高级I/O详解


今天我将总结一下标准I/O的一些内容。

非阻塞I/O:

对于这个概念,我们得先知道低速系统调用,它在被调用后可能会使进程永远阻塞的一类系统调用。而非阻塞I/O,就是说在操作这些I/O的时候不会永远阻塞。如果这种操作不能完成,那么将立即出错返回,表示该操作如果继续执行那么将阻塞。

那么对于一个描述符制定非阻塞I/O的操作有两种:

(1)调用open函数获得描述符,则制定O_NONBLOCK标志

(2) 调用fcntl函数可以修改已经打开的描述符,由函数开启O_NONBLOCK文件状态标志。

记录锁:

记录锁的作用是防止在同一时间对同一个文件(更准确的是字节范围)的修改,也就是说在一个时间段内,只能有一个进程可以修改指定字节范围的数据。

对于记录锁的操作一般用函数fcntl:

#include<fcntl.h>
int fcntl( int fd, int cmd, .../* struct flock * flockptr*/);
对于记录锁,cmd取F_GETLK, F_SETLK. F_SETLKW。使用方法查看man fcntl。
对于flock结构体
struct flock
{
      short l_type;
      off_t l_start;
      short l_whence;   
      off_t l_len;
      pid_t l_pid;
};
关于这个结构体详细信息问男人。。。。

注意:

1.加写锁的时候,必须是在已O_WRONLY,打开,加读锁必须是O_RDONLY打开。

2. 当设置、释放锁的时候,系统会组合或者裂开相邻区域。如下图


对于锁的隐含继承和释放有两点:

1. 当进程终止的时候,它锁建立的锁全部释放。
2. 当多个文件描述符指向同一个文件的时候,那么只要close任意一个文件那么该文件上的所有记录锁全部都释放。

有不理解吗?看下图

看到了吗?关于锁的记录是放在i节点的哦?所以上面的两点就不难理解了撒。struct lockf记录的进程ID,fork后子进程和父进程pid就不同,当然锁就不会继承了撒。而当我们close一个文件的时候,就会到i节点上将所有该pid的struct lockf全部清空。


对于记录锁的尾端加锁,需要注意的是宏SEEK_END,SEEK_CUR是不断变化的,所以呢,我们必须是使用独立于当前文件偏移量或文件尾端而记住锁。

最后一个知识点了,对于建议锁和强制锁的理解

建议锁就是说,我们所有程序在执行的时候,我们按照约定,都先去获得对应文件锁后,在进行操作,但是如果有程序不这样做,那么就没有其它的约束了。由此就出现了强制性锁,它指的是使内核对每一个open、read、write系统调用都要检查是不是和某个锁有冲突。但是对于强制锁的实现,各个系统并没有统一起来,甚至还有系统不支持。对于linux中,如果要使用强制锁,则用命令_omand选项打开。有一种实现是这样对一个特定文件打开其设置组ID位并且关闭组执行位,则表示开启了强制性锁机制。

I/O多路复用:

还记得我们在系统调用read,write系统调用的读取终端或者是读取网络文件的时候,我们称这为慢系统调用,因为read或者write的时候可能会造成进程的永久阻塞。

那么如果我们要同时读和写多个文件,比如说telnet命令,它既要从终端读入数据,又要从守护进程读数据,并且写到终端中去


对于这种情况呢?我们不能使telnet命令简单的调用read,因为终端和守护进程都是输入,不能因为任何一个read而阻塞整个telnet,因为不能确定那一个输入才是程序想要的。

要解决这种有多种方式:

(1). 直接新建两个进程如下图:

虽然这种方法也可以实现该功能,但是它也有缺点,首先创建两个进程系统花销大,而且如果telnet结束,当子进程退出,会给父进程发送SIGHUP信号,那么可以实现两个进程都推出,但是呢,如果父进程先推出,就不会由系统产生信号通知子进程,这时就只能是由程序调度kill函数发送信号,或者通过IPC进行通信,这样就会大大加大对这个程序的复杂性。

(2)还可以通过信号机制实现如信号SIGIO。这里不详解了。

(3)就是马上要说到的I/O多路复用

对于I/O多路复用不得不说的三个函数select/pselect/poll。

#include  <sys/select.h>
int select(int nfds, fd_set *readfds, fd_set *writefds,
                  fd_set *exceptfds, struct timeval *timeout);
关于这个函数的参数:

nfds:是要查询文件描述符的最大值+1,这是为了减少内核查询次数,提高效率
readfds,writefds,exceptfds:分别表示的指向描述的指针,他们代表的是当前所关心的读写异常的描述符
timeval:表示的是超时时间。如果在timeval中没有条件满足,那么该函数不会在阻塞,会返回0,否则返回准备好的数目(详情man select)
这个函数作用,就是当我们在函数中fd_set设置的我们关心的文件描述符的情况满足,比如我们关心读的文件,现在可以read,并且不会发生阻塞的情况发生,那么该函数就会返回并且修改fd_set,将准备就绪的文件描述符位进行设置,那么我们就只需要检查文件描述位,就可以知道那些文件准备好了。它实现了同时监听多个文件描述符,并且在没有文件设备准备好情况下,会在指定时间返回。

 #include <sys/select.h>

       int pselect(int nfds, fd_set *readfds, 
		fd_set *writefds, fd_set *exceptfds,
		const struct timespec *timeout,
		const sigset_t *sigmask);
其实这个函数和select函数用途是差不多的,只是在select上面进行了一些改进,有两点首先是超时时间进行了修改,由于select函数中的超时时间是可修改的,在不同系统对这个超时时间的操作不一样,有些系统在函数返回时,用这个结构表示还有多长时间未等待,有的不修改,而在pselect中将他们统一起来都不修改,并且设置为const类型。另外一点是添加了第六个参数,这是一个信号掩码表,它表示在进入这个函数的时候将进程掩码修改为sigmask,函数返回后则还原回来,这样有一个好处,就是在pselect函数的时候,不会被非期望的信号打断。

#include <poll.h>
       int poll(struct pollfd *fds, nfds_t nfds, int timeout);

这个函数和select函数功能都是差不多的。详情请man

散布读和聚集写:

它就是说可以一次性读取或者写多个不连续的缓冲区。它用到连个函数readv和writev函数

 	#include <sys/uio.h> 
	ssize_t readv(int fd, const struct iovec *iov, int iovcnt);
     	ssize_t writev(int fd, const struct iovec *iov, int iovcnt);

参数中iovec结构体如下

  struct iovec {
               void  *iov_base;    /* Starting address */
               size_t iov_len;     /* Number of bytes to transfer */
           };
该结构体指定了多个缓冲区如下图所示



注意对于数组的大小要小于IOV_MAX


存储映射I/O:

Memory-mapped I/O 使一个磁盘文件与存储空间中的一个缓冲区相映射,于是当从缓冲区中读取数据,就相当于读文件相应字节。写也一样,但是为了不同的效果,要设置一些属性。

 #include <sys/mman.h>

 void *mmap(void *addr, size_t length, int prot, int flags,
                  int fd, off_t offset);
该函数如果成功则返回映射区地址,出错返回MAP_FAILED

对于addr参数是指定存储区起始地址。通常设置为0,表示由系统选择起始地址。
fd则表示被映射文件的描述符
off:则表示fd指定文件的起始文件的相对位置
对于参数prot,依照下表:

注意哦,对存储区的操作权限是不能超过open文件所指定的权限的。
参数flag:
      MAP_FIXED:表示函数返回值必须登陆addr。
      MAP_SHARED:本进程对映射区的所有操作和操作原文件一样效果
      MAP_PRIVATE:对映射区的操作导致创建一个该映射文件的一个私有副本,而不是原文件。

                                                    




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值