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


函数汇总:

open()、write()、read()、close()

fopen()、fwrite()、fread()、fclose()

一、什么是文件

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,代码如下:

?
123456789101112131415161718192021222324252627282930#include #include #include #include  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代码如下:

?
12345678910111213141516171819202122232425262728#include #include  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等。

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

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

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

参考:

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

《Linux 高性能服务器编程》