<<UNIX环境高级编程>>之第三章理解

来源:互联网 发布:阿里云nodejs环境部署 编辑:程序博客网 时间:2024/05/16 09:01

第三章 文件I/O
1.引言:大多数unix文件I/O只需用到5个函数:open,read,write,lseek以及close.然后说明不同”缓存器长度”对read和write函数的影响:
2.unbufferedI/O:不带缓存指的是每个read和write都调用内核中的一个系统调用.这些不带缓存的I/O函数不是ANSI C的组成部分,但是是POSIX.1和XPG3的组成部分.
3.原子操作:涉及在多个进程间共享资源,原子操作的概念非常重要.在”操作系统”中的原子操作,就是不能被更高等级”中断抢夺优先”的操作。
4.文件描述符:第一章提过.补充:unix shell 使文件描述符0与进程标准输入结合,文件描述符1与标准输出结合,文件描述符2与标准出错结合.文件描述符的范围是0~OPEN_MAX.
5.open函数:调用open函数可以打开或创建一个文件.

#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>int open(const char* pathname, int oflag,.../*,mode_t mode */);(尚未理解这种定义方法)

返回:成功返回文件描述符,出错返回-1.
理解:第三个参数写为”…”,没见过,书上说法是说明余下参数和类型可以变化的方法.open可以实现打开和创建功能,仅当创建新文件才使用第三个参数.pathname是打开或创建文件的名字.oflag参数定义在

#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>int creat(const char* pathname, mode_t mode );等价 open(pathname,0|1|2(读写方式),mode);

mode:表示文件存取许可权,以后会说明.
以此可看出,creat函数无法指定读写方式,其默认为以只写方式打开所创建文件.
7.close函数:关闭一个打开的文件,并释放该进程加在该文件上的所有记录锁(关于锁,12章3节会提到).

#include <unistd.h>int close(int filedes);

8.lseek函数:每个打开文件都有一个与其关联的”当前文件位移量”(非负整数,用以度量从文件开始处计算的字节数).读写操作都从当前文件位移量处开始,并使位移量增加所读或写的字节数.通常,当打开一个文件时,除非制定O_APPEND选择项,否则该位移量被设置为0.
lseek显示定位一个打开文件.

#include <sys/types.h>#include <unistd.h>off_t lseek(int filedes,off_t offset,int whence);
对参数offset的解释与参数whence的值有关.1.whence是SEEK_SET,则将该文件的位移量设置为距文件开始处offset个字节.2.whence是SEEK_CUR,则将该文件的位移量设置为其当前值加offset,OFFSET可正可负.3.whence是SEEK_END,则将该文件的位移量设置为文件长度加offset,offset可正可负.若lseek成功执行,则返回新的文件位移量.off_t currpos;currpos = lseek(fd,0,SEEK_CUR);例:

include

include “apue.h”

int main(void)
{
if(lseek(STDIN_FILENO,0,SEEK_CUR) == -1)
printf(“cannot seek\n”);
else
printf(“seek OK\n”);
exit(0);
}

./3-1 < apue.h
seek OK

首先说下重定向问题:第一章提过:>或<,$ cmd >file 把cmd命令的输出重定向到文件file中.如果file已经存在,则清空原有文件.¥cmd < file 把cmd命令从FILE读入;这就很好理解以上命令了.lseek仅将当前的文件位移量记录在内核内它并不引起任何的I/O操作.然后,该位移量用于下一个读或写操作.空洞文件:文件位移量可以大于文件的当前长度,在这种情况下,对该文件的下一次写将延长该文件,并在文件中构成一个空洞,这一点是允许的.(位于文件中但没有写过的字节都读为0).实例:创建具有空洞的文件.#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include "apue.h"char buf1[] = "abcdefghij";char buf2[] = "ABCDEFGHIJ";int main(void){        int fd;        if( (fd = creat("file.hole",FILE_MODE)) < 0)                err_sys("creat error");        if(write(fd,buf1,10) != 10)                err_sys("buf1 write error");        /*offset now = 10 */        if(lseek(fd,40,SEEK_SET) == -1)                err_sys("lseek error");        /*offset now = 40 */        if(write(fd,buf2,10) != 10)                err_sys("buf2 write error");        /*offset now = 50 */        exit(0);}yichen@yichen-Lenovo-G510:~$ ls -l file.hole-rw-r--r-- 1 yichen yichen 50 113 12:08 file.holeyichen@yichen-Lenovo-G510:~$ od -c file.hole0000000   a   b   c   d   e   f   g   h   i   j  \0  \0  \0  \0  \0  \00000020  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \00000040  \0  \0  \0  \0  \0  \0  \0  \0   A   B   C   D   E   F   G   H0000060   I   J0000062

我们来看运行后的变化:ls -l 检查其大小,od -c 检查其实际内容.我们发现中间的未写字节都不读为\0;

9.read函数:从已打开文件中读取数据.

#include <unistd.h>ssize_t read(int filedes,void * buff,size_t nbytes)

有多种情况可使实际读到的字节数少于要求读字节数:
1.读普通文件时,在读到要求字节数之前已到达了文件尾端.
2.当从终端设备读时,通常一次最多读一行.(第11章讲如何改变).
3.当从网络读时,网络中的缓冲机构可能造成返回值小于所要求读的字节数.
4.某些面向记录的设备,例如磁带,一次最多返回一个记录.
10.write函数:向已打开文件写入数据.

#include <unist.h>ssize_t write(int filedes,const void * buff,size_t nbytes);

对于普通文件,写操作从文件的当前位移量处开始.
11.I/O效率:与BUFFSIZE的选取有关,当BUFFSIZE达到8192后不再减小.
12.文件共享:unix支持在不同进程间共享打开文件.
13.内核数据结构:每个进程在进程表都有一个记录项,每个记录项有一张打开文件描述符表,可将其视为一个矢量,每个描述符占用一项.
文件描述符:文件描述符标志,指向一个文件表项的指针;
文件表(内核为所有打开文件维持一张文件表):文件状态标志,当前文件位移量,指向该文件v节点表项的指针.
v节点结构(每个打开文件都有):包含了文件类型和对此文件进行各种操作的函数的指针信息.
这里写图片描述

14.原子操作:任何一个要求多余一个函数调用的操作都不能称为原子操作,因为在两个函数调用之间,内核有可能会临时挂机该进程.
15.dup和dup2函数:可用来复制一个现存的文件描述符

#include <unistd.h>int dup(int filedes);int dup2(int filedes,int filedes2);

dup:返回的新文件描述符一定是当前可用文件描述符中的最小数值.
dup2:可以用filedes2参数制定新描述符的数值.如果filedes2已经打开,则现将其关闭.如若filedes等于filedes2,则dup2返回filedes2,而不关闭它.
这些函数返回的新文件描述符与参数filedes共享同一个文件表项.如图:
我们假定进程执行了newfd = dup(1);那么下一个可用描述符是3(0,1,2由shell打开).
复制一个文件描述符另一种方法是使用fcntl函数.

dup(filedes) 等价 fcntl(filedes,F_DUPFD,0);dup2(filedes,filedes2) 等价 close(filedes2);fcntl(filedes,F_DUPFD,filedes2);区别;dup2是原子操作,而fcntl不是.16.fcntl函数:可以改变已经打开文件的性质.#include <sys/types.h>#include <unistd.h>#include <fcntl.h>int fcntl(int filedes,int cmd,.../* int arg */);

fcntl函数的五种功能:
1.复制一个现存的描述符(cmd = F_DUPFD);
2.获得/设置文件描述符标记(cmd = F_GETFD或F_SETFD).
3.获得/设置文件状态标志(cmd = F_GETFL或F_SETFL).
4.获得/设置异步I/O权(cmd = F_GETOWN或F_SETOWN).
5.获得/设置记录锁(cmd = F_GETLK,F_SETLK或F_SETLKW).
好,具体函数,用时具体查询.下一章.

0 0
原创粉丝点击