UNIX I/O

文件描述符:

文件描述符是一个非负整数。当打开一个现存文件或创建一个新文件时,内核向进程返回一个文件描述符。文件描述符 0、1、2 (通常使用<unistd.h>中定义的 STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO) 则用作进程的标准输入文件、标准输出文件和标准出错文件。

虚拟目录/proc/self/fd 中记录了当前进程所打开的文件描述符。

open 函数:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char * pathname, int oflag,.../*, mode_t  mode * / ) ;
返回:若成功为文件描述符,若出错为- 1

oflag   包括了 O_RDONLY(以只读方式打开)、O_WRONLY(以只写方式打开)、O_RDWR(以读写方式打开),这三个标志只能使用一个。下列常数则是可选择的:

O_APPEND  每次写时都加到文件的尾端。O_CREAT  若此文件不存在则创建它。使用此选择项时,需同时说明第三个参数 mode,用其说明该新文件的存取许可权位。O_EXCL  如果同时指定了O_CREAT,而文件已经存在,则出错。O_TRUNC  如果此文件存在,而且为只读或只写成功打开,则将其长度截短为 0。O_NOCTTY  如果pathname指的是终端设备,则不将此设备分配作为此进程的控制终端。O_NONBLOCK  如果pathname指的是一个FIFO、一个块特殊文件或一个字符特殊文件,则此选择项为此文件的本次打开操作和后续的 I / O操作设置非阻塞方式。O_SYNC  使每次write都等到物理I / O操作完成。

还包括三个 POSIX 可选的同步标志:O_DSYNC、O_RSYNC、O_SYNC。对于 Linux,三个标志的含义都O_SYNC 这个标志相同,使用此标志时,write(2)操作将阻塞到内核将内容真正同步到设备,文件在这之前将一直保持打开。

creat函数:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int creat(const char * pathname, mode_t  mode) ;
返回:若成功为只写打开的文件描述符,若出错为- 1

等效于:
open (pathname, O_WRONLY |O _ CREAT|O_TRUNC,  mode) ;

creat的一个不足之处是它以只写方式打开所创建的文件。

close函数:

用close函数关闭一个打开文件:

#include <unistd.h>
int close (int  filedes);
返回:若成功为 0,若出错为- 1

关闭一个文件时也释放该进程加在该文件上的所有记录锁。

lseek 函数:

调用lseek显式地定位一个打开文件。

#include <sys/types.h>
#include <unistd.h>
off_t lseek(int  filedes, off_t  offset, int whence) ;
返回:若成功为新的文件位移,若出错为- 1

lseek 执行时,将文件 filedes 的当前读写位置更改到相对 whence 指定的位置 offset 处的地方。whence 包括了 SEEK_SET(文件开始)、SEEK_END(文件末尾)、SEEK_CUR(当前),后两者的offset   可以是负数。offset   的类型 off_t 通常定义为一个机器字的长度(一般 typedef 自 long 类型,因为根据标准 C,long 在任何机器上都是和机器字的长度相同的。对于 32 位平台则为 4 个字节)。
lseek 成功时将返回相对于文件开始处的偏移量(可能是负数),失败返回­1 并设置 errno。在文件是FIFO、管道或者套接字时,lseek 将失败并设置 errno 为 ESPIPE(Illegal seek)。
lseek 只更改进程打开文件的状态,并不会引起 I/O 操作。

文件位移量可以大于文件的当前长度,在这种情况下,对该文件的下一次写将延长该文件,
并在文件中构成一个空调,这一点是允许的。位于文件中但没有写过的字节都被读为 0。

read函数:

用read 函数从打开文件中读数据。

#include <unistd.h>
ssize_t read(int  filedes, void * buff, size_t  nbytes) ;
返回:读到的字节数,若已到文件尾为 0,若出错为- 1

read 按指定的字节数 nbytes   从文件 filedes   的当前位置处读取数据,输入到缓冲区 buf 中。
write函数:

#include <unistd.h>
ssize_t write(int  filedes, const void * buff, size_t  nbytes) ;
返回:若成功为已写的字节数,若出错为- 1

其返回值通常与参数 nbytes的值不同,否则表示出错。 write出错的一个常见原因是:磁盘已写满,或者超过了对一个给定进程的文件长度限制

I/O的效率:


文件共享:

内核使用了三种数据结构,它们之间的关系决定了在文件共享方面一个进程对另一个进程可能产生的影响。

(1)  每个进程在进程表中都有一个记录项,每个记录项中有一张打开文件描述符表,可将其视为一个矢量,每个描述符占用一项。与每个文件描述符相关联的是:
(a)  文件描述符标志。
(b)  指向一个文件表项的指针。

(2)  内核为所有打开文件维持一张文件表。每个文件表项包含:
(a)  文件状态标志(读、写、增写、同步、非阻塞等 )。
(b)  当前文件位移量。

(c)  指向该文件v节点表项的指针。

(3)  每个打开文件(或设备)都有一个 v 节点结构。 v节点包含了文件类型和对此文件进行各种操作的函数的指针信息。对于大多数文件, v 节点还包含了该文件的 i 节点(索引节点) 。这些信息是在打开文件时从盘上读入内存的,所以所有关于文件的信息都是快速可供使用的。

下图显示了进程的三张表之间的关系。该进程有两个不同的打开文件——一个文件打开为标准输入(文件描述符0) ,另一个打开为标准输出(文件描述符为 1)。


如果两个独立进程各自打开了同一文件,则有下图所示的安排。


write 函数:

#include <unistd.h>
ssize_t write(int filedes, const void *buf, size_t nbytes);
write 按指定的字节数 nbytes   从 buf   处取数据,输出到文件 filedes   的当前位置处,如果已经到文件末
尾,将增加文件长度并在最后添加 EOF 标志。

原子操作:添加至一个文件

同时访问一个文件带来的同步问题

原子操作:创建一个文件

如果在打开和创建文件之间不同进程对同一个文件操作造成的问题

pread 和 pwrite 函数:

#include <unistd.h>
ssize_t pread(int filedes, void *buf, size_t nbytes, off_t offset);
ssize_t pwrite(int filedes, const void *buf, size_t nbytes, off_t 
offset);
这两个函数先将文件位置定位到距开始 offset   处,然后对其按给定参数进行读/写。这两个步骤是原子操作,这意味这要不这些步骤一次性全部执行,要不就不执行。如果不是原子操作,则可能会由于内核调度或者信号处理等原因,使其他进程插到几个步骤之间更改所操作对象的状态,而引起后续操作发生意外。

dup和dup2函数:

用来复制一个现存的文件描述符

#include <unistd.h>
int dup(int  filedes) ;
int dup2(int  filed s, int filedes2) ;
两函数的返回:若成功为新的文件描述符,若出错为- 1

函数使用一个当前进程中可用的最小文件描述符引用 filedes   所引用的文件,这个新的文件描述符的状态(打开标志、模式、当前位置等)和 filedes   相同。若成功,返回这个新的文件描述符。失败时返回­1(例如 filedes 不存在)并设置 errno。
dup2 可以直接指定 dup 中新的文件描述符的值为 filedes2,如果 filedes2   已经打开,则先原子的关闭之。如果 filedes==filedes2   且存在时则直接返回文件描述符而不执行关闭。失败时也返回­1 并设置errno。
dup2 常用于输入/输出重定向以实现管道操作。例如
dup2(filedes, STDOUT_FILENO);
则重定向标准输出到 filedes,相当于在 shell 中使用重定向操作符执行了"> file"。dup2 的功能也可以用 fcntl(2)实现,但后者不能实现为原子操作且某些 errno 不同。
如果使用 dup2(2)重定向描述符后,要关闭原来的文件描述符。则在 dup2 之前应比较两个参数是否
相等,以避免误关闭。


sync、fsync 和 fdatasync 函数:

通常内核为了考虑吞吐效率等情况,write 调用成功后并不马上将数据写到磁盘,而是放在磁盘的存区中并通过缓存区交换算法(例如最近最少使用)不定期的同步数据到磁盘,或者通过守护进程定时进行数据同步,也可以通过调用 sync 使其马上进行数据同步。
#include <unistd.h>
int fsync(int filedes);
int fdatasync(int filedes);
void sync(void);
sync 立即将内核缓冲的数据送到磁盘中的写队列,并直接返回。
fsync 也立即将内核缓冲的数据送到磁盘中的写队列,等待到磁盘写结束时才返回。
fdatasync 类似 fync,但除了同步数据外还同步文件的属性(例如 stat 结构的 st_ctime 等)。

fcntl 函数:

改变已经打开文件的性质:

#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
int fcntl(int  f i l e d e s, int c m d,.../* int  arg * / ) ;
返回:若成功则依赖于 cmd(见下),若出错为- 1

• 复制一个现存的描述符(cmd=F_DUPFD)。
• 获得/设置文件描述符标记(cmd = F_GETFD或F_SETFD)。
• 获得/设置文件状态标志(cmd = F_GETFL或F_SETFL)。
• 获得/设置异步I / O有权(cmd = F_GETOWN或F_SETOWN)。
• 获得/设置记录锁(cmd = F_GETLK , F_SETLK或F_SETLKW)。

arg 为根据 cmd 进行不同取值的相关参数

ioctl 函数:

不同平台对 ioctl函数的用途不尽相同

/dev/fd:

打开文件/dev/fd/n等效于复制描述符 n 

在函数中调用:
fd = open("/dev/fd/0", mode);等效于:
fd = dup(0);



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值