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

第三章 文件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.hole
yichen@yichen-Lenovo-G510:~$ od -c file.hole
0000000   a   b   c   d   e   f   g   h   i   j  \0  \0  \0  \0  \0  \0
0000020  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0
0000040  \0  \0  \0  \0  \0  \0  \0  \0   A   B   C   D   E   F   G   H
0000060   I   J
0000062

我们来看运行后的变化: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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值