unix环境高级编程 第三版 第三章 文件I/O

3.1 引言

    理解常用的五个函数:open,read,write,lseek,close,另外了解所谓不带缓冲的I/O,即每个read和write都调用内核中的一个系统调用。还要通过open函数来讨论原子操作的概念,然后说明dup,fcntl,sync,fsync,ioctl几个函数即可!

3.2 文件描述符

    1.对于内核而言,所有打开的文件都通过文件描述符引用,文件描述符是一个非负整数,当打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符。

  2.一般而言,unix的shell将文件描述符0,1,2分别与标准输入,标准输出,标准错误相关联,即STDIN_FILENO,STDOUT_FILENO,STDERR_FILENO。这些常量定义在<unistd.h>中,文件描述符的范围是0~OPEN_MAX-1。

 3.3 函数open和openat

    1.调用这两个函数可以打开或则创建一个文件。

#include<fcntl.h>

int open(const char *path, int oflag, .../*mode_t mode*/);      //仅当创建新文件时才使用最后这个参数

int openat(int fd, const char *path, int oflag, ... /*mode_t mode);

下面有各参数含义:

path:打开或者创建文件的名字

oflag参数可以用来说明此参数是多个选项,可用 | 构成该参数,

O_RDONLY   只读打开

O_WRONLY 只写打开

O_RDWR  读写打开

O_EXE 只执行打开

O_SEARCH 只搜索打开

很显然,上面这5个参数有且只能有一个参与,然而下面这几个参数可以不只一个参与,即没有唯一性

O_APPEND 每次写时都追加到文件尾

O_CLOEXEC 把FD_CLOEXEC常量设置为文件描述符

O_CREAT 若此文件不存在则创建它

O_DIRECTORY 如果path引用的不是目录,则出错

O_EXCL 如果同时指定了O_CREAT,而文件已经存在,则出错

O_NOCTTY 如果path引用的是终端设备,则不将该设备分配作为此进程的控制终端

O_NOFOLLOW 如果path引用的是一个符号链接,则出错

O_NOBLOCK 如果path引用的是一个FIFO,一个块特殊文件或一个字符特殊文件,则出错

O_SYNC 使每次write等待物理I/O操作完成,包括该write操作引起的文件属性更新所需I/O

O_TRUNC 如果此文件存在,而且为只写或读-写成功打开,则将其长度截断为0

O_TTY_INIT 如果打开一个还未打开的终端设备,设置非标准termious参数值,使其符合独立的UNIX特性。

2. fd参数把open和openat函数区分开,共有三种可能性

(1)path参数指定的是绝对路径名,在这种情况下fd参数被忽略,openat函数就相当于open函数

(2)path参数指定的是相对路径名,fd参数指出了相对路径名在文件系统中的开始地址,fd参数是通过打开相对路径名所在的目录来获取

(3)path指定的是相对路径名,fd参数具有特殊值AT_FDCWD,在这种情况下路径名在当前工作目录中获取,open函数在操作上与openat函数相似

3.4 函数create

#include<fcntl.h>

int creat(const char *path,mode_t mode);     //若成功,返回为只写打开的文件描述符,若出错,返回-1

注意,此函数等效为open(path,O_WRONLY|O_CREAT|O_TRUNC,mode);这个函数的缺点是只能创建以只写的方式打开的文件

3.5 函数close

可以调用close函数关闭一个打开文件

#include<unistd.h>

int close(int fd);//成功返回0,出错则返回-1

当一个进程终止时,内核会自动关闭它所打开的文件。

3.6 函数lseek

每个打开文件都有一个与其相关联的“当前文件偏移量”,其通常是一个非负整数,用以度量从文件开始出的计算的字节数。可以用lseek显示地为一个文件设置偏移量。

#include<unistd.h>

off_t lseek(int fd,off_t offset ,int whence);   //若成功,返回新的文件偏移量,若出错,返回-1

对参数offset的解释与whence的值相关

1.若whence是SEEK_SET,则将该文件的偏移量设置为距文件开始出offset个字节

2.若whence是SEEK_CUR,则将该文件偏移量设置为当前值加offset,offset可正可负

3.若whence的值是SEEK_END,则将该文件偏移量设置为文件长度加offset,offset可正可负

3.7 read函数

[cpp]  view plain  copy
  1. /* 
  2. *函数功能:读取已打开文件的数据; 
  3. *返回值:若成功返回读取的字节数,若到文件尾则返回0,若出错则返回-1; 
  4. */  
  5. /* 
  6. *函数原型: 
  7. */  
  8. #include <unistd.h>  
  9.    
  10. ssize_t read(int filedes, void *buf, size_tnbyte);  
  11.    
  12. /* 
  13. *参数解释: 
  14. *filedes是指所要读取文件的文件描述符; 
  15. *buf存储所读取数据; 
  16. *nbyte表示最多读取的字节数; 
  17. */  

3.8 write函数

[cpp]  view plain  copy
  1. /* 
  2. *函数功能:向已打开文件写入数据; 
  3. *返回值:若成功返回已写的字节数,若出错则返回-1; 
  4. */  
  5. /* 
  6. *函数原型: 
  7. */  
  8. #include <unistd.h>  
  9.    
  10. ssize_t write(int filedes, const void *buf,size_t nbyte);  
  11.    
  12. /* 
  13. *参数解释: 
  14. *filedes是指所要写入数据文件的文件描述符; 
  15. *buf存储所要存储的数据; 
  16. *nbyte表示最多写入的字节数; 
  17. */  

3.10 文件共享

    UNIX支持不同进程共享打开的文件。内核使用三种数据结构表示打开的文件:

进程在进程表中都有一个记录项,记录项中包含一张打开文件的描述符表。每个描述符占一项:描述符标志fd;指向一个文件表项的指针。

内核维持一张所有打开文件的文件表,每个文件表项包含文件状态标志、当前文件偏移量、指向文件v节点表项的指针。

每个打开文件有一个v节点表,每个v节点包含文件类型、操作函数指针和文件的i节点等。

        Linux将v节点和i节点实现为独立于文件系统的i节点和依赖文件系统的i节点。

       不同进程共享文件时,每个进程都有一个该文件的文件表项,指向同一个v节点表。多个文件描述符也可能指向同一个文件表项,如使用 dup 函数和 fork 后的父子进程。以下是不同进程共享同一个文件的示意图:



3.11 原子操作

         原子操作指由多步组成的操作,执行时要么全部执行,要么一步也不执行。
       多个进程共享同一个文件,可能造成进程对文件的连续的操作被打乱,这就需要使操作成为原子操作。如 O_APPEND 将到尾端和写入数据组成原子操作,还有 O_CREAT 和 O_EXCL 将检查文件是否存在和创建文件组成原子操作。例如:pread 和 pwrite 将偏移量和读/写组成原子操作。

pread 和 pwrite 函数

[cpp]  view plain  copy
  1. #include <unistd.h>   
  2. /* 函数功能:定位并读取数据 
  3. * 返回值:成功返回读到的字节数,已到文件尾则返回0,出错返回-1 ; 
  4. */  
  5. ssize_t pread(int fd, void *buf, size_tcount, off_t offset);  
  6. /* 函数功能:定位并写入数据 
  7. * 返回值:成功返回写入的字节数,出错返回-1; 
  8. */  
  9. ssize_t pwrite(int fd, const void *buf,size_t count, off_t offset);  

3.12 函数dup 和dup2

这两个函数用于复制一个文件描述符

#include<unistd.h>

int dup(int fd);

int dup2(int fd ,int fd2); //这两个函数用于复制一个文件描述符,dup返回一个新的文件描述符,且这个文件描述符一定是当前可用文件描述符中的最小数。

对于dup2,可以用fd2来描述新的文件描述符,若fd2已经打开,则先将其关闭。若fd=fd2,则dup2返回fd2,而且不关闭它。否则fd2的FD_CLOEXEC文件描述标志就被清除,这样fd2在进程调用exec时是打开状态。

3.13 刷新缓冲区函数

[cpp]  view plain  copy
  1. unix系统在写数据时是采用文件延迟写,但是我们可以自己刷新缓冲区,将数据写入磁盘。以下是刷新缓冲区,将数据写入磁盘的函数sync,fsync,fdatasync;  
  2. #include <unistd.h>  
  3.    
  4. /* 将所有修改过的块缓冲区排入写队列,然后返回,不等待写磁盘结束 */  
  5. void sync(void);  
  6.    
  7. /* 对指定文件刷新块缓冲区,等待写磁盘结束,更新文件属性 
  8. * 返回值:成功返回0,出错返回-1 */  
  9. int fsync(int fd);  
  10.    
  11. /* 对指定文件刷新块缓冲区,等待写磁盘结束,不更新文件属性 
  12. * 返回值 成功返回0,出错返回-1 */  
  13. int fdatasync(int fd);  

3.14 fcntl函数

该函数可以改变已经打开文件的属性

#include<unistd.h>

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

该函数有以下五个功能

(1)复制一个已有的描述符(cmd=F_DUPFD或F_DUPFD_CLOEXEC)

(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)

(3)ioctl函数

头文件

#include <unistd.h>   /*System V*/

#include <sys/ioctl.h>  /*BSD and Linux*/

#include <stropts.h>   /*XSI STREAM*/

函数原型

int ioctl(int filedes, int request, …)

参数

filedes:文件描述符

request:请求

返回

若出错则返回-1,若成功则返回其他值

功能

ioctl函数是I/O操作的杂物箱,不能用本章中其他函数表示的I/O操作通常都能用ioctl表示。













  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值