zoukankan      html  css  js  c++  java
  • Linux编程---I/O部分

    非常多函数都能够在网上找到,也比較基础,所以原型仅仅给出了函数名.详细用到再man吧.

    输入输出是个非常重要的一块内容.差点儿网络相关的东西基本都是靠底层IO调用来实现的.

    好吧.还是先踏踏实实的介绍一下C标准库中的IO函数吧.个别函数我也是第一次见.对于不太常见的我就多解释一下,反正通常这些函数百度一下就清楚了,我就不多解释了~

    1.C标准库IO函数

    1.1流的关闭开启与重定向

    fopen:打开一个流

    fclose:关闭一个流

    freopen:又一次打开一个流

    1.2 读与写

    :

    fgetc   getc  getchar  fgets  gets  getline  getdelim  fread

    :

    fputc  putc  putchar  fputs  puts  ungetc(这个叫读回退..写一个字符到输出缓冲流里)  fwrite

    1.3 文件定位

    ftell :告诉文件指针的位置

    fseek: 指定文件指针的位置

    rewind: 将文件指针指向文件开头

    fgetpos: 返回当前的文件指针的位置,并通过參数传回

    fsetpos: 设置文件指针的位置

    后面两个应该非常少用吧..相对来说,一般都是通过相对位置来找,而不是绝对位置来找.

    1.4 文件结束和错误指示器

    ferror: 当且仅当流的错误指示器被设置,返回0

    feof: 当到达文件结尾,结束条件指示器被设置,返回0

    clearerr: 用于清除上面两个函数的指示器

    这里的指示器是每一个流对象内部的标志变量.没怎么用过这两个函数..不太清楚为什么要有clearerr这个函数..难道是为了忽略错误继续运行?

    1.5 流缓冲

    setbuf: 用于设置打开或关闭流的缓冲.这里的缓冲都是调用者提供的内存.

    setvbuf: 这个除了指定哪里为缓冲外,还能够指定缓冲类型(全缓冲,航缓冲,无缓冲).

    我想普通情况下用setbuf即可了吧~

    说道流,就不得不提刷新了.特别是多线程的情况下.刷新是必备的~

    fflush: 刷新流的缓冲区.

    事实上我也是才知道,在行缓冲下,换行符能够取代fflush~

    1.6 格式IO

    事实上这个全然能够放到上面的读写其中去.可是书上是这么写的,所以我也这么总结吧.

    输出:  格式为%[posp$][flags][width][.prec][size]fmt

    printf  fprintf  sprintf

    输入:  格式%[posp$][*][width][size]fmt

    scanf  fscanf  sscanf

    格式什么的对于C比較熟的我就不说明了.基本就是那些,假设记不起来的百度看看~

    1.7 暂时文件

    tmpnam: 返回一个合法的暂时文件名称,这个文件一定在/tmp下建立.

    tempnam: 功能比上面多一点,能够指定文件存放文件夹

    tempfile: 能够避免竞争的创建暂时文件(多线程同一时候使用tmpnam).进程结束自己主动关闭~

    我还专门查了一下,发现标准C中是真有这个函数的.这对于执行中的进程来说非常方便啊~

    2. 低级IO

    2.1 创建,关闭和打开文件

    create  open  close

    字面意思非常明显了.所以使用方法就不多说了.这里关键是要了解打开文件后其在内核中管理的形式是怎样的.

    1) 这几个函数返回的都是一个int类型的值,叫做文件描写叙述字.这个值是按最小未分配的文件描写叙述字来优先分配的.所以你一開始打开了1号文件描写叙述符,那么关闭之后,再打开其它文件,那么文件描写叙述符就是1.

    2) 因为系统为了保证读写一致性的问题,会在内核中统一设立一个打开的文件表.而且每一个流都有一个指向其表项的指针.然后每一个系统打开文件表中,又有一个指针指向一个vnode的文件信息结点.

    文件表项会记录文件的状态与文件指针位置信息,vnode则仅仅记录文件的基本信息(包括内容,文件大小和详细的磁盘上相应的记录位置,即包括inode上的信息).

    尽管两个指针麻烦多了,只是这样分开之后,对于文件系统的要求就减少了.能够直接通过vnode这样的统一的格式来操作文件呢.而且这个vnode是以文件打开才建立,而是针对每一个文件,意思就是不同进程打开同一文件,必然使用同样的vnode.

    3) 对于fork出来的子进程,全部的东西都会继承.所以也包括这个文件描写叙述符和文件表项~

    因为仅仅复制进程中的资源,所以复制的是文件表项的指针~

    2.2 读与写

    read  write

    linux的函数写的真是太标准了....想让人望文生义都难啊...

    这个我也就不多说了,详细的百度吧``

    顺带附上lseek这个设置位置的函数.事实上跟fseek差点儿相同,所以也没什么好说的.

    只是要注意这个函数返回off_t,fseek返回int.

    而且都能够把文件指针指向文件最大值之外,造成空洞.

    2.3 重定向

    dup: 复制描写叙述字到一个新的描写叙述字.即添加一个文件描写叙述符描写叙述同一个文件表项

    dup2 把一个文件描写叙述字所指的文件表项换成还有一个文件描写叙述字所指文件表项.即重定向

    这个函数阐释了重定向的根本.仅仅是改动文件描写叙述符的指针所指向的文件表项.

    2.4 文件描写叙述符与流的转换

    fdopen: 给它一个文件描写叙述字,还你一个文件流!

    fileno: 给它一个流,它就给你一个文件描写叙述字

    2.5 文件控制函数

    fcntl: 对已打开的描写叙述字进行操作.

    事实上就是改动文件表项中的信息.只是应该不止如此,书上还写有文件锁.所以我觉得应该部分vnode内容也能够被改动.

    详细使用方法也非常多,主要有以下三条:

    1) 反复文件描写叙述字

    2) 文件描写叙述字标签,也就是包括描写叙述字相应指针的那个结构体(这纯粹是我自己的理解,不正确还请指正).

    3) 文件状态标签.这个就多了.什么读写啊,追加啊,都能够通过这个函数更改和查看.

    我自己也没怎么用过这个函数,毕竟不写库,文件的状态基本都是清楚的,不是必需再改或者查询.

    2.6 分散读和收集写

    readv: 用于将自己文件里的连续信息,发到多个地址中去.

    writev: 把多个地址的信息,写到自己的文件中面来.

    这两个函数预计也就仅仅有搞server开发的会用到了吧..

    貌似UDP广播能够用这个实现.

    2.7 清理内核IO缓冲区

    fsync: 将文件改动的内容从内核缓冲区写到磁盘中去

    fdatasync: 比上面功能好一点,仅仅把改动过的数据写到磁盘中.所以一般比fsync.

    这里理一下.内核缓冲区也是分IO.对于O而言,用的是上面提到的输出缓冲.

    关于I的部分.我个人觉得非常可能没有占用内核空间,而是直接用的read參数的地址.

    当然,这还是得看了源代码才知道是怎么回事.

    3.文件相关

    这一部分事实上和函数调用相关的比較少,主要是一些概念性的东西.

    3.1 文件

    前面提到过vnode.只是在实际磁盘上用于保存信息的是inode.因为linux有虚拟文件系统VFS,统一了以下的文件系统接口.所以实际执行过程中就创建的vnode.inode就是实际的ext文件系统中的内容了.

    这个inode,包括了文件的属性信息.详细有文件的类型,所属的角色关系,訪问权限,时间戳.

    假设想要查看相关信息能够使用stat,fstatlstat.

    这三个函数的区别在于fstat直接用文件描写叙述符訪问.statlstat用路径来訪问.而且对于符号链接来说,stat返回的是符号链接所指的文件,lstat返回的是符号链接本身的信息.

    3.2 文件类型

    1) 普通文件.绝大部分文件都是这个.一般的存储字符的文件和二进制文件都是这个类型.訪问方式靠的訪问它的程序约定.

    2) 文件夹.就是windows下的文件夹.可是对于用户不能写文件夹,仅仅有内核能够(预计就是仅仅能用系统调用写吧).

    3) 链接文件.linux下有两种链接方式.

    一种是硬链接,即仅仅有链接数为0才删去的链接.可是有局限性,一是是不能跨越文件系统.二是仅仅有root才干创建文件夹的硬链接.

    能够用link函数来创建硬链接

    还有一种,符号链接.这个和windows的快捷方式非常像.源文件删了就会提示找不到快捷方式.这样的链接仅仅保存一个源文件的路径名.所以连文件名称都不须要了..

    4) 特殊文件 

    这个也称为设备文件.设备文件不含数据,它们的作用是将物理设备映射到文件系统中的文件名称.能够用mknod函数来创建,而且与内核的一个软件相连,这个软件就是设备驱动程序.

    这样的特殊文件也分为两种:

    --块文件 ls -l中的b文件

    块文件主要是按固定大小且可随机訪问的块来存储和运行I/O,常见的有硬盘什么的.

    仅仅有块设备才干包括文件系统~

    --字符文件  ls -l中的c文件

    能够存储和传送随意大小的数据.这个主要是串行设备,如显示器,鼠标和键盘之类的.

    这本书上写的还蛮清楚的.我记得好像是鸟哥的还是什么书,这里写的愣是没看明确区别.

    5) socketpipe文件

    这两种文件到时候写管道还有网络相关的东西再写吧.毕竟这不是一两句能搞定的~

    3.3 文件的所属角色关系和用户组

    主要分为--实际--有效--设置 这三种用户和租.我简单的说明一下,这三个权限是针对正在执行的进程而言的.

    实际RUID ---就是正在使用该文件的进程.

    有效EUID---就是依据这个ID推断它有什么属性,然后给与这个ID对这个文件的权限

    设置SUID---这个主要是让权用的.即在运行这个文件的时候,运行者拥有和文件全部者同样的权力.即仅仅要有可运行的权限,就能够拥有文件全部者的对文件的权限.

    这里RUIDEUID中的ID包括组ID和用户ID.

    有三个函数能够来让进程使用:chown,fchown,lchown.

    当中对于文件的全部者关系的改变,仅仅能是通过root来改变.详细的也是非常多宏參数,我也就不细说了.

    3.4 文件权限

    每一个文件都有三个关系拥有者,所属组,其它人

    还有另外三种权限,,运行

    关系非常明确了,权限我要说一下.

    对于文件夹而言权限有不同的解读.

    读权限:代表能够读文件夹内的信息,即文件夹中有哪些文件(注意,文件名称的信息是在文件夹文件里记录,而不是一个文件一个文件翻出来的).

    写权限:代表能够在文件夹内创建文件,重命名,删除文件夹内文件.

    运行权限:这个是个特殊的概念,事实上应该说是叫”检索权限”.仅仅有有了这个权限,才干显式的去訪问文件夹中的子文件夹或文件.假设没有读权限,仅仅有写权限的话,你就仅仅能靠全然路径名来訪问文件了.

    这里额外有一个sticky权限

    这个主要是把用过的文件保留一个副本在swap区中.能够加速文件的打开工作.假设是程序,就加快程序的开启工作.同一时候在现代linux系统中,这个还起到了保护文件的作用.

    保护作用有点复杂,主要是:

    假如我所在的文件夹有写权限,那么我全然能够创建一个文件去覆盖一个仅仅能读不能写的文件.

    如果我有一个read文件对全部角色仅仅有r权限,而且所在文件夹有写权限.那么依照以下来做:

    cat read>temp

    vi temp ;这里随便改改

    mv temp read  ;选择y

    cat read

    你就会发现read的内容已经被修改了.

    假设一个文件夹在上了stickly权限之后,那么这个文件夹下的文件仅仅有文件全部者,文件夹全部者和root才干删除移动或重命名文件了.

    3.5 确定和改变文件方式

    1) 屏蔽字 

    umask函数.这个预计写过shell脚本的都知道把..假设不改屏蔽字,那么创建的时候就是就会依照屏蔽字来屏蔽对应的权限位.

    2) 改变权限

    主要有两个函数:chmodefchmod

    仅仅有当有效用户ID等于文件用户ID时才干改,否则仅仅有是root权限才干改

    3) 判别进程是否原本就拥有这个文件的运行权限

    access函数.能够依据要求推断当前进程是否具有这个文件的某种权限.

    尽管stat也能够,可是access方便一些.參数用的路径.

    3.6 文件大小

    文件大小通常是16512的倍数(硬盘一个块是512字节,16我就不清楚了).

    而且对于存储了大量空洞内容(0)的文件来说,linux会降低其占用的空间.

    假设文件里有非常多个0.那么就会在当中插入一个计数,来指定插入了多少个0.

    关于截断文件

    ftruncatetruncate两个函数.ftruncate是针对以写方式打开的文件来说的,參数是文件描写叙述符.truncate參数则是路径,但相同进程要对截断文件有写属性.

    3.7 文件时间

    主要有三个指标:

    st_atime:近期一次读或运行的时间

    st_mtime:近期一次写文件的时间

    st_ctime:近期一次改变inode的时间

    这三个变量在stat结构体中定义了.可能有人觉得mtimectime要一个就够了吧?但事实上非常多时候写文件是有写缓冲的.因为系统维护inode,所以inode的改变仅仅用最后一次要写入磁盘的时候再改即可了.

    怎样更改文件的訪问时间和改动时间?

    utimeutimes函数

    utime使用utimebuf结构体,这个里面仅仅有atimemodtime.ctime仅仅能被系统来改动,所以有这两个成员就够了.当中utime是以秒来计数的,utimes则是以微秒来计数.

    utimes貌似资料非常少,预计高实时系统,用微秒来计数还不如直接上汇编自己写定时程序吧.

    我记得好像也在哪看过.linux的最小时间片是毫秒级的,这可能也是utimes用得少的原因.

    3.8 文件的删除与改名

    1) 删除函数unlink

    unlink看上去不像是删除文件的函数.可是linux存在硬链接这一说,所以unlink也就非常好理解了.当一个进程正在訪问被unlink的文件时,假设调用了unlink这个文件,那么直到进程关闭这个文件,才会把文件删除.原来close系统调用会来检查这个文件链接数!

    这里有一点就是必须有能够改动这个文件的权限才行.

    还有就是对于普通用户,不能用这个来删除文件夹,仅仅能用以下的函数

    2) 删除文件夹函数rmdir

    这个仅仅能删除空文件夹

    3) 真正的删除函数remove

    就是unlinkrmdir的结合体

    4) 改名函数rename

    --必须有该文件夹的的写权限

    --假设是文件,那么会删除全部硬链接  硬链接多的文件还是不要改名了..

    --假设是文件夹,新名字能够是个已存在的空文件夹名.

    --假设是符号链接,假设oldname是符号链接的路径名,那么符号链接被改名(符号链接本来的名字就是符号链接的路径名).假设newname是一个符号链接的路径名,那么原来的符号链接被删除.

    3.9 工作文件夹

    getwdgetcwd用于得到工作路径.第二个函数多一个參数来指明存放路径名数组的大小.要相对第一个安全一些.

    chdirfchdir用于更改当前进程工作文件夹.第二个通过文件描写叙述符来指明文件.

    发现了没有?差点儿全部带f的文件操作就相当于告诉你參数是用的文件描写叙述符.

    1) 创建文件夹函数mkdi

    这个和shell指令差点儿相同.也就不多说了.

    2) 关闭与打开文件夹opendir--closedir

    这里用的不是FILE结构,而是DIR结构.注意这里DIR的概念类似C中的流,而不是一个文件描写叙述符.预计linux对文件夹文件特殊化处理了吧.

    3) 文件夹项读取函数readdir

    这里文件夹项是DIR中的结构.每一个文件夹项用struct dirent这个结构体来表示.每次调用这个都返回一个文件夹项,然后DIR流指向下一个文件夹项.

    这里的文件夹项就是我之前解释过的,文件夹中的文件名称都存在文件夹项中.

    4) 对DIR流的几项操作

    类似FILE这个也有定位的函数.

    rewinddir : 重定位DIR流到第一项

    telldir : 返回当前位置

    seekdir : 依据參数设置DIR流的值

    4. 高级IO

    4.1 文件锁

    主要分为两种锁:

    读锁:主要是为了防止在读的时候文件被别的进程改写

    写锁:主要是为了保证写操作是原子操作.

    注意,仅仅有普通文件才须要以下这样的锁,网络或中断文件不建议用这样的锁.

    函数用的就是fcntl.仅仅是传给第二个參数的值为F_GETLK,F_SETLK,F_SETLKW.

    1)F_GETLK 得到锁信息,文件的某个区域是否存在有碍于创建新锁的其它锁

    2)F_SETLK 同一时候在第三个參数中加上F_RDLCKF_WRLCK.同一时候也能够用F_UNLCK清除.

    3)F_SETLKW 和上面一样,仅仅是它会一直堵塞进程知道完毕设置或清除锁

    文件锁也比較特殊,它锁的是文本的一部分.能够自己依据第三个參数来设置其锁的部分起点和长度

    用锁的时候也多用readwrite,由于C标准库中的函数会自带缓冲,读入和写出的数据大小会与你自己写的不一样.

    并且这个锁也是个建议锁,readwrite中并不强制性检查.所以仅仅能在多个配合的进程同一时候用fcntl来检查.至于强制锁,首先要让文件系统用mount -o suid命令安装,并且文件本身设置了设置组ID且关闭了组的运行权限位的普通文件...

    另一些锁的相关信息,可能编程的时候会遇到:
    1) 锁与进程是相连的.文件上的每个记录所都关联一个进程ID.即锁不会被子进程所继承.

    2) 锁仅仅与文件相连而不与文件描写叙述符相连.意思就是你用dup重定向描写叙述符之后,锁会继承过去.个人理解锁在vnode,所以改变文件描写叙述符并不改变其锁的设置.

    4.2 信号驱动IO

    其本质上还是同步IO,毕竟是用的信号中断.只是节省了时间去等待输入的堵塞循环.

    Linux下有两个信号是用来驱动IO:SIGIOSIGURG.但后者仅仅用于通知进程在网络链接上到达了带外数据.

    假设在文件描写叙述字上设置了O_ASYNC标志,那么文件描写叙述字相应文件上有输入或者输出的时候,就会向进程发送一个信号.假设文件描写叙述字相应一个终端设备,那么SIGIO则会发送给前台进程组,其它情况应当调用fcntl(fileds,F_SETDOWN,pid)来设置接受信号的进程或进程组.

    详细使用信号驱动IO的步骤:

    1) 调用sigaction()建立信号句柄

    2) 调用fcntlF_SETDOWN来设置接受新号的进程或进程组

    3) 假设要接受的新号是SIGIO,则必须调用fcntlF_SETFL命令设置文件描写叙述字的O_ASYNC标志使其可以生成SIGIO信号

    4.3 多路转接IO

    參考的书中尽管介绍了select/poll,可是没有介绍epoll真是太遗憾了.epoll就我自己搜集网上资料写一写吧.

    4.3.1 select函数

    这个函数原理就是轮询,所以效率比較差,用的也比較少.

    这个函数最后一个參数是时间,假设当中的内容所有为0,那么就是不等待.假设设置了NULL则是永远等待.其它情况都是按值来设置.尽管这个时间能够设置成微秒,可是如之前所说,Linux的最小时间片是以毫秒为单位的,书上说是10ms.

    中间三个參数rfds,wfds,efds分别代表读,,例外条件.

    也就是说,rfds中存放须要读的描写叙述字.wfds存放须要写的描写叙述字,efds用于检查外带数据.

    rfds相应0~31号描写叙述字,wfds相应32~63号描写叙述字,efds相应64~95号描写叙述字了(这就是为什么要晓得描写叙述字的分配规则的原因吧)

    select的中间三个參数都为NULL,事实上这个就相当于是sleep...

    这三个參数也须要使用对应的宏来设置.

    FD_ZERO //清空为0

    FD_CLR (fileds,fdset) //出去fileds描写叙述字

    FD_ISSET(fileds,fdset) //推断是否fileds在当中

    FD_SET(fileds,fdset) //增加fileds描写叙述字

    select的返回值也有点特点:

    >0 : 表示已经有文件就绪,准备读或写或额外数据了.给出就绪的描写叙述字

    =0 : 表示时间到了,仍然没有就绪..注意,这样的情况下描写叙述字集合会被清空!

    -1 : 表示出现错误.比如当被信号中断.这时候并不改变描写叙述字集合.

    4.3.2 poll函数

    这个函数本质上和上面同样,都是将一堆文件描写叙述符统一来管理.知识參数不太一样.

    这个函数的时间选项比較有特点.小于0,堵塞等待;等于0马上返回;大于0,其值为等待的毫秒数.基本已经被epoll代替了,所以不多说了.

    4.3.3 epoll函数

    早就听过这个函数效率高,可是一直没怎么接触过.这次写总结干脆就学一学吧.

    这个函数有两种用法:

    LT : 和前面的两种方法相似,IO就绪就去运行.而且内核会一直保持有IO就绪的这个状态.

    ET : LT的区别就是不维持状态,仅仅发送一次信号给进程.至于进程有没有处理,内核并无论.

    使用起来也就三步:

    1) 调用epoll_create.创建一个epoll的描写叙述字.用于兴许操作

    2) 使用epoll_ctl,相当于select的集合操作.仅仅是仅仅是个专门的函数,而不是多个宏组成的.

    描写叙述參数依次为efds,op,fd,event.

    efds就是creat返回的文件描写叙述字.相当于一个集合.

    op就是要对上面这个集合做得事情.

    fd就是新增加这个集合的文件描写叙述字.

    event就是对这个新加的fd採取什么样的动作.

    详细的描写叙述还是用到的时候查手冊吧.

    上面所说的LT和ET在event中设置.

    这里事实上另一个data联合体包括在event结构体中.event中的事件描写叙述应该和data中相应的类型有相应关系吧.以后看nginx的时候再看看吧.

    3) 使用epoll_wait来等待事件的发生

    4) 调用close关闭第一步生成的文件描写叙述字.

     

     

    看网上的解释,这个函数真是各种厉害啊..

    mmap不用说,直接映射,用过的都知道效率.

    ET触发.网上比喻是沿触发.这个效率也是非常nice的.

    socket描写叙述符数目是进程打开上限..这个比select不到100个真是屌炸天啊..

    回调函数的伪AIO(以下讲的),仅仅有活跃socket操作.这不知道要节省多少查询时间...

    有时间还是看看底层实现吧.

     

    4.4 异步IO

    4.4.1 异步IO相关函数介绍

    简单来说就是IO的时候进程并不堵塞.通过aio_read和aio_wirte向系统提交申请,然后由系统来排队,并由内核生成线程来执行异步IO控制块.完毕后发送信号到进程,通知其IO完毕.以下是glibc中的异步IO的相关函数:

    aio_read :这个非常清楚了,就是读

    aio_write:也非常清楚...就是写

    aio_error :用来检查IO是否成功完毕.

    aio_return : 获得IO的状态

    aio_suspend :挂起进程,直到有一个请求已完毕

    aio_cancel :删除悬挂在文件描写叙述字上的一个或多个请求

    lio_listio :启动一组IO请求,和readv还有writev非常像,能够降低进程内核间的切换次数       

    aio_fsync :异步地将系统缓冲区中含有的文件已改动数据永久写到磁盘上.即强制同步

     

    这里调用read和write要注意的是,异步io结束后,文件的当前位置是不确定的.必须用其IO结束返回值来更新文件的指针位置.

    对于lio_listio来说,其请求数组中的顺序不一定是运行顺序,详细的改动优先级比較重要吧.每一个IO操作完毕后都会给进程发一个信号.当第一个參数选择LIO_NOWAIT时,所有的IO操作完毕之后也会再发一个所有完毕的信号.这里因为数组參数中结构体包括一个信号的结构体,所以是能够指定每一个IO操作产生的信号的.

    异步IO是在内核中进行的,所以无法使用之前同步IO中得到IO信息的方法.因此,每一个异步IO都保持一个返回值和errno.并通过aio_error和aio_return来得到相关信息.我想主要用在信号在设置时候指定的函数中吧.对于aio_return来说,仅仅要一运行,那么就会将其相关资源返回给系统.对于一个未完毕的IO操作,说不定还会产生数据丢失.所以一般顺序是调用aio_error,然后确定状态后再调用aio_return.

    aio_suspend则有点像lio_listio,可是它是堵塞完毕的.仅仅有当list中的IO请求至少有一个完毕时,才会停止堵塞.或者出现一个信号时,或者时间參数设置的时间到了就停止堵塞了.

    aio_cancel则是撤销一个异步IO请求.通过文件描写叙述字来指定要删除的异步IO.当中第二个參数结构体中的fd信息必须与第一个參数同样.而且这个函数并不一定就能撤销其IO操作.还要看系统是否支持,或其IO操作进行的状态也不同意撤销.

    aio_fsync则是与磁盘同步的函数,仅仅要是带缓冲的情况,都会带有一个fsync同步的相关函数吧(只是标准C中貌似用fflush来同步的..).这个函数并不等待其同步的完毕,仅仅是发出一个请求来告诉内核须要写到磁盘上,但并不强制等待.这也就是异步IO的优点吧.在内核收到这个请求之后,会派生一个线程来将其请求立马写的IO从缓冲区中取出,而且逐个写入磁盘.假设此时在调用一个aio_fsync的话,并不会再发一个请求,由于内核已经在開始运行了.

     

    4.4.2 异步IO的一般使用步骤:

    1) 调用open打开指定的文件,并获得文件描写叙述字,并设置其文件指针所指向的内容的地址.

    2) 创建并填充异步IO控制块aiocb

    3) 假设使用信号,还要设置信号的句柄(即运行的函数指针)

    4) 调用aio_相关的一些异步IO请求,或使用aio_sync来使其同步.

    5) 假设应用须要等待IO的完毕,那么能够用aio_suspend,或者一直使用aio_error来查询.

    6) 完毕异步IO之后调用aio_return函数

     

    4.4.3 异步IO使用注意事项

    1) 不要再相应的IO操作还未完毕之前反复使用同一个AIO控制块.

    linux中aiocb不仅存放了应用使用的成员,还包括了操作系统内部使用的成员,以及存放操作错误编码的成员和存放返回值的成员.

    2) 必须自己管理异步IO文件的位置指针.

    上面说道过,就不多说了.记得在在信号句柄中加return和lseek~

    3) 不要对同一个文件描写叙述字混合使用同步和异步IO

    这样非常危急,由于异步IO和进程是并行的.这样非常可能导致文件指针的位置混乱.而且写的数据错位.当然你想用也能够,可是一定要清楚文件指针位置在哪里,而且别弄成并发的即可.

     

    5. 存储映射IO

    在之前的方法中,对于不同进程读同一个文件一直是对每一个进程保留一个副本的(书上虽说vnode每一个进程共享,而且保存inode信息).所以对于内存空间的浪费是比較大的.特别是网络应用,动不动成千上万个socket..所以mmap就随之诞生了.

    5.1 mmap函数

    使用这个函数的优点在于能够共享,而且映射之后,对于mmap的映射区域改动的操作也非常迅速,毕竟直接操纵内存.这个函数实现的机制也和硬件接触比較多,比較底层,效率也就比較高了.再就是mmap每次映射都是以页为单位的.

    这个函数还提供了一种私有的形式,这样的形式比較特殊.往私有副本里面写东西并不会改变磁盘上的信息.仅仅是在内存里面改着玩...

    另一个优点就是,所有进程共享同一片区域,那么更改是马上可见的.仅仅要是一个进程一改,另外的进程就能够直接见到改动.既危急又高效.

     

    仅仅是要注意一点就是:mmap不会改变源文件的大小!

    我曾经编过一个程序就是准备靠mmap来写一个文件,还多个程序合作写...调试半天...最后查mmap的相关说明才发现原来不能扩充文件.....

    另一点就是mmap传的是文件描写叙述字,所以你以什么方式打开的文件,才干针对mmap的页设置其子集的属性.

     

    5.2 munmap和msync函数

    munmap:这个函数就是用来解除映射的.但并不会指定让其写回到存储器.

    msync:这个函数就是强制写回了.而且还能够配置写回的方式,异步或者同步.而且这个仅仅能针对原mmap的区域是共享性的才干运行.

     

    IO部分就写到这里了.虽说异步IO的底层函数没讲,但我认为够用了.以后用到再补进来吧.

  • 相关阅读:
    EChart处理三维数据做图表、多维legend图例处理
    详解Vuex常见问题、深入理解Vuex
    解决vuex在页面刷新后数据丢失的问题
    vue-router路由元信息详解
    修改Nginx与Apache配置参数解决http状态码:413上传文件大小限制问题
    ab.exe使用
    项目经验
    消息队列系列(一):.Net平台下的消息队列介绍
    【转】谈基于SOA的应用系统设计和开发
    2015年12周(2015-03-16~2015-03-22)
  • 原文地址:https://www.cnblogs.com/mengfanrong/p/3857383.html
Copyright © 2011-2022 走看看