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

第三章    文件操作

3.1 linux文件结构

与UNIX一样,linux环境中的文件具有特别重要的意义,因为它们为操作系统服务和设备提供了一个简单而一致的接口。在linux中,一切都是文件。

这意味着,通常程序可以像使用文件那样使用磁盘文件、串行口、打印机等等。

目录也是文件,但它是一种特殊类型的文件。在现代UNIX(包括linux)版本中,即使是超级用户可能也不再被允许直接对目录进行写左操作了。所有用户通常都使用上层的opendir/readdir接口来读取目录,而无需了解特定系统中目录实现的具体细节。

3.1.1 目录

文件,除了本身包含的内容以为,它还有一个名字和一些属性,即”管理信息“,包括我呢见的创建/修改日期和它的访问权限。这些属性被保存在文件爱你的inode(节点)中,它是文件系统中的一个特殊的数据块,它同时还包含文件的长度和文件在磁盘上的存放位置。系统使用的是文件的inode编号,目录结构为文件命名仅仅是为了便于人们使用。

目录是用于保存其他文件的节点号和名字的文件。目录文件中的每个数据项都是指向某个文件节点的链接,删除文件名就等于删除与之对应的链接。文件的节点号通过ln -i命令查看。

删除一个文件时,实质上是删除了该文件对应的目录项,同时指向该文件的链接数减1.该文件中的数据可能仍然能够通过其他指向同一个文件的链接访问到。如果指向某个文件的链接数变为0,就表示该节点以及其指向的数据不再被使用,磁盘上的相应位置就被标记为可用空间。

根目录中通常包含用于存放系统程序(二进制可执行文件)的/bin,用于存放系统配置文件的/etc和用于存放系统函数库的/lib。代表物理设备并未这些设备提供接口的文件通常会存放在/dev。

3.1.2 文件和设备

硬件设备在linux中通常也被表示(映射)为文件。

UNIX和linux中比较重要的设备文件有3个:/dev/console,/dev/tty和/dev/null。

1./dev/console

系统控制台,错误信息和诊断信息通常被发送这个设备。

2./dev/tty

如果一个进程有控制终端的话,那么特殊文件/dev/tty就是这个控制终端的别名。

虽然/dev/console设备只有一个,但是通过/dev/tty却能够访问许多不同的物理设备。

3./dev/null

/dev/null文件是空设备。所有写向这个文件的输出都将被丢弃,而读这个设备会立刻返回一个文件尾标志,所以在cp命令里面可以把它用作复制空文件的源文件。

创建空文件的另一个方法是使用touch <filename>命令,该命令的作用是改变文件的修改时间,如果指定的文件不存在,就创建它,但该命令不会把有内容的文件变成空文件。

3.2 系统调用和设备驱动程序

只需少量的函数就可以对文件和设备进行访问和控制,这些函数被称为系统调用,由UNIX直接提供,它们也是通向操作系统本身的接口。

操作系统的核心部分,即内核,是一组设备驱动程序。它们是一组对系统硬件进行控制的底层接口。例如,磁带机就有一个与之对应的设备驱动程序,它知道如果启动磁带,如果和对它前后回绕,如何对它读写等,因为磁带是一个顺序存取设备,所以驱动程序并不能直接访问磁带上的数据块,而是必须把它回绕到正确的位置。

为了向用户提供一个一致的接口,设备驱动程序封装了所有与硬件相关的特性,硬件的特有功能通常可通过ioctl系统来系统。

下面是用于访问设备驱动程序的底层函数(系统调用)

open    打开文件或设备

read    从打开的文件或设备中读取数据

write    向文件或设备写数据

close    关闭文件或设备

ioctl    把控制信息传递给设备驱动程序

3.3 库函数

针对输入输出操作直接使用底层系统调用的一个问题是它们的效率非常地:

1.使用系统调用影响系统的性能。与函数调用相比,系统调用的开销要大,因为在执行系统调用时,linux必须从运行用户代码切换到执行内核代码,然后返回用户代码。减少这种开销的一个方法是,在程序中尽量减少系统调用的次数,并且让每次系统调用完成尽可能多的工作。

2.硬件会限制对底层系统调用使用一次所读写的数据块的大小。

使用库函数,可以高效的写任意长度的数据块,库函数则在数据满足数据块长度要求时安排执行底层调用,这样就极大降低了系统调用的开销。

3.4 底层文件访问

每个运行中的程序被称为进程process,它有一些与之关联的文件描述符。这是一些小值整数,可以通过它们访问打开文件或设备。当一个程序开始运行时,它一般有3个已经打开的文件描述符:

0:标准输入

1:标准输出

2:标准错误

3.4.1 write系统调用

系统调用write的作用是把缓冲区buf的前nbytes个字节写入与文件描述符fildes关联的文件中。下面是write系统调用的原型:

#include <unistd.h>

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

返回实际写入的字节数(正常的话)

返回0表示未写入任何数据

返回-1表示write调用中出现了错误

编写程序simple_write.c

3.4.2 read系统调用

系统调用read的作用是:从与文件描述符fildes相关联的文件里读取nbytes个字节的数据,并把它们放到数据区buf中。

#include <unistd.h>

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

返回实际读入的字节数(正常的话)

返回0表示未读入任何数据,已经到达了文件尾。

返回-1表示read调用出现了错误。

编写程序simple_read.c

echo hello there | ./simple_read.exe

./simple_read.exe < draft1.txt

第一次运行程序时,使用echo通过管道为程序提供输入。

第二次运行程序时,通过文件重定向输入。

3.4.3 open系统调用

为了创建一个新的文件描述符,需要使用系统调用open。

#include <fcnt1.h>

#include <sys/types.h>

#include <sys/stat.h>

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

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

简单地说,open建立了一条到文件或设备的访问路径。如果调用成功,它将返回一个可以被read,write和其他系统调用使用的文件描述符。准备打开的文件或设备的名字作为参数path传递给函数,oflags参数用于指定打开文件所采取的动作。

open ("myfile", O_CREAT, S_IRUSR|S_IXOTH_);

它的作用是创建一个名为myfile的文件,文件爱你属主拥有读权限,其他用户拥有执行权限。

3.4.4 close系统调用

使用close调用终止文件描述符fildes与其对应文件之间的关联。文件描述符被释放并能够重新使用。close调用成功时返回0,出错时返回-1

#include <unistd.h>

int close(int fildes);

3.4.5 ioctl系统调用

ioctl调用有点像个大杂烩,它提供了一个用于控制设备及其描述符行为和配置底层服务的接口,终端,文件描述符,套接字甚至是磁带机都可以有为它们定义的ioctl。

#include <unistd.h>

int ioctl(int fildes, int cmd,...);

编写一个底层程序copy_system.c,用来逐个字符地把一个文件复制到另外一个文件。

然后再编写一个改进的程序copy_block.c,通过复制大一些的数据块来改善效率。

3.4.6 其余与文件管理的系统调用

1.lseek系统调用

lseek系统调用对文件描述符fildes的读写指针进行设置。

2.fstat,stat和lstat系统调用

3.dup和dup2系统调用

3.5标准I/O库

标准I/O库及其头文件stdio.h为底层I/O系统提供了一个通用的接口。使用标准I/O库的方式和使用底层文件描述符一样,需要先打开一个文件建立一个访问路径。这个操作的返回值将作为其他I/O库函数的参数。在标准I/O库中,与底层文件描述符相对应的是流,它被实现为指向结构FILE的指针。

3.5.1 fopen函数

fopen函数类似于底层的open系统调用,它主要用于文件和终端的输入输出。如果需要对设备进行明确的控制,最好使用底层系统调用,因为这样可以避免用库函数带来的一些潜在问题,如输入输出缓冲。

#include <stdio.h>

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

fopen打开由filename参数指定的文件,并把它与一个文件流关联起来。mode参数指定文件的打开方式,它取下列字符串中的值:

r只读;w写方式,并把文件长度截短为零;a写方式,新内容追加在文件尾,r+更新方式打开(读和写);w+更新方式打开,并把文件长度截短为零;a+以更新方式打开,新内容追加在文件尾。

fopen在成功时返回一个非空的FILE*指针,失败时返回NULL值,NULL值在头文件stdio.h里定义。

3.5.2 fread函数

fread函数用于从一个文件流里读取数据。数据从文件流stream读到由ptr指向的数据缓冲区里。fread和fwrite都是对数据激励进行操作,size参数指定每个数据记录的长度,计数器nitems给出要传输的记录个数。它的返回值是成功读到数据缓冲区的记录个数。

#include <stdio.h>

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

3.5.3 fwrite函数

fwrite函数从指定的数据缓冲区里取出数据记录,并把它们写道输出流中。

#include <stdio.h>

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

3.5.4 fclose函数

fcloase函数关闭指定的文件流stream,使所有尚未写出的数据都写出。

#include <stdio.h>

int fclose(FILE *stream);

3.5.5 fflush函数

fflush函数的作用是把文件流里的所有未写出数据立刻写出。

#include <stdio.h>

int fflush(FILE* stream)

3.5.6 fseek函数

fseek函数与lseek系统调用对应的文件流函数。它在文件流里为下一次读写操作指定位置。offset和whence参数的汉所以与lseek系统调用一样。

#include <stdio.h>

int fseek(FILE* stream, long int offset, int whence);

3.5.7 fgetc、getc和getchar函数

fgetc函数从文件流取出下一个字符并把它作为一个字符返回。当它到达文件尾或者出现错误时,它返回EOF。

#include <stdio.h>

int fgetc(FILE* stream);

int getc(FILE* stream);

int getchar();

getc函数作用和fgetc一样,getchar函数的作用相当于getc(stdin),它从标准输入里读取下一个字符。

3.5.8 fputc、putc和putchar函数

fputc函数把一个字符写到一个输出流中,它返回写入的值,如果失败,则返回EOF

#include <stdio.h>

int fputc(int c, FILE* stream);

int putc(int c, FILE* stream);

int putchar(int c);

putchar函数相当于putc(c, stdout),它把单个字符写到标准输出。

3.5.9 fgets和gets函数

fgets函数从输入文件流stream里读取一个字符串

#include <stdio.h>

char *fgetc(char* s, int n, FILE *stream);

char *gets(char* s);

fgets把读到的字符写到s指向的字符串里,直到出现下面的某种情况:

1.遇到换行符

2.已经传输了n-1个字符

3.到达文件尾

它会把遇到的换行符也传递到接收字符串里,再加上一个表示结尾的空字节\0。一次调用最多传输n-1个字符,因为它必须把空字节加上以结束字符串。

当成功完成时,fgets返回一个指向字符串s的指针。

如果已经到达文件尾,fgets会设置这个文件流的EOF标识并返回一个空指针。

gets函数类似于fgets,只不过它从标准输入读取数据并丢弃遇到的换行符。它在接收字符串的尾部加上一个null字节。

注意:gets对传输字符的个数并没有限制,所以它可能会溢出自己的传输缓冲区。因此,应该避免使用gets并且用fgets来替代。

时间: 2024-08-05 19:37:29

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

【linux程序设计4th】第三章1

makefile .PHONY:clean all CC=gcc CFLAGS=-Wall -g ###replace your bin BIN=simple_write simple_read copy_system all:$(BIN) %.o:%.c $(CC) $(CFLAGS) -c $< -o [email protected] clean: rm -f *.o $(BIN) simple_write.c /* #include <unistd.h> ssize_t writ

《javascript高级程序设计》第二、三章知识点整理

第二章知识点总结 1.<script>在html中的使用 主要功能:在页面中嵌入javascript代码或包含外部javascript文件. 常用属性: type:用于定义脚步代码的语言类型,默认为text/javascript. src:包含外部域的javascript文件. defer:表示脚本会在整个页面加载完毕之后运行,只对外部文件有效,最好只包含一个延迟脚本. async:脚本在不妨碍其他操作的情况下立即下载(不保证下载文件的顺序). 插入位置:在页面<body>标签页面

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

C语言文件操作(三)

实例3:读写字节文件,每次读入一个缓存里面. #include<stdio.h> #include <stdlib.h> #define MAXLEN 1024 int main() { FILE *fpin ; FILE *fpout; unsigned char buf[MAXLEN]; int c; fpout=fopen("c:\\dest.jpg","wb"); if((fpin=fopen("c:\\test.jpg&q

鸟哥的 Linux 私房菜(服务器) 第三章 局域网络架构简介 第四章 连上 Internet

鸟哥的 Linux 私房菜(服务器) 第三章 局域网络架构简介 第四章 连上 Internet [TOC] 3.1 局域网络的联机 3.1.1 局域网络的布线规划 3.1.1-1 Linux 直接联网-与 PC 同地位 3.1.1-2 Linux 直接联网-与一般 PC 分开网域 3.1.1-3 Linux 直接联网-让 Linux 直接管理 LAN 3.1.1-4 Linux 放在防火墙后-让 Linux 使用 Private IP 3.2 本书使用的内部联机网络参数与通讯协议 3.2.1 联

linux程序设计——文件锁定(第七章)

7.2 文件锁定 这篇为linux的文件锁定,代码在文件锁定代码下载.文件锁定是多用户.多任务操作系统中一个非常重要的组成部分.程序经常需要共享数据,而这通常是通过文件来实现的.因此,对于这些程序来说,建立某种控制文件的方式就非常重要.只有这样,文件才可以通过一种安全的方式更新,或者说,当一个程序正在对文件进行写操作时,文件就会进入一个暂时状态,在这个状态下,如果另外一个程序尝试读这个文件,它就会自动停下来等待这个状态的结束. linux提供了多种特性来实现文件锁定,其中最简单的方式就是以原子操

【Linux】文件操作系统调用

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

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

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

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

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