1.文件IO

文件IO

Unix系统中的文件I/O通常只用到5个函数:openreadwritelseekclose。这一节描述的函数都是不带缓冲的I/O,不带缓冲是指每个readwrite都会执行一个系统调用,而不是从缓冲中读取数据。

1.文件描述符

对于内核而言,所有打开的文件都通过文件描述符引用。文件描述符是一个非负数,范围0~OPEN_MAX-1。默认情况下0,1,2对应进程标准输入、输出和错误,这些魔法数宏定义在<unistd.h>

2.函数open和openat

调用openopenat函数可以打开或创建一个文件。

#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是函数模式(比如只读),通过位与|操作设定,mode用于指定插入模式(比如从尾端追加)。两个函数的区别在于fd,共3种可能:

  • path指定的是绝对路径,fd会被忽略,两个函数相同
  • path指定的相对路径,fd给出相对路径名在文件系统中的开始地址,fd是通过打开相对路径名所在的目录来获取
  • path参数指定了相对路径,fd参数具有特殊值AT_FDCWD,路径名在当前工作目录中获取。

openat的提出主要是解决:

  • 线程可以使用相对路径名打开目录中的文件,而不再只能打开当前工作目录
  • 避免TOCTTOU错误:两个基于文件的调用,由于两个调用不是原子操作,函数调用之间文件可能发生改变,导致第一个调用结果不再有效

3.函数creat

creat同样用于创建一个新文件:

#include<fcntl.h>
int creat(const char *path, mode_t mode);
/* 返回值:成功返回文件描述符,失败返回-1 */

函数等效于:

open(path, O_WRONLY | O_CREAT | O_TRUNC, mode);

creat的缺陷是它以只写方式打开创建的文件。

4.函数close

close函数用于关闭打开的文件:

#include<unistd.h>
int close(int fd);
/* 返回值:成功0,失败-1 */

关闭文件时,会释放该进程加在该文件上的所有记录锁。当一个进程终止时,内核自动关闭它所有打开的文件,所以很多程序并没显式调用close

5.lseek

lseek用于指定读写操作的偏移量处(不指定默认为0):

#include<unistd.h>
off_t lseek(int fd, off_t offset, int whence);
/* 返回值:成功返回偏移量,失败返回-1 */

whence有3个值:

  • SEEK_SET,则偏移量为距离文件开始处offset字节
  • SEEK_CUR,则偏移值设置为当前值+offset(可正可负)
  • SEEK_END,则偏移值设置为文件长度+offset(可正可负)

利用lseek测试输入是否能使用偏移值:

#include<apue.h>
int main()
{
    if(-1 == lseek(STDIN_FILENO, 0, SEEK_CUR))
        printf("cannot seek\n");
    else
        printf("seek OK\n");
    exit(0);
}

管道,FIFO或网络套接字都不能使用lseek,所以结果为(只测试了管道和普通文件):

lseek测试

注意:

  • 由于偏移量可能是负值,因此比较lseek返回值时应当测试它是否等于-1,而不是是否小于0。
  • 文件空洞问题,文件偏移量可以大于文件长度,因此下一次写将加长该文件,在文件中构成空洞,文件空洞并不要求占用磁盘上的存储区,具体处理方式与文件系统有关。

空洞文件测试:

#include "apue.h"
#include <fcntl.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, 16384, SEEK_SET) == -1)
        err_sys("lseek error");
    /* offset now = 16384 */

    if (write(fd, buf2, 10) != 10)
        err_sys("buf2 write error");
    /* offset now = 16394 */

    exit(0);
}

运行该程序可以得到:


hole

6.read和write

read函数用于打开文件中读取数据:

#include<unistd.h>
ssize_t read(int fd, void *buf, size_t nbytes);
/* 返回值:读取的字节数,若到尾部返回0;若出错返回-1 */

write函数用于向打开文件写数据:

#include<unistd.h>
ssize_t write(int fd, const void *buf, size_t nbytes);
/* 返回值:若成功,返回写入字节数;出错返回-1 */

7.文件共享

内核使用三种数据结构表示打开文件,它们间关系决定了文件共享方面一个进程对另一个进程的影响。

打开文件的内核数据结构

从上图可以看出,每个进程都有一个进程表项,里面存放文件表项的指针;文件表项包含当前文件状态(读、写、同步、阻塞等),偏移值v-node表项指针v-node表项包括v-node信息(文件类型和操作函数),以及inode(索引节点),索引节点包含文件信息(文件所有者、长度、指向实际磁盘位置等),Linux中没有v-node只有索引节点。

如果两个独立进程各种打开同一个文件,则有下图关系:

共享文件的进程

假定第一个进程在文件描述符3上打开该文件,另一个在文件描述符4上打开该文件,这样每个进程各自获得一个文件表项(这样做,可以让每个进程拥有自己的偏移值),但是这两个文件表项指向相同的v-node。

8.原子操作

Unix为文件IO提供原子操作,例如在打开文件时设置O_APPEND,这样使得内核每次写操作时都将当前偏移量设置为文件尾端。

同时提供preadpwrite函数,他们分别执行的是lseek后调用read和write,但是在调用函数时,无法中断其定位和读写操作。

9.dup和dup2

下面两个操作可以用来复制一个现有文件描述符:

#include<unistd.h>
int dup(int fd);
int dup2(int fd, int fd2);
/* 返回值:成功返回文件描述符;失败返回-1*/

dup2是一个原子操作。

10.fcntl

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

#include<fcntl.h>
int fcntl(int fd, int cmd, ... /* int arg */)
/* 返回值:若成功,则依赖于cmd,若出错,返回-1 */

fcntl函数有以下5个功能(cmd控制):

  • 复制一个已有描述符
  • 获取/设置文件描述符标志
  • 获取/设置文件状态标志
  • 获取/设置异步I/O所有权
  • 获取/设置记录锁

11.其他函数

其余函数ioctl/dev/fd没怎么见过,前者可以实现各种功能,后者通过打开文件的方式复制文件描述符

小结

这节内容挺多的,主要是对文件I/O进行介绍,很多函数功能介绍,不太容易记住,所以需要多实验。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容