linux 下的文件IO基础

2019-09-23

关键字:Linux 文件类型、Linux 文件IO、Linux 标准IO、C 语言的流


 

文件的本质就是一组数据的集合。计算机在日常生活中几乎都是被应用于处理数据方面的。而在 linux 操作系统中,将一切都视为是 “文件”。

 

1、常见文件类型

 

Linux 中常见的文件类型及其标识符有以下七种:

1、常规文件 : -

2、目录文件 : d

3、字符设备文件 : c

一种特殊的文件,代表着一个设备。如:键盘、鼠标、摄像头等。

4、块设备文件 : b

 与上面的字符设备文件一样,都是用于代表一个外部设备的映射。块设备文件与字符设备文件的区别就是传输数据时的大小不同。块设备文件是以固定长度来传送数据的,而字符设备文件则是不定长度的。

5、管道文件 : p

一般用于进程间通信。

6、套接字文件 : s

一般用于进程间通信。既可以实现网络通信,也可以实现本地通信。

7、符号链接文件 : l

类似于快捷链接。分软链接与硬链接两种。

 

2、文件IO的基础概念

 

祼机开发

直接通过代码来操作硬件的开发模式就称为“祼机开发”。换句话说,祼机开发通常是指在无操作系统的情况下进行软件开发的开发模式。同样地,可以引申出“祼机”的概念就是指无操作系统的硬件电路设备系统。

 

系统调用

与上面的祼机开发相对应,另一种操作硬件的方式是要先经过操作系统的。操作系统会把所有硬件设备资源封装起来,不允许应用程序直接操作,而是开放出一些接口给应用程序调用,经由操作系统间接地来控制硬件设备。这些由操作系统开放出来的用于控制硬件的接口就被称为“系统调用”。从另一种角度来说,操作系统的封装对硬件还能起到保护作用。

 

3、标准IO

 

标准IO就是提供给普通应用程序操作文件的支援库。它由 ANSI C标准定义。标准IO已在所有主流操作系统上实现,所以它的兼容性很好。标准IO中将文件封装成 FILE 结构体,又称之为“流”。流严格来讲需要区分“二进制流”与“文本流”。由于操作系统与硬件设备的通信是需要一定的资源开销的,为了提升运行效率、节约资源,标准IO设置了缓冲机制。所谓的缓冲就是指应用程序在进行读写操作时,先将数据暂存于内存中,当达到既定的条件后才将这些数据一起与硬件进行通信。

标准IO的缓冲机制有三种:

1、全缓冲;

将所有IO数据都暂存于内存中,直至既定的内存空间耗尽才发生实际的系统调用。

2、行缓冲;

将所有的IO数据都缓冲到内存中,直至遇到 '\n' 字符才发生实际的系统调用。我们的标准输入输出( scanf(), printf() )就是行缓冲模式的,只要在调用时没有遇到换行符,就一直将数据缓存在内存中,除非程序结束运行。我们有时会遇到程序在运行过程中发生段错误时,在错误之前有些打印没有打出来的情况,这就是因为这些要打印的数据被缓存到内存中了,在等待换行符的过程中遇到段错误强制退出,就导致了打印信息没打出来。

3、无缓冲。

如题。

 

标准IO预定了三个流,它们会在程序运行时自动打开,它们的名称与进程号分别为:

1、标准输入流 : 0 :STDIN_FILENO :stdin

2、标准输出流 : 1 :STDOUT_FILENO :stdout

3、标准错误流 : 2 :STDERR_FILENO :stderr

 

标准IO中操作文件流的方式:

打开一个流:

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

 

使用 fopen 创建的文件的权限默认是 0666,但是实际生成的文件的权限还要考虑到 Linux 中的 umask 才能最终决定。

 

关闭一个流:

int fclose(FILE *stream);

当关闭成功的时候会返回值 0。否则会返回 EOF,并设置相应的 errno。

当一个流执行 fclose 时,它会自动将缓存中的所有数据都刷写到对应硬件中,并释放缓冲区。

当一个程序正常终止时,会自动关闭所有打开的流。

 

处理错误流:

标准IO中预定义了一个整型变量用于指示不同类型的错误,它就是 errno 整型变量。如果我们需要在应用程序中引用到这个 errno,则可以进行如下声明:

extern int errno;

还有一个可以打印错误信息的输出函数接口:void perror(const char *msg); 这个函数会首先打印出参数 msg,然后再打印出当前的 errno 及其对应的错误信息。可以说是错误指示利器了。

还有一个函数,它可以返回指定错误号所代表的意思: char *strerror(int errno);

 

标准IO中操作对象流的方式:

所谓的操作对象流其实就是按既定的格式从流中读取或写出数据。这个既定的格式我们可以简单理解成就是一个自定义的结构体。通过这种方式可以很方便地按照任意需求从流中读写数据。

 

这种操作对象流的函数主要有两个:

size_t fread(void *ptr, size_t size, size_t n, FILE *fp);

size_t fwrite(const void *ptr, size_t size, size_t n, FILE *fp);

这两个函数仅对第二、第三个参数作一下解释。第二个参数就是由我们自定义的“结构体”,其实也就是一次要读取多少个字节的数据的意思。通常可以使用 sizeof(xxx) 来代替。而第三个参数就是我们希望读取多少个 size_t 的数据,通常使用 sizeof(xxx)*n 来代替。这两个函数的返回值都是实际进行读或写的数据量,即实际进行了多少个 size_t 字节的数据的读写。当读写出错时会返回 EOF,当读写达到末尾时返回 0。

 

刷新流:

前面有提到,标准IO的输入与输出都是有缓冲机制的,只有在流数据达到既定的条件时才会发生实际的系统调用过程。但标准IO同时也提供了供应用程序手动将缓冲区中的数据刷写到系统调用的接口。它就是

int fflush(FILE *fp);

当我们调用这个函数时,处于写缓冲区中的数据就会被立即刷写到系统调用中去。刷写成功时返回值 0,失败时返回值 EOF。同时要注意,这个刷写只对写缓冲有效。

 

如果我们非得刷新读缓冲区,那么可以通过 fgetc 函数读取到一个无用的地址中消费掉来曲线实现。

 

定位流:

标准IO提供的用于定位流的函数有三个:

long ftell(FILE *stream);

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

long rewind(FILE *stream);

ftell 就是返回指定流当前的读写位置。

fseek 则是将指定流的读写位置定位到 whence 位置中进行 offset 偏移后的位置处。whence 参数里可以任意填写整数,也可以使用标准IO中提供的三个宏:SEEK_SET, SEEK_CUR, SEEK_END。这三个宏分别表示流的起始位置、流的当前位置、流的结尾位置。offset 可正可负,用于控制往前往后偏移。

rewind 就是将指定流的读写位置直接定位到起始位置。

 

检测流:

标准IO提供了两个函数用于给应用程序判断指定的流是否出错或到达末尾。

int ferror(FILE *steam);

int feof(FILE *stream);

这两个函数的返回值都是一个 “逻辑值”,当返回值 1 时表示逻辑真,返回值 0 时表示逻辑假。

 

格式化输出:

int printf(const char *fmt, ...);

int fprintf(FILE *stream, const char *fmt, ...);

int sprintf(char *s, const char *fmt, ...);

第一个函数最常用了,将指定格式化的字符串输出到标准输出流里。

第二个函数其实是第一个函数的升级版,它将输出流的控制权交由应用程序来决定。它的作用是将格式化后的字符串输出到指定的流里。

第三个函数则是将格式化的字符串输出到内存中的字符串中。

 

4、文件IO

 

文件IO是一种使用更广泛的IO接口。文件IO是遵循 POSIX 协议的一组函数,它的使用范围更广泛,能够支持多种文件格式。文件IO的核心概念是“文件描述符”,即 fd。

 

在 Linux 系统中,标准IO只是文件IO的一个分支而已。

 

文件IO的接口都被封装在 fcntl.h 中。

 

打开流:

int open(const char *path, int flags, ...);

当打开成功时返回文件的描述符号。出错时返回 EOF。

这个函数的参数是可变的,参数个数为 2 个或 3 个。当打开一个文件时,只需要使用到前面两个参数。当创建文件时,则需要使用到三个参数。

flags 参数一般使用接口中定义的宏,可选的类型有:

O_RDONLY : 只读

O_WRONLY :只写

O_RDWR : 读写

O_CREAT : 如果文件不存在就创建一个新的文件。使用这个参数时必须要填写第三个参数。

O_EXCL : 用于测试指定文件是否存在。通常与 O_CREATE 配合使用。当新建文件时文件已存在,则会设置错误信息 errno。

O_NOCTTY : 

O_TRUNC : 如果文件已存在,则打开时先删除掉已有数据。

O_APPEND : 追加。

第三个参数是一个整型,使用文件权限表示法来表示,如 0666, 0777, 0755 等。

 

关闭流:

int close(int fd);

执行成功时返回值 0,失败时返回值 EOF。

 

读写流:

#include <unistd.h>

ssize_t read(int fd, void *buf, size_t count);

不解释了。返回值是成功读取到的字节数。当返回值为 0 时表示读到末尾。出错时返回 EOF。

 

#include <unistd.h>

ssize_t write(int fd, void *buf, size_t count);

不解释了。

 

定位:

#include <unistd.h>

off_t lseek(int fd, off_t offset, intt whence);

与标准IO中的定位接口类似。这个函数是定位当前的读写位置。执行成功时返回的就是指定文件描述符下的当前的读写位置。出错时返回 EOF。

 

访问目录:

#include <dirent.h>

DIR *opendir(const char *path);

DIR 是一个结构体类型,描述的是打开的目录文件类型。打开成功时返回的是相应的目录流指针,失败时返回的是 NULL。

 

#include <dirent.h>

struct dirent *readdir(DIR *dirp);

读取某个目录下的内容。执行成功时返回的是目录项内容。这个函数一次只返回一个目录项,这就意味着通常需要在一个循环中多次调用这个函数,直至这个函数返回 NULL 时才表示读完了目录中的所有内容。

 

#include <dirent.h>

int closedir(DIR *dirp);

不解释。

 

修改文件的访问权限:

#include <sys/stat.h>

int chmod(const char *path, mode_t mode);

int fchmod(int fd, mode_t mode);

两个函数的功能完全一样。都是修改指定文件的访问权限。

 

获取文件的属性:

#include <sys/stat.h>

int stat(const char *path, struct stat *buf);

int lstat(const char *path, struct stat *buf);

int fstat(int fd, struct stat *buf);

第一个函数返回的是实际文件的属性,即如果传入的文件是一个符号链接,那么返回的是这个符号链接指向的真实文件的属性。

第二个函数则可以返回符号链接本身的属性,当然也可以获得普通文件的属性。

第三个函数与第一个功能一样,只是用法不同。

 

关于 struct stat 结构体:

它就是专用于存放文件属性的结构体类型。它的结构成员有:

mode_t st_mode -- 用于存放类型和访问权限。

uid_t st_uid -- 文件所有者的ID。

uid_t st_git -- 文件所有者所属组的ID。

off_t st_size -- 文件的大小。

time_t st_mtime -- 文件最后修改时间。

...

 

关于文件类型 mode_t 结构体:

这个结构体是一个 32 位的整型变量。关于文件类型的记录应该按以下规则计算:

st_mode & 0170000

S_ISREG -- 0100000

S_ISDIR -- 0040000

S_ISCHR -- 0020000

S_ISBLK -- 0060000

S_ISFIFO -- 0010000

S_ISLNK -- 0120000

S_ISSOCK -- 0140000

 

文件访问权限是保存在低 8 位的。

 


 

转载于:https://www.cnblogs.com/chorm590/p/11565956.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Linux IO 模型是指 Linux 操作系统中的 IO 处理机制。它的目的是解决多个程序同时使用 IO 设备时的资源竞争问题,以及提供一种高效的 IO 处理方式。 Linux IO 模型主要分为三种:阻塞 IO、非阻塞 IOIO 多路复用。 阻塞 IO 指的是当程序进行 IO 操作时,会被挂起直到 IO 操作完成,这种方式简单易用,但是对于高并发环境不太适用。 非阻塞 IO 指的是程序进行 IO 操作时,如果无法立即完成,会立即返回一个错误码,程序可以通过循环不断地进行 IO 操作来实现轮询的效果。非阻塞 IO 可以提高程序的响应速度,但是会增加程序的复杂度。 IO 多路复用指的是程序可以同时监听多个 IO 设备,一旦有 IO 事件发生,就会立即执行相应的操作。IO 多路复用可以提高程序的效率,但是需要程序员手动编写代码来实现。 Linux IO 模型还有其他的实现方式,比如信号驱动 IO 和异步 IO 等。但是这些方式的使用比较复杂,一般不常用。 ### 回答2: Linux中的IO模型是指操作系统在处理输入输出的过程中所遵循的一种方式。它主要包括阻塞IO、非阻塞IO、多路复用IO和异步IO四种模型。 阻塞IO是最简单的IO模型,当一个IO操作发生时,应用程序会被阻塞,直到IO操作完成才能继续执行。这种模型的特点是简单直接,但是当有多个IO操作时会造成线程的阻塞,影响系统的性能。 非阻塞IO是在阻塞IO基础上发展而来的,应用程序在发起一个IO操作后可以继续执行其他任务,不必等待IO操作的完成。但是需要通过轮询来不断地检查IO操作是否完成,效率相对较低。 多路复用IO使用select、poll、epoll等系统调用来监听多个IO事件,当某个IO事件就绪时,应用程序才会进行读写操作,避免了前两种模型的效率问题。多路复用IO模型适用于连接数较多时的场景,如服务器的网络通信。 异步IO是最高效的IO模型,应用程序发起一个IO操作后,立即可以执行其他任务,不需要等待IO操作的完成。当IO操作完成后,操作系统会通知应用程序进行后续处理。异步IO模型常用于高吞吐量、低延迟的应用,如高性能服务器和数据库等。 总之,Linux IO模型提供了多种不同的方式来处理输入输出,每种模型都有其适用的场景和特点。选择合适的IO模型可以提高系统的性能和效率。 ### 回答3: Linux IO模型是指操作系统中用于处理输入输出操作的一种方法或机制。在Linux中,常见的IO模型有阻塞IO、非阻塞IOIO多路复用和异步IO。 阻塞IO是最基本的IO模型,当应用程序发起一个IO请求时,它将一直阻塞等待直到IO操作完成,期间无法做其他任务。虽然简单易用,但是对资源的利用不高。 非阻塞IO在发起一个IO请求后,不会阻塞等待IO操作完成,而是立即返回并继续做其他任务。应用程序需要不断地轮询IO操作状态,直到操作完成。由于需要不断轮询,对CPU的占用较高,但可以提高资源的利用率。 IO多路复用是通过一个线程同时监听多个IO事件,从而实现并发处理多个IO操作。在IO多路复用模型中,应用程序不需要进行轮询,而是通过调用select、poll或epoll等系统调用监听多个文件描述符的IO事件。这样可以在单个线程中处理多个IO操作,提高并发性能。 异步IO模型在发起一个IO请求后,应用程序不需要等待IO操作完成,而是继续做其他任务。当IO操作完成后,操作系统会通知应用程序。异步IO模型需要操作系统的支持,效率较高,但实现较为复杂。 通过选择合适的IO模型,可以根据不同的应用场景来提高IO操作的效率和性能。例如,对于需要同时处理大量连接的服务器应用,IO多路复用是一种常见的选择;而对于需要处理大量IO操作的高性能服务器,则可以考虑使用异步IO模型。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值