Linux下的文件IO

IO与操作系统

IO的概念

I/O,即输入/输出(Input/Output),是计算机科学中的一个基本概念,涉及数据在不同实体之间的传输。

基本概念:

  • 输入:指数据进入系统或程序的过程,例如用户通过键盘输入数据,或程序从文件或网络接收数据。
  • 输出:指数据离开系统或程序的过程,例如程序将结果打印到屏幕或写入文件。

I/O设备:

  • 输入设备:如键盘、鼠标、扫描仪、触摸屏、麦克风等,用于向计算机提供输入。
  • 输出设备:如显示器、打印机、扬声器等,用于展示计算机处理后的数据。

I/O操作:

  • 读取:从存储设备或输入设备中获取数据。
  • 写入:将数据发送到存储设备或输出设备。

I/O流:

  • 数据流可以是字节流或字符流,根据处理的数据单位和方式不同而有所区别。

I/O模式:

  • 阻塞I/O:操作必须等待I/O请求完成才能继续执行。
  • 非阻塞I/O:操作不需要等待I/O请求完成,可以继续执行其他任务。
  • 同步I/O:I/O操作由请求它的同一线程执行。
  • 异步I/O:I/O操作由操作系统或其他线程执行,完成后通知请求者。

I/O缓冲:

  • 许多I/O系统使用缓冲区来提高效率,数据先写入缓冲区,再一次性传输到目标设备。

I/O接口:

  • 硬件I/O接口:硬件层面上连接输入/输出设备与计算机的接口,如USB、HDMI等。
  • 软件I/O接口:软件层面上用于执行I/O操作的API或系统调用。

I/O在操作系统中的作用:

  • I/O是操作系统核心功能之一,负责管理计算机硬件设备和软件之间的数据交换。

I/O是计算机系统中不可或缺的一部分,无论是在硬件层面、软件层面还是用户交互层面,都扮演着重要角色。理解I/O的概念对于学习计算机科学和开发高效的应用程序至关重要

操作系统的概念

操作系统(Operating System, OS)是管理计算机硬件资源和提供各种服务的系统软件,它是计算机系统中最基本的软件

操作系统通常包含两种不同的含义

(1)指完整的软件包:包括核心软件与应用软件

应用软件:命令解释器、图形用户界面、文件操作工具与文件编辑器

核心软件:管理和分配计算机资源(CPU,RAM,其它设备),即操作系统核心软件[内核]

(2)专指操作系统核心软件

内核的职责:

1. 系统管理和资源分配:

  • 操作系统负责管理系统资源,包括CPU时间、内存、存储设备、输入/输出设备等。

2. 用户接口:

  • 操作系统提供了用户与计算机交互的接口,如命令行界面(CLI)和图形用户界面(GUI)。

3. 程序执行:

  • 操作系统负责程序的加载、执行和调度。

4. 进程管理:

  • 操作系统管理进程(程序的执行实例)的生命周期,包括创建、调度、同步和通信。

5. 线程管理:

  • 在支持多线程的操作系统中,操作系统还负责管理线程的创建、调度和同步。

6. 内存管理:

  • 操作系统负责内存的分配和回收,包括虚拟内存管理、内存保护和地址转换。

7. 文件系统:

  • 操作系统通过文件系统提供数据的持久存储,管理文件和目录的创建、删除、读写和组织。

8. I/O设备管理:

  • 操作系统管理输入/输出设备,提供设备驱动程序以实现硬件的抽象和控制。

9.系统调用:

  • 操作系统提供了一组系统调用,允许应用程序请求操作系统的服务。

10. 系统启动和关闭:

  • 操作系统管理计算机的启动和关闭过程,包括硬件检测、系统初始化和清理。

11. 网络通信:

  • 操作系统支持网络协议,提供网络通信和数据传输的功能。

12. 安全性:

  • 操作系统提供安全机制,如用户认证、权限控制和数据保护。

13. 错误检测和处理:

  • 操作系统负责检测和处理错误,如硬件故障、程序错误等。

14. 系统维护:

  • 操作系统提供了维护工具,如性能监控、资源管理、软件更新等。

15. 可移植性:

  • 操作系统的设计允许在不同的硬件平台上运行,提供了一定的可移植性。

Linux操作系统结构

Linux操作系统是一种基于Unix的开源操作系统内核

Linux操作系统结构关键组件:

1. 内核(Kernel)

  • 内核是Linux系统的核心,负责管理系统资源,如CPU、内存、I/O设备等。
  • 内核提供了硬件抽象层,为应用程序提供统一的接口。

2. 用户空间(User Space)

  • 用户空间是操作系统中除内核外的所有部分,包括应用程序、库、服务等。
  • 用户空间的程序通过系统调用与内核交互。

3. 系统调用(System Calls)

  • 系统调用是用户空间程序请求内核服务的接口。
  • 常见的系统调用包括文件操作、进程控制、内存管理等。

4. C库(C Library)

  • C库,如glibc,提供了对系统调用的封装,使得程序员可以使用C语言编写的高级函数来执行底层操作。

glibc 是属于 GNU(GNU’s Not unix) 工程的一部分,这个工程当初的目标是为了开发一款完整的操作系统,但在开发过程中将除了 Linux 内核以外的组件都开发完成,由于难度很大,开发周期长,在 1992 年 由 Linus Torvalds 开发出来了 Linux 内核,填补了 GNU 系统的一个重要空白,所有后面将 GNU 组件与 Linux 合并组成现在的 GNU/Linux

glibc 包含 标准 c 库函数集合 和 系统调用

标准的 c 库函数是跨平台的,既可以在 Linux 系统下调用,也可以在 windows 系统下调用

系统调用是 Linux 内核给用户提供的访问接口,但在 glibc 中封装了系统调用接口而形成了 glibc 的库函数.

glibc 库函数主要是封装了系统调用的过程, 相应的系统调用一般实现在 Linux 内核中一般的 glibc 中的库函数都会与系统调用关联,但也有库函数不需要使用系统调用,比如字符串操作函数

 一般分为用户层和内核层

用户层:表示在内核层之上的库与应用程序(app)

内核层:操作系统内核 

用户层与内核层是相辅相成,用户层的应用程序依赖于库或内核,库与内核给应用层提供服务内核通过系统调用来给应用层提供接口

Linux IO设计思想

Linux IO框架基于一切皆文件的思想来设计

目的 : 屏蔽底层不同设备之间的 IO 差异,给应用层提供统一的操作接口

思想 : 即将底层的 IO 操作统一抽象成文件操作,操作系统只需要提供一组文件 IO 操作接口就可以为应用程序提供 IO 服务

文件IO操作

文件描述符(File Descriptors)

  • 在Linux中,所有I/O操作都通过文件描述符进行,它是一个代表打开文件的非负整数。
  • 标准文件描述符包括:stdin(0),stdout(1),stderr(2)。

open函数

open 函数是 POSIX 标准和 Linux 系统中用于打开文件的基本函数。它用于创建或打开一个文件,并返回一个文件描述符,该文件描述符可以用于后续的读写操作

函数原型

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

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

参数说明:

  1. pathname:指向包含文件名的字符串的指针。
  2. flags:文件打开方式的标志位,可以是以下值之一或它们的组合:
    • O_RDONLY:以只读方式打开文件。
    • O_WRONLY:以只写方式打开文件。
    • O_RDWR:以读写方式打开文件。
    • O_CREAT:如果文件不存在,创建文件。如果 O_CREAT 标志被设置,并且文件不存在,open 将创建一个新文件,并可能需要第三个参数来设置文件的权限模式。
    • O_EXCL:与 O_CREAT 一起使用,文件必须不存在。
    • O_TRUNC:如果文件存在,截断文件内容到长度为0。
    • O_APPEND:每次写操作都从文件末尾开始。
  3. ...:可选参数,用于指定文件的权限模式,仅当 O_CREAT 标志被设置时使用。

返回值:

  • 成功时,open 返回非负的文件描述符。
  • 失败时,返回 -1 并设置 errno 以指示错误。

close函数

close 函数是 POSIX 标准和 Linux 系统中用于关闭文件描述符的基本函数。当一个文件描述符不再需要时,使用 close 函数可以释放与该文件描述符关联的所有资源

函数原型

#include <unistd.h>

int close(int fd);

参数说明:

  • fd:文件描述符(file descriptor),一个非负整数,表示要关闭的文件或设备。

返回值:

  • 成功时,close 返回 0。
  • 失败时,返回 -1 并设置 errno 以指示错误

示例代码

#include <stdio.h>
#include <unistd.h>

int main() {
    int fd = open("example.txt", O_RDWR);
    if (fd == -1) {
        perror("Failed to open file");
        return 1;
    }

    // 执行文件操作...

    if (close(fd) == -1) {
        perror("Failed to close file");
        return 1;
    }

    return 0;
}

read函数

read 函数是 POSIX 标准和 Linux 系统中用于从文件描述符中读取数据的基本函数。它允许你从指定的文件描述符中读取一定数量的数据。

函数原型

#include <unistd.h>

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

参数说明:

  • fd:文件描述符,一个非负整数,表示要读取数据的文件或设备。
  • buf:指向缓冲区的指针,数据将被读取到这个缓冲区中。
  • count:要读取的字节数。

返回值:

  • 成功时,read 返回实际读取的字节数,如果达到 EOF(文件末尾),返回 0。
  • 失败时,返回 -1 并设置 errno 以指示错误。

示例代码:

#include <unistd.h>

int main() {
    int fd = open("example.txt", O_RDONLY);
    if (fd == -1) {
        perror("Failed to open file");
        return 1;
    }

    char buffer[1024];
    ssize_t bytesRead = read(fd, buffer, sizeof(buffer));

    if (bytesRead == -1) {
        perror("Failed to read from file");
        close(fd);
        return 1;
    }

    if (bytesRead == 0) {
        printf("End of file reached.\n");
    } else {
        // 处理读取的数据
        printf("Read %ld bytes from file.\n", bytesRead);
    }

    close(fd); // 关闭文件描述符
    return 0;
}

write函数

write 函数是 POSIX 标准和 Linux 系统中用于向文件描述符写入数据的基本函数。它允许你将数据从缓冲区写入到指定的文件或设备中

函数原型

#include <unistd.h>

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

参数说明:

  • fd:文件描述符,一个非负整数,表示要写入数据的文件或设备。
  • buf:指向包含要写入数据的缓冲区的指针。
  • count:要写入的字节数。

返回值:

  • 成功时,write 返回实际写入的字节数。
  • 失败时,返回 -1 并设置 errno 以指示错误

示例代码

#include <stdio.h>
#include <unistd.h>

int main() {
    int fd = open("output.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);
    if (fd == -1) {
        perror("Failed to open file");
        return 1;
    }

    const char *message = "Hello, World!\n";
    ssize_t bytesWritten = write(fd, message, strlen(message));

    if (bytesWritten == -1) {
        perror("Failed to write to file");
        close(fd);
        return 1;
    }

    printf("Wrote %ld bytes to file.\n", bytesWritten);

    close(fd); // 关闭文件描述符
    return 0;
}

lseek函数

lseek 函数是 POSIX 标准和 Linux 系统中用于改变文件描述符的文件位置指针的函数。使用 lseek,你可以查询或修改文件描述符指向的文件中的当前读写位置

函数原型

#include <sys/types.h>
#include <unistd.h>

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

参数说明:

  • fd:文件描述符,一个非负整数,表示要改变位置指针的文件。
  • offset:要移动的字节偏移量。正偏移量表示向前移动,负偏移量表示向后移动。
  • whence:指定 offset 参数的起始位置,可以是以下宏之一:
    • SEEK_SET:文件的开头(这是默认的位置)。
    • SEEK_CUR:当前文件位置。
    • SEEK_END:文件的结尾。

返回值:

  • 成功时,lseek 返回新的文件位置偏移量,从文件开头开始计算。
  • 失败时,返回 (off_t)-1 并设置 errno 以指示错误

示例代码

#include <stdio.h>
#include <unistd.h>

int main() {
    int fd = open("example.txt", O_RDONLY);
    if (fd == -1) {
        perror("Failed to open file");
        return 1;
    }

    // 移动到文件开头的第 10 个字节
    off_t new_pos = lseek(fd, 10, SEEK_SET);
    if (new_pos == (off_t)-1) {
        perror("Failed to seek");
        close(fd);
        return 1;
    }

    // 读取当前位置的 20 个字节
    char buffer[20];
    ssize_t bytesRead = read(fd, buffer, sizeof(buffer));
    if (bytesRead == -1) {
        perror("Failed to read from file");
    }

    close(fd); // 关闭文件描述符
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值