先进I/O

非阻塞IO

非阻塞I/O因此,我们可以称之为open、read和write这种I/O操作,而这些操作不会永久阻止。我们假设,该操作不能完成,然后调用立即返回一个错误。则表示该操作将继续作为堵塞。

对于一个给定的描写叙述符有两种方法对其指定非堵塞I/O:

1)      假设调用open获得描写叙述符。则可指定O_NONBLOCK标志

2)      对于已打开的一个描写叙述符,则可调用fcntl。由该函数打开O_NONBLOCK文件状态标志。

记录锁

记录锁的功能是:当一个进程正在读或改动文件的某个部分时,它能够阻止其它进程改动同一文件区。

锁定的是文件里的一个区域。

记录锁函数fcntl:

#include<unistd.h>

#include<fcntl.h>

int fcntl(intfd, int cmd, ... /* arg */ );

对于记录锁。cmd是F_GETLK、F_SETLK或F_SETLKW。

当中第三个參数arg是一个flock结果指针。其结构组成例如以下:

struct flock {

...

short l_type;    /* Type of lock: F_RDLCK,

F_WRLCK,F_UNLCK */

short l_whence;  /* How to interpret l_start:

SEEK_SET,SEEK_CUR, SEEK_END */

off_t l_start;   /* Starting offset for lock */

off_t l_len;     /* Number of bytes to lock */

pid_t l_pid;     /* PID of process blocking our lock

(F_GETLKonly) */

...

};

flock结果说明例如以下:

所希望的所类型:由l_type决定, 可为F_RDLCK(共享锁)、F_WRLCK(独占性写锁)或F_UNLCK(解锁一个区域)。

要加锁或解锁区域的起始字节偏移量。这由l_whence和l_start决定。

区域字节:由l_len决定。

若l_len为0,则表示锁的区域从起点開始直至最大可能偏移量为止。

具有能堵塞当前进程的锁,其持有进程的ID存放在l_pid中(由F_GETLK情况下返回)

共享读锁和独占写锁:多个进程在一个给定的字节上能够有一把共享的读锁。可是在一个给定字节上仅仅能有一个进程独用一把锁。

共享读锁和独占写锁概念是针对不同进程提出锁的请求。不适用于但个进程提出多个锁请求。

当一个进程对一个文件区间已经有了一把锁,后来该进程有企图在同一文件区间再加上一把锁。那么新锁将替换老锁。

加读锁时,该描写叙述符必须是读打开;加写锁时,必须是写的打开。

下面说明fcntl函数的三种命令:

F_GETLK:推断由arg中所描写叙述的锁是否会被另外一把锁排斥(堵塞)。

假设存在一把锁。他阻止创建由arg所描写叙述的锁。则把该现存锁的信息写到arg指向的结构中。假设不存这样的情况,则除了l_type设置为F_UNLCK之外,arg所指向结构中的其它信息保持不变。

F_SETLK:设置由arg所描写叙述的锁。

假设试图建立一把读锁或写锁。但实际情况不同意建立锁(比方已经有写锁),则fcntl出错返回。

F_SETLKW:它是F_SETLK的堵塞版本号。

锁的隐含继承和释放:

关于记录锁的自己主动继承和释放有三条规则:

1.             锁与进程文件双方面有关:第一点非常明显。当一个进程终止时,它所建立的锁所有释放;第二点指的是不论什么时候关闭一个描写叙述符时。则该进程通过这一文件描写叙述符引用的文件上的不论什么一把锁都被释放。

2.             由fork产生的子进程不能继承父进程所设置锁。

这意味着。若一个进程得到一把锁。然后调用fork。那么对于父进程获得的锁而言,子进程被视为还有一个进程,对于继承过来的任一描写叙述符。子进程须要调用fcntl才干获得它自己的锁。这与锁的作用相一致,锁的作用是阻止多个进程同一时候写同一个文件。假设子进程继承父进程的锁,则父子进程就能够同一时候写同一个文件。

3.             在运行exec后,新程序能够继承原运行程序的锁。除非队以文件描写叙述符设置了close-on-exec标志,那么运行exec时,将释放锁。

锁的数据结构实现

考虑一个进程。它运行下列语句:

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)

}

当中write_lock和read_lock调用fcntl的加锁实现。

下图显示了父子进程暂停后的数据结构情况:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvd2Fsa2Vya2Fscg==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">

有了记录锁之后,在原来的数据结构上添加了lockf结构,他们由i节点開始相互连接起。

注意,每个lock结构说明了一个给定进程的一个加锁区域(有偏移量和长度定义)。图中显示了两个lockf结构。一个是由父进程调用write_lock形成的,还有一个则由子进程调用read_lock形成的。

每个结构都包括了对应的进程ID。

在父进程中,关闭fd1、fd2和fd3中不论什么一个都将释放由父进程设置的写锁。

建议性锁和强制性锁

建议性锁是这样规定的:每一个使用上锁文件的进程都要检查是否有锁存在,当然还得尊重已有的锁。

内核和系统整体上都坚持不使用建议性锁。它们依靠程序猿遵守这个规定。(Linux默认是採用建议性锁)

强制性锁是由内核运行的。当文件被上锁来进行写入操作时,在锁定该文件的进程释放该锁之前,内核会阻止不论什么对该文件的读或写訪问。每次读或写訪问都得检查锁是否存在。

样例:

例1,我有几个进程(不一定有亲缘关系)都通过fctnl机制来操作文件,这个就叫一致的方法。

可是。假设同一时候。又有个流氓进程。管它3721,冲上去。open, write。这时候那几个进程fcntl对这样的方式无能为力,这样就叫不一致。文件最后的状态就不定了。正由于这样的锁约束不了其他的訪问方式。所以叫建议行锁。

强制性锁须要内核支持的,对read, write, open都会检查锁。

例2,所谓建议性锁就是假定人们都会遵守某些规则去干一件事。

比如,人与车看到红灯都会停。而看到绿灯才会继续走。我们能够称红绿等为建议锁。但这仅仅是一种规则而已,你并不防止某些人强闯红灯。而强制性锁是你想闯红灯也闯不了。

STREAMS

I/O多路转接

I/O多路转接:先构造一张有关描写叙述符的列表,然后调用一个函数,知道这些描写叙述符中的一个已准备好进行I/O时,该函数返回。

在返回时。它告诉进程哪些描写叙述符已准备好能够进行I/O。

poll、pselect和select这三个函数使我们可以运行I/O多路转接。

select和pselect函数

#include<sys/select.h>

int select(intnfds, fd_set *readfds, fd_set *writefds,

fd_set *exceptfds, structtimeval *timeout);

select函数使我们能够运行I/O多路转接。

最后一个參数,它指定愿意等待的时间:

structtimeval {

long    tv_sec;         /* seconds */

long    tv_usec;        /* microseconds */

};

由三种情况:

timeout== NULL:永远等待。

假设捕捉到一个信号则中断此无限期等待。当所指定的描写叙述符中的一个已准备好或捕捉到的一个信号则返回。假设捕捉到一个信号。则select返回-1,errno设置为EINTR。

tvptr->tv_sec== 0 && tvptr->tv_usec == 0:全然不等待。測试全部指定的描写叙述符并马上返回。这是得到多个描写叙述符的状态而不堵塞select函数的轮询方法。

tvptr->tv_sec!= 0 || tvptr->tv_usec != 0:等待指定的秒数和微妙数。当指定的描写叙述符之中的一个已准备好,或当指定的时间值已超过时马上返回。

假设在超时时还没有一个描写叙述符准备好,则返回值是0。

与第一种情况一样,这样的等待可被捕捉到的信号中断。

中间的三个參数readfds,writefds和exceptfds是指向描写叙述符集的指针。这三个描写叙述符集说明了我们关心的可读、可写或处于异常条件的各个描写叙述符。每一个描写叙述符集存放在一个fd_set数据类型中。对fd_set数据类型而进行的处理是:分配一个这样的类型的变量;将这样的类型的变量赋予同类型的还有一个变量;或对于这样的类型的变量使用下列四个函数的一个。

#include <sys/types.h>

void FD_CLR(int fd, fd_set *set);     //将一个指定位清除

int  FD_ISSET(int fd, fd_set*set); //測试一指定为是否设置。若fd存在,返回非0

//值;否则返回0。

void FD_SET(int fd, fd_set *set);     //设置一个fd_set变量的指定位

void FD_ZERO(fd_set *set);              //讲一个指定的fd_set变量的全部位设置为0

測试程序例如以下:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/select.h> int main()
{
fd_set rfds;
struct timeval tv;
int retval;
char buf[1024];
for(;;)
{
FD_ZERO(&rfds);
FD_SET(STDIN_FILENO, &rfds);
/* Wait up to five seconds. */
tv.tv_sec = 5;
tv.tv_usec = 0;
retval = select(1, &rfds, NULL, NULL, &tv);
/* Don't rely on the value of tv now! */
if (retval)
{
printf("Data is availablenow.\n");
if(FD_ISSET(STDIN_FILENO, &rfds))
{
read(STDIN_FILENO,buf,1024);
printf("Read buf is:%s\n",buf);
}
}
else
printf("No data within five seconds.\n");
}
exit(0);
}

运行结果

hello

Data is available now.

Read buf is: hello

No data within five seconds.

No data within five seconds.

world

Data is available now.

Read buf is: world

No data within five seconds.

pselect与select功能同样。但其超时值用timespec结构(秒和纳秒)指定。可选择信号屏蔽字。

超时值被声明为const。

#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);

poll函数

poll函数类似于select,可是其程序接口不同。

#include <poll.h>

int poll(struct pollfd *fds, nfds_t nfds,int timeout);

与select函数不同,poll不是为每一个状态构造个描写叙述符集,而是构造一个pollfd结果数组。

结构定义例如以下:

struct pollfd {

int   fd;        /* file descriptor */

short events;     /* requested events */

short revents;    /* returned events */

};

fds数组中元素个数由nfds指定。

timeout == -1:永远等待

timeout == 0:不等待

timeout > 0:等待timeout毫秒

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 */

};

iovec结构第一个元素指向缓冲区起始地址,第二个元素指定长度。iovec数组元素个数由iovcnt指定。

writev以顺序iovec[0],iovec[1], iovec[2]从缓冲区中聚集输出数据,返回总的输出字节数。

readv同理。

存储映射I/O

存储映射I/O使磁盘文件与存储空间中的一个缓冲区相映射。

于是当从缓冲区中取数据。就相当于读文件里的对应字节。与此类似,将数据存入缓冲区。则对应字节就自己主动写入文件。这样就能够在不使用read和write的情况下运行I/O。

为实现这样的功能,应首先告诉内核将一个给定的文件映射到一个存储区域中。这是由mmap函数时实现的。

munmap能够去除映射关系。

#include<sys/mman.h>

void *mmap(void*addr, size_t length, int prot, int flags,

int fd, off_t offset);

int munmap(void*addr, size_t length);

当中addr參数用于指定映射存储区的起始地址。

通常设置为0。表示由系统选择该映射区的起始地址。此函数的返回地址是该映射区的事实上地址。

fd指定要被映射文件的描写叙述符。在映射该文件到一个地址空间之前。先要打开该文件。len是映射的字节数。

off是要映射字节在文件里的起始偏移量。

prot參数说明对映射区的保护要求。能够为PROT_NONE。或者PROT_READ、PROT_WRITE、PROT_EXEC随意组合的按位或。对指定映射存储区的保护要求不能超过文件open模式訪问权限。

flag:可设为

MAP_FIXED:返回值必须等于addr

MAP_SHARED:这一标志说明本进程对映射区所进行的存储操纵的配置。指定存储操作改动映射文件。

MAP_PRIVATE:本标志说明。对映射区的存储操作导致创建该映射文件的一个私有副本。全部后来对该映射区的引用都是引用该副本。而不是原文件。

调用mprotect能够更改一个现存映射存储区的权限。

#include<sys/mman.h>

intmprotect(const void *addr, size_t len, int prot);

假设共享存储映射区中的页已被改动,能够调用msync将该页冲洗到被映射的文件里。

#include<sys/mman.h>

int msync(void*addr, size_t length, int flags);

例如以下程序是使用mmap的样例:

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h> int main(int argc, char *argv[])
{
int fdin, fdout;
void *src, *dst;
struct stat statbuf; if (argc != 3)
printf("usage: %s <fromfile> <tofile>", argv[0]); if ((fdin = open(argv[1], O_RDONLY)) < 0)
printf("can't open %s for reading", argv[1]); if ((fdout = open(argv[2], O_RDWR | O_CREAT | O_TRUNC,
S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) < 0)
printf("can't creat %s for writing", argv[2]);
if (fstat(fdin,&statbuf) < 0) /* need size ofinput file */
printf("fstat error"); /*set size of output file */
if (lseek(fdout, statbuf.st_size - 1, SEEK_SET) == -1)
printf("lseek error");
if (write(fdout, "", 1) != 1)
printf("write error"); if ((src = mmap(0, statbuf.st_size, PROT_READ, MAP_SHARED,
fdin, 0)) == MAP_FAILED)
printf("mmap error for input"); if ((dst = mmap(0, statbuf.st_size, PROT_READ | PROT_WRITE,
MAP_SHARED, fdout, 0)) == MAP_FAILED)
printf("mmap error for output"); memcpy(dst, src, statbuf.st_size); /* does the file copy */
exit(0);
}

该程订购完整的类似cp特征。

版权声明:本文博主原创文章,博客,未经同意不得转载。

Linux/UNIX先进I/O的更多相关文章

  1. Linux/Unix 线程同步技术之互斥量(1)

    众所周知,互斥量(mutex)是同步线程对共享资源访问的技术,用来防止下面这种情况:线程A试图访问某个共享资源时,线程B正在对其进行修改,从而造成资源状态不一致.与之相关的一个术语临界区(critic ...

  2. 学习linux/unix编程方法的建议(转)

    假设你是计算机科班出身,计算机系的基本课程如数据结构.操作系统.体系结构.编译原理.计算机网络你全修过 我想大概可以分为4个阶段,水平从低到高从安装使用=>linux常用命令=>linux ...

  3. Linux/Unix双机建立信任教程

    Linux/Unix双机建立信任教程 一 需要建立信任关系的2台主机都执行生成密钥输入ssh-keygen -t rsa之后全部默认回车,这样就会在/root/.ssh下生成密钥文件 [root@pl ...

  4. 今天在Mac机器上使用了Flex Builder编辑了一个源代码文件,保存后使用vim命令去打开时发现系统自动在每一行的结尾添加了^M符号,其实^M在Linux/Unix中是非常常见的,也就是我们在Win中见过的/r回车符号。由于编辑软件的编码问题,某些IDE的编辑器在编辑完文件之后会自动加上这个^M符号。看起来对我们的源代码没有任何影响,其实并不然,当我们把源代码文件Check In到svn之类

    今天在Mac机器上使用了Flex Builder编辑了一个源代码文件,保存后使用vim命令去打开时发现系统自动在每一行的结尾添加了^M符号,其实^M在Linux/Unix中是非常常见的,也就是我们在W ...

  5. Linux/Unix 怎样找出并删除某一时间点的文件(转)

    在Linux/Unix系统中,我们的应用每天会产生日志文件,每天也会备份应用程序和数据库,日志文件和备份文件长时间积累会占用大量的存储空间,而有些日志和备份文件是不需要长时间保留的,一般保留7天内的文 ...

  6. Linux Unix 环境变量设置实例

    背景 从第一次写Hello World我们便开始接触环境变量.这最基础的系统设置是必须要掌握的,尤其在是Linux/Unix系统中.比如,哪天某个Java进程出现问题,我们想分析一下其线程堆栈,却发现 ...

  7. Linux / UNIX create soft link with ln command

    How to: Linux / UNIX create soft link with ln command by NIXCRAFT on SEPTEMBER 25, 2007 · 42 COMMENT ...

  8. 【转载】在LoadRunner向远程Linux/Unix执行命令行并收集性能数据

    前面介绍过在LoadRunner的Java协议实现“使用SSH连接Linux”,当然连接之后的故事由你主导. 今天要讲的,是一个非Java版本.是对“在LoadRunner中执行命令行程序之:pope ...

  9. LINUX&UNIX 安装vmware workstation10和centOS6

    大一下时,学习了linux&unix这门课程,全字符的操作,我对它并不是很感冒,不过,还是找学长安装过虚拟机和Linux系统,在考前利用它和putty进行复习.现在重装系统之后,各类软件,自然 ...

随机推荐

  1. python可变交换性能优化

    离许多新的压力python性能优化见交换两个变量值可以使用 a,b = b,a 这样能够提高性能 >>> from timeit import Timer >>> ...

  2. Linux下常用操作汇总

    查看linux操作系统位数 (1) 终端输入: file /sbin/init 如 显示: /sbin/init: ELF 32-bit LSB executable, Intel 80386, ve ...

  3. LinearLayout具体解释一:LinearLayout的简单介绍

    LinearLayout,中文意思是线性布局.假设你是初学android的,肯定会非常困惑"啥叫布局",啥又叫"线性布局"呢. 有的时候,我尝试用官方的语言去解 ...

  4. 【Jqurey EasyUI+Asp.net】---DataGrid增加、删、更改、搜

    在前面写了两,但不知道如何完成,对比刚刚开始学这个,他们摸着石头过河,一步步.在最后两天DataGridCRUD融合在一起.因此份额.我希望像我这样谁是刚刚开始学习一些帮助. 直接主题酒吧. 它是说数 ...

  5. 机械革命X5(MECHREVO MR-X5)开包检验

    不废话.直接的问题,左右X5没有更具体的信息.为了通过有机会了解后续的选择,具体的数据被释放约: 首先看包装: 1.快递包裹,基于卖方这可以是不同的,包装各不相同 watermark/2/text/a ...

  6. 从原理角度解析Android (Java) http 文件上传

    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/23781773 文件上传是我们项目中经常使用的功能,一般我们的服务器可能都是web ...

  7. Hdu-1565 电网接入(1) (国家压缩dp获得冠军

    正方形格通路(1) Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total ...

  8. Redis时延问题

    单线程你别阻塞,Redis时延问题分析及应对 内容目录: 耗时长的命令造成阻塞 fork产生的阻塞 持久化造成的阻塞 单线程你别阻塞,Redis时延问题分析及应对 Redis的事件循环在一个线程中处理 ...

  9. Android-管理Activity生命周期 -重新创建Activity

    按照正常的app行为,很少情况下activity会销毁,只有当用户点击了返回按钮或者activity通过调用finish()发出销毁信号.系统也有可能销毁activity如果它是停止状态并且很久没有使 ...

  10. 2014年10本月微软MVP应用程序启动!

     2014年10本月微软MVP启动应用程序!    CSDN与微软合作,长期为用户提供申请"微软最有价值专家"的平台,希望有兴趣.资历的朋友以及正在朝这个方向努力的朋友可以积极參与 ...