`
DiaoCow
  • 浏览: 244359 次
  • 性别: Icon_minigender_1
  • 来自: 南京
社区版块
存档分类
最新评论

【读书笔记】 《Unix环境高级编程》第三章 File I/O

阅读更多
对于unix系统而言,一切皆“文件”,所以我们就可以使用一套基本的IO函数,来对所有的“文件”进行读写操作!现在我们来看下文件操作的几个底层IO

1.open函数用来打开或创建一个文件,其函数原型如下:
int open(const char *pathname, int oflag, ... /* mode_t mode */);

第一个参数pathname用来指定需要打开的文件名;
第二个参数oflag用来指定打开文件的方式以及其他一些附加属性,常用的值有:

O_RDONLY  只读
O_WRONLY  只写
O_RDWR    读写

以上三个属性必须指定一个,但是下列属性是可选的

O_APPEND该参数表示每次写操作都是将内容添加到文件末尾(注意:使用该标记位后,每次调用write函数时,首先将file_table中current file offset字段(文件偏移)设置为文件末尾,然后在进行写操作,并且这一系列操作都是原子操作)
O_CREAT该参数表示当需要打开的文件不存在时,则创建(当指定这个标记位的时候,需要使用第三个参数mode,来指定创建文件的默认权限)
O_EXCL该参数表示如果需要打开的文件已经存在,并且指定了O_CREAT标记,则报错。该参数的作用可以参考下列讨论 http://bbs.csdn.net/topics/350001997
O_TRUNC该参数表示如果打开的文件已经存在,则删除文件中的内容(将文件大小截断为0)


如果打开或创建文件成功,open会返回一个文件描述符(int型值),否则返回-1表示调用失败

2.在较老的版本中,open函数只能用来打开一个已存在的文件,所以当文件不存在时,open函数会报错;若我们希望创建一个新的文件,则必须调用一个creat函数:
int creat(char *pathname, mode_t mode);

该函数等价于: open(pathname, O_WRONLY | O_CREAT | O_TRUNC, mode_t)

creat函数有一个不足,就是对于新创建的文件只能够进行写操作,若是你想对这个刚创建的文件进行读操作,就只能先close掉,然后再open进行读操作,非常繁琐,但是如果我们使用最新的open函数(对于不存在的文件,可以先创建然后再打开),则可以很简单的实现这一功能:open(pathname, O_RDWR | O_CREAT | O_TRUNC, mode_t)

所以,基本上我们可以使用open函数来替代creat函数

3.当我们成功调用open或者是creat函数时,会得到一个文件描述符(用来代表我们需要操作的文件),接着我们就可以拿着这个文件描述符对文件进行读写操作

a.read函数原型如下:
ssize_t read(int fileds, void *buf, size_t nbytes);

第一个参数fileds是文件描述符,第二个和第三个参数用来指定读取数据的存放位置以及每次read函数允许读取数据的最大数量(nbytes),read函数的返回值为此次调用读取到的字节数,若读取失败返回-1;

默认情况下,只有读取完nbytes个字节后,read函数才会返回,但是当遇到以下几种情况时,在未读取足够的字节时(< nbytes),read函数也会返回:
1.如果读取的是一个普通文件,当遇到EOF时,read函数立即返回;
2.如果读取的是一个终端设备(譬如标准输入),当遇到一个换行符时,read函数立即返回;
3.其余情况参考《apue》(自己尚未理解,暂不罗列);

b.write函数原型:
ssize_t write(int fileds, void *buf, size_t nbytes);

该函数返回值表示成功写入的字节数,若返回值等于nbytes表示写操作成功;
该函数比较简单,但是要注意的是,当文件打开标记含有O_APPEND时,每次写操作(调用write函数)首先会将当前的文件的偏移设置为文件默认,然后再写入输出数据;

4.当我们完成读写操作后,需要调用close函数关闭刚才打开的文件描述符(释放资源),close函数原型为:
int close(int fileds);

注意,即使我们没有主动关闭文件描述符,当一个进程终止时,也会关闭所有还未关闭的文件描述符;说了这么多,我们来看一个例子(会用上我们之前说的所有函数):
#include <apue.h>
#include <fcntl.h>
#define BUF_SIZE 4096
#define FILE_MODE 0666 

int main(void) 
{
    int fd, n;
    char buf[BUF_SIZE];

    if ((fd = open("out.tmp", O_WRONLY | O_CREAT | O_TRUNC, FILE_MODE)) < 0) {
        printf("Fail to open out.tmp\n");
    }   

    while ((n = read(STDIN_FILENO, buf, BUF_SIZE)) > 0) {
        if (write(fd, buf, n) != n) {
            printf("Fail to write out.tmp\n");
        }   
    }   

    close(fd);
    
    return 0;
}

这样我们就用最底层文件IO实现了将标准输入内容输出到文件的功能!
ps:一个进程中默认有三个已打开的文件描述符:STDIN_FILENO(标准输入),STDOUT_FILENO(标准输出),STDERR_FILENO(标准错误输出)

5.通常我们是从文件开始处读写数据,但有的时候我们可能需要在文件中间某个位置读写数据,要实现这样的功能,需要使用lseek函数,其函数原型如下:
off_t lseek(int filedes, off_t offset, int wherece);

第二个参数offset表示相对于指定位置wherece的偏移量,wherece有以下3个值:

SEEK_SET 文件起始处
SEEK_CUR 文件当前位置
SEEK_END 文件末尾


--------------------------------------------------------------------------华丽风格线--------------------------------------------------------------------------

unix使用三种数据结构表示一个打开的文件,这三种结构是:
1.file_descripter_table(文件描述符表): 每个进程都维护了一个文件描述符表来操作和管理该进程打开的所有文件,该表的每条记录主要有两个字段:
a.文件描述符
b.文件指针(指向另一个数据结构file_table)

2.file_table(文件表):该表由操作系统维护,用来记录被打开文件的相关信息,其主要字段有:
a.文件标记(譬如我们使用open函数打开文件所指定的标记位)
b.文件偏移
c.v-node指针(指向另一个数据结构v-node_tabe)

3.v-node_table:关于它,只需要知道每一个打开的文件有且仅对应一个v-node即可

这三张表的大体结构我们已经说完,从文字描述中,我们也可以得知,这三表存在很强的关联性,现在我们通过下面这张图来更清晰的看下这几张表的关系:

当我们调用lseek函数时,其实只是移动了相关file_table中的current file offset,而未进行实际的IO操作(如果current file offset的值超过current file size,则更新v-node的current file size)

现在根据上图,我们来思考下,多个进程是如何实现文件享的,譬如A进程打开了文件hello.c,此时假如B进程也打开了hello.c文件 ,那么B进程能否读取hello.c文件并且B进程的读取是否会影响到A进程的读取?

要回答上面的问题,我们先看下面的图片


可以发现,当多个进程打开同一个文件时,只有v-node_table这一张表是共享的,其他都是独立的,这样每个进程就可以独立的记录自己的文件偏移,因此B进程的读取不会影响到A进程!通过这样的表结构组织,我们就实现了文件共享读!

最后我们在看下dup,dup2这两个函数,这两个函数时用来复制文件描述符的,那么复制文件描述符有什么作用呢?我们先看一个linux命令:

echo "hello, world" > out.tmp

该命令用来把"hello, world"从标准输出重定向到out.tmp文件中,那么这个重定向是如何实现的呢? 这里就需要使用dup或者dup2函数,把标准输出的文件指针指向指向out.tmp文件的file table, 如下图:


我们看下相关代码:
#include <apue.h>
#include <fcntl.h>

int main(void) 
{
	int fd, stdout_fd;
	char *str = "hello, world\n";

	if((fd = open("out.tmp", O_WRONLY | O_CREAT | O_TRUNC, 0666)) < 0) {
		printf("Fail to create out.tmp\n");
		exit(1);
	}

	stdout_fd = dup(STDOUT_FILENO);
    dup2(fd, STDOUT_FILENO);
	
	write(STDOUT_FILENO, str, strlen(str));
	dup2(stdout_fd, STDOUT_FILENO);
	write(STDOUT_FILENO, str, strlen(str));
	
	close(fd);

	return 0;
}

执行结果:


可以发现终端只输出了一次"hello, world",而原因是:第一次调用write输出时,标准输出文件描述符被重定向到文件(此时"hello,world"被输出到out.tmp文件),第二次调用write函数时,标注输出文件描述符已经恢原来设置(指向终端),所以此时我们看到在屏幕上输出了"hello,world"





  • 大小: 41.1 KB
  • 大小: 47.2 KB
  • 大小: 27.4 KB
  • 大小: 13.5 KB
分享到:
评论

相关推荐

    APUE读书笔记《UNIX环境高级编程第二版》

    ### APUE读书笔记《UNIX环境高级编程第二版》知识点概览 #### 第一章 Unix基础 **1. Unix手册页** - 手册页是Unix系统中查询命令、函数等帮助文档的方式。 - 分类包括命令(1)、系统调用(2)、库函数(3)、特殊...

    APUE读书笔记(Unix高级环境编程)

    #### 第三章 文件I/O - **文件描述符**:解释文件描述符的作用和使用方法。 - **打开文件**:介绍`open`函数的参数和返回值,以及打开文件的模式。 - **创建新文件**:使用`creat`函数创建新文件,并设置初始权限。...

    我的APUE2读书笔记

    #### 第三章 文件I/O **1. 文件描述符** 文件描述符是一个非负整数,用于标识一个打开的文件。 **2. 打开文件:open函数** `open`函数用于打开或创建一个文件,返回一个文件描述符。 **3. 创建新文件:creat函数*...

    awk学习笔记(学习awk编程的好帮手)

    awk是一种强大的文本分析工具,广泛应用于Linux和Unix系统中。...学习awk能够极大地提升在Linux和Unix环境下处理文本数据的能力。通过深入理解和实践,你可以利用awk解决各种数据处理挑战,提高工作效率。

    perl学习笔记参考

    Perl最初被设计用于Unix环境下的编程任务,随着时间的发展,其应用范围已经扩展到了各种操作系统之上。 **特点:** - **强大的能力与灵活性:**Perl融合了高级语言如C语言的强大功能和脚本语言的便捷性,使得用户既...

    Python基础笔记

    - **丰富的库**:拥有大量第三方库,扩展性强。 - **规范的代码**:强制使用空白符作为语句缩进。 - **缺点**: - **运行速度相对较慢**:由于是解释型语言,运行效率低于编译型语言。 - **国内市场份额较小**...

    c#学习笔记.txt

    下列转换属于隐式转换:例:object o=i; 标识转换。 隐式数值转换。 隐式枚举转换。 隐式引用转换。 装箱转换。 隐式常数表达式转换。 用户定义的隐式转换。 下列转换属于显式转换: object 0=(object)i; 所有...

    linux入门学习笔记

    ### Linux 入门学习笔记 #### 一、Linux 安装与配置 ##### 1. Linux 的安装方式 - **虚拟机安装**:通过虚拟化技术,在现有操作系统上模拟一个完整的计算机环境,安装 Linux。 - **安装虚拟机软件**: - **...

    awk学习笔记

    由于支持用户自定义函数和动态正则表达式等功能,awk成为了Linux/Unix环境中非常重要的编程工具之一。在命令行模式下,可以通过简单的命令来调用awk程序,但在实际应用中,更多的时候是通过编写脚本来实现复杂的数据...

    总结搜集的shell脚本学习笔记(完结篇).pdf

    substring=${string:2:3} # 截取从第3个字符开始的3个字符 replaced_string=${string/old/new} # 替换第一个匹配的子串 ``` #### 十四、数组实现 Shell脚本支持一维数组,可以通过索引访问和修改数组元素。示例: ...

    java笔记(适用于初学者)

    基本语法如`jar cvf myJar.jar file1 file2 ...`,`c`表示创建,`v`表示详细模式,`f`指定jar文件名,`file1 file2`则是要打包的文件或目录。jar文件本质上是ZIP格式的压缩文件,可以用`unzip`命令解压。 【计算机...

    awk学习笔记.pdf

    AWK是一种编程语言,用于在Linux环境下对文本和数据进行处理和分析。它是许多UNIX系统中的标准工具,并且是文本处理的强大工具。AWK通过模式匹配来处理文本文件,其中可以指定一系列规则,称为模式和动作,来对输入...

    shell脚本笔记

    Shell脚本是Linux/Unix操作系统中用于自动化任务的编程语言,它基于Bash或其他Shell变体,如Sh、Csh或Ksh。本笔记将深入探讨Shell脚本的基础知识、语法特性、常用命令以及如何编写实用的脚本来提高日常工作效率。 ...

    Linux命令笔记

    ### Linux命令笔记知识点详解 #### 1. UNIX发展历史与主要发行版本 ##### UNIX发展历史 - **1965年**:美国麻省理工学院(MIT)、通用电气公司(GE)以及AT&T贝尔实验室联合启动了名为Multics的项目。该项目旨在...

    awk简介与学习笔记收集第1/3页

    awk是一种强大的文本分析工具,主要用于在Linux/Unix环境中处理文本和数据。它的名字来源于三位创始人——Alfred Aho、Brian Kernighan和Peter Weinberger的首字母。awk通过匹配文本中的模式来执行指定的操作,它...

    GTK+2.0.pdf linux图形编程

    ### GTK+2.0 教程 — Linux图形编程 #### 概述 GTK+(GIMP Toolkit)是一款功能强大的开源图形界面开发库,主要用于Linux和其他类UNIX系统上开发跨平台的应用程序。GTK+2.0作为GTK的一个重要版本,提供了丰富的用户...

Global site tag (gtag.js) - Google Analytics