Linux系统学习笔记:文件I/O

Linux支持C语言中的标准I/O函数,同时它还提供了一套SUS标准的I/O库函数。和标准I/O不同,UNIX的I/O函数是不带缓冲的,即每个读写都调用内核中的一个系统调用。本篇总结UNIX的I/O并和标准I/O进行对比。

文件描述符

内核通过文件描述符引用打开的文件,它是一个非负整数。按惯例,shell中使用0与进程的标准输入关联,1与标准输出关联,2与标准错误输出关联。依照POSIX,这些幻数应替换为符号常量 STDIN_FILENO 、 STDOUT_FILENO 、STDERR_FILENO 以提高可读性 ,定义在 <unistd.h> 中。

Linux中打开文件 /dev/fd/n 等价于复制描述符n,还有 /dev/stdin 、 /dev/stdout 、 /dev/steerr 分别等价于/dev/fd/0 、 /dev/fd/1 、 /dev/fd/2 。

fd = open("/dev/fd/0", mode); 大多数系统忽略这个函数调用所指定的mode,而另外的一些系统则要求mode必须是所引用的文件(这里是标准输入)初始打开时所使用的打开模式的一个子集。因为上面的打开等效于 fd = dup(0); ,所以描述符0和fd共享同一个文件表项。正是因为共享同一个文件表项,所以他们看到的文件状态标志(也就是打开模式)应该是一样的。

UNIX标准的I/O函数

UNIX标准的一组I/O函数主要包括 open 、 creat 、 close 、 lseek 、 read 、 write 。

使用 open 和 creat 打开文件

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

/* 打开或创建文件
 * @return      成功返回文件描述符,出错返回-1 */
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
/* 创建文件
 * @return      成功返回为只写打开的文件描述符,出错返回-1 */
int creat(const char *pathname, mode_t mode);

参数说明:

pathname
要打开或创建的文件的名字。
flags

函数选项,包括:

  • O_RDONLY ,只读打开。
  • O_WRONLY ,只写打开。
  • O_RDWR ,读写打开。

以上三个选项必须有且只有一个。可选的选项还有:

  • O_APPEND ,每次写都追加在文件尾。
  • O_CREAT ,文件不存在时创建文件,需设置 mode 参数。
  • O_EXCL ,和 O_CREAT 同用,文件已存在则出错,文件不存在则创建文件,使测试和创建成为一个原子操作。
  • O_TRUNC ,文件存在且为写打开时,长度截短为0。
  • O_NOCTTY ,对于终端设备,不将该设备分配为此进程的控制终端。
  • O_NONBLOCK ,对于FIFO、块特殊文件、字符特殊文件,设为非阻塞模式。

还有三个同步相关的选项:

  • O_SYNC , write 等待物理I/O操作完成,包括文件属性的更新。
  • O_DSYNC , write 等待物理I/O操作完成,若写不影响读取(如文件大小没变化),不等待文件属性的更新。
  • O_RSYNC , O_SYNC 的同义词。

open 返回的文件描述符一定为最小可用描述符,有时用这一特性来在标准输入/输出/错误输出上打开文件。

creat 是一个历史遗留函数,用于以前没有 O_CREAT 选项的情况,它等价于:

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

使用 close 关闭文件

#include <unistd.h>

/* 关闭打开的文件
 * @return      成功返回0,出错返回-1 */
int close(int fd);

关闭文件时会释放该进程加在文件上的所有记录锁。进程终止时,内核会自动关闭它打开的文件。

打开的文件有一个相关联的当前文件偏移量,通常为非负整数,表示从文件开始的字节数。读/写操作从当前文件偏移量处开始,使偏移量增加读写的字节数。默认打开文件偏移量为0,以 O_APPEND 打开偏移量为文件的字节数。可以用lseek 设置文件的偏移量。

lseek 获取或设置文件的偏移量

#include <sys/types.h>
#include <unistd.h>

/* 设置文件的偏移量,设为whence指定的位置加offset
 * @return      成功返回新的文件偏移量,出错返回-1 */
off_t lseek(int fd, off_t offset, int whence);

参数说明:

offset
要增加的偏移量。
whence
  • SEEK_SET ,相对文件开始处。
  • SEEK_CUR ,相对文件当前位置。
  • SEEK_END ,相对文件结尾处。

lseek 并不引起I/O操作,偏移量记录在内核中。

普通文件的偏移量必须是非负整数。偏移量可以大于文件的长度,这样之后的写会形成一个空洞,空洞不占存储,其中的字节被读为0。可以写一个这样的文件,用 od -c 命令验证一下,还可用 ls -ls 命令查看磁盘块占用情况。

用 read 函数读取文件的数据

#include <unistd.h>

/* 从打开的文件读数据,从当前偏移量开始,并将偏移量增加实际读取字节数
 * @return      成功返回读到的字节数,已到文件尾返回0,出错返回-1 */
ssize_t read(int fd, void *buf, size_t count);

受文件大小、网络缓冲区、管道、FIFO的实际字节数的限制,实际读到的数据可能少于要读的字节数,信号中断也会造成这种情况。终端设备通常一次最多读一行,磁带等设备一次最多读一个记录。

用 write 函数向文件写入数据

#include <unistd.h>

/* 向打开的文件写数据,从当前偏移量开始,并将偏移量增加实际写入字节数
 * @return      成功返回写入的字节数,出错返回-1 */
ssize_t write(int fd, const void *buf, size_t count);

对于普通文件,写操作从文件的当前偏移量开始。如果在打开文件时,指定了O_APPEND选项,则在每次写操作之前,将文件的偏移量设置在文件的当前结尾处。

#include <stdlib.h>
#include <unistd.h>
#include "error.h"

#define BUFFSIZE    4096

int main(void)
{
    int     n;
    char    buf[BUFFSIZE];

    while ((n = read(STDIN_FILENO, buf, BUFFSIZE)) > 0)
        if (write(STDOUT_FILENO, buf, n) != n)
            err_sys("write error");
    if (n < 0)
        err_sys("read error");
    exit(0);
}

注意linux上不同缓冲区长度对读操作的时间的影响。

文件共享

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

  • 打开文件描述符表:进程在进程表中都有一个记录项,包含一张打开文件的描述符表。每个描述符占一项,包含描述符标志和指向一个文件表项的指针。
  • 文件表:内核维持一张所有打开文件的文件表,每个文件表项包含文件状态标志、当前文件偏移量、指向文件v节点表项的指针。
  • v结点表:每个打开文件有一个v节点表,每个v节点包含文件类型、操作函数指针和文件的i节点等。

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

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

注意:文件表项要存储在内核中,打开文件描述符表可以存放在用户空间(作为一个独立的对应于每一个进程的结构,可以换出),而非进程表中。这些表也可以用多种方式实现,不必一定是数组,例如,可以将它们实现为结构的链表。

原子操作

时间: 2024-10-08 10:29:10

Linux系统学习笔记:文件I/O的相关文章

Linux 程序设计学习笔记----文件管理系统

本文部分整理自网络 Linux下文件系统管理 1.VFS文件系统概述 linux采用VFS来管理文件系统,而且linux设计的原则之一就是everything is file.因此文件管理系统是linux设计最核心的体现. VFS的全称是Virtual File System (虚拟文件系统). 总体上说 Linux 下的文件系统主要可分为三大块:一是上层的文件系统的系统调用,二是虚拟文件系统 VFS(Virtual Filesystem Switch),三是挂载到 VFS 中的各实际文件系统,

Linux系统学习笔记:序

Linux系统学习笔记:序 ??Linux是一套免费使用和自由传播的类Unix操作系统,是一个基于POSIX和UNIX的多用户.多任务.支持多线程和多CPU的操作系统.它能运行主要的UNIX工具软件.应用程序和网络协议.它支持32位和64位硬件.Linux继承了Unix以网络为核心的设计思想,是一个性能稳定的多用户网络操作系统. 本人使用的Linux为Ubuntu,主要以<APUE>(第3版)为学习蓝本. 1. Unix/Linux 体系结构 如图: 内核的接口被称为系统调用.公用函数库构建在

linux系统学习笔记:无死角理解保存的设置用户ID,设置用户ID位,有效用户ID,实际用户ID

一.基本概念 实际用户ID(RUID):用于标识一个系统中用户是谁,一般是在登录之后,就被唯一的确定,就是登录的用户的uid. 有效用户ID(EUID):用于系统决定用户对系统资源的权限,也就是说当用户做任何一个操作时,最终看它有没有权限,都是在判断有效用户ID是否有权限.如果有,则ok,否则报错不能执行.在正常的情况下,一个用户登录之后(假设是A用户),A用户的有效用户ID和实际用户ID是相同的,但是如果A用户在某些场景中想要执行一些特权操作,能顺利的执行吗?上面说到了用户的任务操作,linu

Linux系统学习笔记之 1 基础命令

翻看日记,看到以前自己学习Linux是的笔记来了,温故而知新乎. 文件命名规则: 1.除了/之外,所有的字符都合法. 2.有些字符最好不要用,如空格符.制表符.退格符.和@ # & ( ) - 等. 2.避免使用.作为普通文件的第一个字符.(以点开头的是隐藏文件) 4.大小写敏感. 命令格式: 命令格式:命令 -选项 参数 例:ls -la /etc 说明:1.当有多个选项时,可以写在一起. 2.两个特殊的目录,.和.. ,分别代表当前目录和当前目录的父目录. 文件处理命令:ls 命令名称:ls

Linux系统学习笔记之 1 一个简单的shell程序

不看笔记,长时间不用自己都忘了,还是得经常看看笔记啊. 一个简单的shell程序 shell结构 1.#!指定执行脚本的shell 2.#注释行 3.命令和控制结构 创建shell程序的步骤 第一步:创建一个包含命令和控制结构的文件 第二步:修改这个文件的权限使它可以执行. 使用chmod u+x 第三步:执行shell sh /test/example.sh Shell变量 变量:是shell传递数据的一种方法,用来代表每个取值的符号名 shell有两类变量:临时变量和永久变量 临时变量是sh

linux shell 学习笔记--文件测试符

1. 文件测试操作 ---------------- 返回true 如果... -e 文件存在 -a 文件存在 这个选项的效果与-e 相同.但是它已经被弃用了,并且不鼓励使用 -f file 是一个regular 文件(不是目录或者设备文件) -s 文件长度不为0 -d 文件是个目录 -b 文件是个块设备(软盘,cdrom 等等) -c 文件是个字符设备(键盘,modem,声卡等等) -p 文件是个管道 -h 文件是个符号链接 -L 文件是个符号链接 -S 文件是个socket -t 关联到一个

Linux命令学习笔记目录

Linux命令学习笔记目录 最近正在使用,linux,顺便将用到的命令整理了一下. 一. 文件目录操作命令: 0.linux命令学习笔记(0):man 命令 1.linux命令学习笔记(1):ls命令 2.linux命令学习笔记(2):cd命令 3.linux命令学习笔记(3):pwd命令 4.linux命令学习笔记(4):mkdir命令 5.linux命令学习笔记(5):rm 命令 6.linux命令学习笔记(6):rmdir 命令 7.linux命令学习笔记(7):mv命令 8.linux命

Linux 程序设计学习笔记----POSIX 文件及目录管理

转载请注明:http://blog.csdn.net/suool/article/details/38141047 问题引入 文件流和文件描述符的区别 上节讲到ANSI C 库函数的实现在用户态,流的相应资源也在用户空间,但无论如何实现最终都需要通过内核实现对文件的读写控制.因此fopen函数必然调用了对OS的系统调用.这一调用在LINUX下即为open, close, read, write等函数.这些都遵循POSIX标准. so,在linux系统中是如何通过POSIX标准实现对文件的操作和目

Linux mkisofs 创建光盘镜像文件(Linux指令学习笔记)

mkisofs命令 创建光盘文件的系统的命令是mkisofs.光盘系统有多种格式,利用Linux系统提供的光盘文件系统创建 命令mkisofs,可以创建多种iso9660文件系统. 我们一般不用mkisofs直接刻录光盘,而常用它创建一个光盘映像文件. mkisofs用法如下: mkisofs  [options] [-o filename] pathspec[pathspec...] -o filename :光盘映像文件名. pathspec:    要刻录的文件名,目录或者树目录. opt