Linux C 文件操作 -- 系统调用(open(),read()...) 和 标准I/O库(fopen(),fread()...)

一、什么是文件

在讲述文件操作之前,我们首先要知道什么是文件。看到这个问题你可能会感觉到可笑,因为对于用过计算机的人来说,文件是最简单不过的概念了,例如一个文本是一个文件,一个work文档是一个文件等。但是在Linux中,文件的概念还远不止于这些,在Linux中,一切(或几乎一切)都是文件。文件包括很多的内容,例如:大家知道的普通文件是文件,目录也是一个文件,设备也是一个文件,管道也是一个文件等等。对于目录、设备这些的操作也可以完全等同于对纯文本文件的操作,这也是Linux非常成功的特性之一吧。

二、系统调用

1、文件描述符

文件描述符是一些小数值,你可以通过它们访问的打开的文件设备,而有多少文件描述符可用取决于系统的配置情况。但是当一个程序开始运行时,它一般会有3个已经打开的文件描述符,就是

0:标准输入

1:标准输出

2:标准错误

那些数学(即0、1、2)就是文件描述符,因为在Linux上一切都是文件,所以标准输入(stdin),标准输出(stdout)和标准错误(stderr)也可看作文件来对待。

2、系统调用常用函数

A、open系统调用

open函数的原型为:

int open(const char *path, int oflags);

int open(const char *path, int oflags, mode_t mode);

path,是包括路径的完整文件名,oflags是文件访问模式(即是什么方式打开文件,只读、只写还是可读并可写等),mode用于设定文件的访问权限。具体的可选参数,可以自己查看手册页,这里不一一详述。

open建立了一条到文件或设备的访问路径,如果调用成功,返回一个可以被read、write等其他系统调用的函数使用的文件描述符,而且这个文件描述是唯一的,不与任何其他运行中的进程共享,在失败时返回-1,并设置全局变量errno来指明失明的原因。

B、write系统调用

write函数的原型为:

size_t write(int fildes, const void *buf, size_t nbytes);

write的作用是把缓冲区buf的前nbytes个字节写入到文件描述符fildes关联的文件中,返回实际写入的字节数。返回0表示没有写入任何数据,返回-1表示调用中出现了错误,错误代码保存在errno中。

注:fildes一定要是在open调用中返回的创建的文件描述符,或者是0、1、2等标准输入、输出或标准错误。

C、read系统调用

read函数的原型为:

size_t read(int fildes, void *buf, size_t nbytes);

read系统调用的作用是从与文件描述符相关的文件里读入nbytes个字节的数据,并把它们放到数据区buf中,返回读入的字节数,失败时返回-1。

D、close系统调用

close调用的函数原型为:

int close(int fildes);

close函数的作用是终于文件描述符fildes一其对应的文件之间的关联。

E、例子

说了这么多,我就给出一个完整的例子吧,就是从一个数据文件(里面有1M个‘0’字符)逐个复制到别一个文件。文件名为copy_system.c,代码如下:

#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>

int main()
{
	char c = ‘\0‘;
	int in = -1, out = -1;

	// 以只读方式打开数据文件
	in = open("Data.txt", O_RDONLY);

	// 以只写方式创建文件,如果文件不存在就创建一个新的文件
	// 文件属主具有读和写的权限
	out = open("copy_system.out.txt", O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);

	// 读一个字节的数据
	while (read(in, &c, 1) == 1)
	{
		// 写一个字节的数据
		write(out, &c, 1);
	}

	// 关闭文件描述符
	close(in);
	close(out);

	return 0;
}

三、标准I/O库

有过C编程经历的人都会知道stdio头文件,它就是C语言的标准IO库,在标准IO库中,与底层文件描述符相对应的是流,它被实现为指向结构FILE的指针。IO库的函数有很多,为了与前面的内容对应,这里还是只讲与前面四个函数相对应的函数,其他的函数,你可以查一查手册页。

A、fopen库函数

fopen库函数的原型为:

FILE* fopen(const char *filename, const char *mode);

它与底层系统调用open类似,成功时返回一个非空指针。失败时返回NULL。

B、fread库函数

fread库函数的原型为:

size_t fread(void *ptr, size_t size, size_t nitems, FILE *stream);

它与底层调用read相似,其作用是从stream读取nitems个长度为size的数据到ptr所指向的缓冲区中。返回值是成功读到缓冲区中的记录个数。

注:stream为用fopen函数返回的文件结构指针。

C、fwrite库函数

fwrite库函数的原型:

size_t fwrite(const void *ptr, size_t size, size_t nitems, FILE *stream);

它与底层调用write相似,其作用是从ptr指向的缓冲区中读取nitems个长度为size到数据,并把它们写到stream所对应的文件中。

D、fclose库函数

fclose库函数的原型为:

int fclose(FILE *stream);

它与系统调用close相似,其作用是关闭指定的文件流stream。

例子

同样地,下面是前一个例子的另一个实现版本,它实现的功能与先前的例子一样,不过使用的是标准I/O库,而不是系统调用,文件名为copy_stdio.c代码如下:

#include <stdio.h>
#include <stdlib.h>

int main()
{
	int c = 0;
	FILE *pfin = NULL;
	FILE *pfout = NULL;

	// 以只读方式打开数据文件
	pfin = fopen("Data.txt", "r");

	// 以只写方式打开复制的新文件
	pfout = fopen("copy_stdio.out.txt", "w");

	// 读数据
	while (fread(&c, sizeof(char), 1, pfin))
	{
		//写数据
		fwrite(&c, sizeof(char), 1, pfout);
	}

	// 关闭文件流
	fclose(pfin);
	fclose(pfout);

	return 0;
}

当然这里你也可以用其他的库函数来完成工作,如:用fgetc代替fread,用fputc代替fwrite等。

四、文件描述符和文件流的关系

每个文件流都对应一个底层文件描述符,你可以把底层输入输出操作与高层文件流操作混合使用,但是一般不要这样做,因为数据缓冲的后果难以预料。我们可以通过调用fileno函数(原型为:int fileno(FILE *stream))来确定文件流使用的底层文件描述符,它返回指向文件流的文件描述符。相反地,你可以通过调用函数fdopen(原型为FILE* fdopen(int fildes, const char* mode))来在一个已经打开的文件描述符上创建一个新的文件流,mode参数与fopen函数的完全一样,同时它必须符合该文件在最初打开时所设定的访问模式。

但是在Linux下的编程,系统调用用得比较多一些,因为很多时候系统调用能提供更多的灵活性和更加强大的功能,有些操作是一定要使用系统调用,例如,创建文件读写锁时就一定要使用系统调用。

五、系统调用 与 标准I/O的性能比较

就拿本例子中的代码来比较,两个例子编译后生成的可执行文件的文件名分别为:copy_system.exe和copy_stdio.exe,在Linux下用time命令来测试其运行时间如下:

从测试结果可以看出,系统调用的效率比库函数要低很多。为什么呢?

因为使用系统调用会影响系统的性能。与函数调用相比,系统调用时,Linux必须从运行用户代码切换到执行内核代码,然后再返回用户代码,所以系统调用的开销要比普通函数调用大一些。然而也是有办法减少这种开销的,就是在程序中尽量减少系统调用的的次数,并且让每次系统调用完成尽量多的工作。

而库函数为什么做同样的事情效率却会高这么多呢?这是因为库函数在数据满足数据块长度(或buffer长度)要求时才安排执行底层系统调用,从而减少了系统调用的次数,也让每次的系统调用做了尽量多的事情,所以效率就比较高。

六、提高系统调用的简单方法举例

用回每一个例子(coy_system.c)的代码,略加修改就能提高我们的效率,例如一次读1024个字节,修改后保存文件名为copy_system2.c,代码如下:

#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>

int main()
{
	char buff[1024];
	int in = -1, out = -1;
	int nread = 0;

	in = open("Data.txt", O_RDONLY);
	out = open("copy_system2.out.txt", O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);

	// 一次读写1024个字节
	while ((nread = read(in, buff, sizeof(buff))) > 0)
	{
		write(out, buff, nread);
	}

	close(in);
	close(out);

	return 0;
}

生成的可执行文件为copy_system2.exe,使用time命令查看其执行时间,如下:

比较下可以看出,其性能改善了一个数量级,其效率甚至比用库函数一个一个字符复制来来得高效,至少在我的机子上是这样。

参考:

http://blog.csdn.net/ljianhui/article/details/10055665

时间: 2024-10-16 04:42:07

Linux C 文件操作 -- 系统调用(open(),read()...) 和 标准I/O库(fopen(),fread()...)的相关文章

【Linux】文件操作系统调用

一. 文件描述符 在Linux下使用文件描述符来表示设备文件和普通文件.文件描述符是一个整型的数据,所有对文件的操作都通过文件描述符实现.文件描述符的范围是0~OPEN_MAX,系统中有3个已经分配的文件描述符,即标准输入.标准输出.和标准错误,他们的文件描述符的值分别为0.1.2. 文件描述符是文件系统中连接用户空间和内核空间的枢纽.当打开一个或者创建一个文件时,内核空间创建相应的结构,并生成一个整型的变量传递给用户空间的对应进程,进程用这个文件描述符来对文件进行操作. 二. 打开.创建文件o

qt ui程序使用Linux的文件操作open、close (转)

原文地址:qt ui程序使用Linux的文件操作open.close 作者:kjpioo 提出这个问题是因为在qt的QWidget类型的对象中,close()函数会和QWidget::close()冲突,如果在类函数实现时直接用close(),在程序编译时会提示提示错误(具体什么错误不记得了). 错误原因是QWidget::close()与stdio.h中的文件关闭标准函数close()产生了歧义.所以可以参考下文链接中的解决方案. http://bytes.com/topic/c/answer

linux程序设计——文件操作(第三章)

第三章    文件操作 3.1 linux文件结构 与UNIX一样,linux环境中的文件具有特别重要的意义,因为它们为操作系统服务和设备提供了一个简单而一致的接口.在linux中,一切都是文件. 这意味着,通常程序可以像使用文件那样使用磁盘文件.串行口.打印机等等. 目录也是文件,但它是一种特殊类型的文件.在现代UNIX(包括linux)版本中,即使是超级用户可能也不再被允许直接对目录进行写左操作了.所有用户通常都使用上层的opendir/readdir接口来读取目录,而无需了解特定系统中目录

【大话QT之五】Windows与Linux下文件操作监控的实现

一.需求分析: 随着渲染业务的不断进行,数据传输渐渐成为影响业务时间最大的因素.究其原因就是因为数据传输耗费较长的时间.于是,依托于渲染业务的网盘开发逐渐成为迫切需要解决的需求.该网盘的实现和当前市场上网盘实现有一些的不同,主要在客户端与服务器端的操作需要双向进行,即:用户在客户端的操作需要及时同步到服务器端:在服务器端作业渲染生成的文件要及时同步到客户端.即:用户不在需要单独的下载数据,而是在作业运行的同时,渲染就过就会自动同步到客户端,大大缩短了等待时间.当然,无论是在客户端还是在服务端都面

Windows与Linux下文件操作监控的实现

一.需求分析: 随着渲染业务的不断进行,数据传输渐渐成为影响业务时间最大的因素.究其原因就是因为数据传输耗费较长的时间.于是,依托于渲染业务的网盘开发逐渐成为迫切需要解决的需求.该网盘的实现和当前市场上网盘实现有一些的不同,主要在客户端与服务器端的操作需要双向进行,即:用户在客户端的操作需要及时同步到服务器端:在服务器端作业渲染生成的文件要及时同步到客户端.即:用户不在需要单独的下载数据,而是在作业运行的同时,渲染就过就会自动同步到客户端,大大缩短了等待时间.当然,无论是在客户端还是在服务端都面

linux编程---文件操作

文件操作的系统函数: open函数,close函数,read函数,write函数,getcwd函数,access函数,stat函数,fstat函数,getcwd函数 函数原型: 1:open函数 功能:打开现有的文件,或者创建新文件并打开:,成功返回值是文件标识号,失败为-1 参数:parhname是文件路径,flag标识可取如下值 mode表示文件访问权限 2:close函数 功能:关闭文件操作,返回值表示是否成功 参数:文件标识号 3:read函数 功能:读取文件内容,返回值表示实际读取内容

linux之文件操作和权限

文件查看 cat显示文本 cat [option] ... [file] ... cat -E a.txt # 显示$符号 cat -n a.txt # 对显示的每一行加行号 cat -b a.txt # 对非空行进行编号 cat -s a.txt # 对连续的空行就行压缩 tac倒叙显示 less分屏显示文本或stdin输出 space空格 # 向下翻屏 enter # 向下翻一行 q # 退出 /文本 # 搜索,搜索结果高亮显示 n # 向下找到 N # 向上查找 less是man命令的默认

Linux学习-文件操作

文件操作 pwd 显示当前的目录名称 cd 更改当前的操作目录 cd /path/to/...绝对路径 cd ./path/to/..相对路径 cd ../path/to/..相对路径 cd /root 回到根目录 cd - 返回上一次的目录中 cd ./ .表示当前的目录 cd .. 回到上一级目录 / 和 /root 是不同的目录 / 是根目录 /root 是root用户的家目录 ls 查看当前目录下的文件 常用参数 l 长格式显示文件,以文件名进行排序 a 显示隐藏文件 r 逆序显示,以文

linux 文件操作系统调用

crate:创建文件 open:打开文件 read:读取文件 write:写文件 lseek :设置文件偏移量 access:判断文件 close:关闭文件的读写操作 版权声明:本文为博主原创文章,未经博主允许不得转载.