Linux文件基础I/O(下)

简介: Linux文件基础I/O

首先,进程拥有独立性,文件表也会拷贝父进程一份,但是文件是不会被拷贝的,也就是说子进程重定向是更改子进程的文件表,并不会影响父进程的。

并且,程序替换的时候也不会影响重定向打开的文件,因为程序替换替换的是程序的代码,而内存中的PCB,文件表,文件,都属于内核数据结构,就像进程的替换不会影响PCB内容的变化,也不会影响pid,ppid一样。

这样就完成了。

为什么linux下一切皆文件?

比如一些硬件,他们有自己的内核数据结构,他们每个都有自己的读写方法(键盘没有写功能,那就指向空),每种硬件读写方式都是不同的。

那么既然是不同的数据结构,怎么进行管理呢?

这时候就会定义一个结构体,里面记录硬件的数据,也能调用对应硬件的读写接口。

file是链接起来的,先描述,再组织。

所以操作系统看来,只需要调用file就可以了,所以说linux下皆文件。

那么,上面说到重定向的时候,为什么我们子进程退出时关闭了一个文件,按理来说父进程也会关闭文件,但是并没有,因为有一个叫做引用计数:

在结构体中有一个专门计数有多少个指针指向这个位置,如果这个数为0,文件就会关闭,如果不为0,即便是子进程关闭文件也就等于这个数减一而已。

因为用户要关闭文件和打开文件,只是我们去告诉操作系统我们要这么去做而已,剩下的就让操作系统实现具体内容。

缓冲区

首先来看一段代码:

打印正常

重定向正常

这时我加了一个fork创建子进程。

打印正常

这个内容是意料之外的。

为什么要有缓冲区

举个例子,我们古代如果普通人想给别人送东西,可能就需要自己一个人去送,费时费力,但是现代有快递站,所以就不用自己人力送了。

在内存中进程也是一样的,需要与外设有接触,但是外设的I/O特别慢,这时缓冲区就可以帮我们快速的与外设传递数据了。

缓冲区的本质就是一段内存!

缓冲区对应的刷新策略

缓冲区刷新也不是随意的刷新,而是根据外设去决定怎样去刷新的。

1.立即刷新,其实就和无缓冲一样。

2.行刷新,行缓存,这个就是相对应显示器,主要是针对人类做使用的,因为我们平时看文字都是一行一行从左到右去读,所以他就是一行一行刷新的,

3.缓冲区满,全缓冲,磁盘文件就是这样的,这个效率也是最快的,因为从进程中拷贝数据到传给外设,一次假设需要10s。

那么0.1s是在从进程拷贝数据到缓冲区,剩下时间就是缓冲区刷新到外设中的时间,也就是说如果进行多次的缓冲刷新,效率不如一次性缓冲刷新。

除了上面的策略,还有两种特殊的情况:

1.用户强制刷新

2.进程退出 ——— 一般都要进行缓冲区刷新

缓冲区的位置在哪里

我们在C语言的时候就一直再说缓冲区,那么它到底在什么位置呢?

刚才打印的代码说明,不在linux内核中,要不然wirte也会被打印两次。

其实我们所说的缓冲区是语言层次的缓冲区!因为在操作系统看来他也只是一块内存而已!

在stdout,stdin,stderr中,因为任何文件中都要去调用这三个,这三个的类型是FILE*,FILE也是一个结构体,里面不仅仅有fd,也有缓冲区!

这就是为什么刷新缓冲区的函数要传入文件指针,因为里面有缓冲区!

Linux中的FILE结构体:

在/usr/include/libio.h

struct _IO_FILE {
int _flags; /* High-order word is _IO_MAGIC; rest is flags. */
#define _IO_file_flags _flags
//缓冲区相关
/* The following pointers correspond to the C++ streambuf protocol. */
/* Note: Tk uses the _IO_read_ptr and _IO_read_end fields directly. */
char* _IO_read_ptr; /* Current read pointer */
char* _IO_read_end; /* End of get area. */
char* _IO_read_base; /* Start of putback+get area. */
char* _IO_write_base; /* Start of put area. */
char* _IO_write_ptr; /* Current put pointer. */
char* _IO_write_end; /* End of put area. */
char* _IO_buf_base; /* Start of reserve area. */
char* _IO_buf_end; /* End of reserve area. */
/* The following fields are used to support backing up and undo. */
char *_IO_save_base; /* Pointer to start of non-current get area. */
char *_IO_backup_base; /* Pointer to first valid character of backup area */
char *_IO_save_end; /* Pointer to end of non-current get area. */
struct _IO_marker *_markers;
struct _IO_FILE *_chain;
int _fileno; //封装的文件描述符
#if 0
int _blksize;
#else
int _flags2;
#endif
_IO_off_t _old_offset; /* This used to be _offset but it's too small. */
#define __HAVE_COLUMN /* temporary */
/* 1+column number of pbase(); 0 is unknown. */
unsigned short _cur_column;
signed char _vtable_offset;
char _shortbuf[1];
/* char* _save_gptr; char* _save_egptr; */
_IO_lock_t *_lock;
#ifdef _IO_USE_OLD_IO_FILE
};

所以说,我们再用文件指针的时候,输入的那些内容都会被封装到对应的文件指针那里,C语言会在合适的时候去刷新这个缓冲区。

那么上面的代码现在也可以进行解释为什么会出现奇怪的内容了。

首先要知道:没用重定向之前,stdout默认使用的是行刷新,在进程fork()之前,三条C函数已经将数据进行打印到显示器上了,这个时候我们的进程内部和FILE内部就没有数据了。

那么:使用重定向之后,写入文件的不是显示器,而是文件,所以就变成全缓存,之前的三天C函数虽然结尾有\n,但是没有写满stdout。

最重要的来了:执行fork的时候,原来的stdout是属于父进程的一部分,然后创建之后整个程序就退出了,之前说过刷新缓冲区的特殊条件,进程退出,并且,刷新缓冲区的时候等于将缓冲区的数据给对应的外设,所以就属于修改内容,那么子进程和父进程只读的时候是不会进行写时拷贝的,但是这里就要谁先退出谁就进行写时拷贝!所以C语言函数的接口就会打印两次!

那么wirte为什么只打印了一次呢?因为上面过程和wirte无关,wirte没有FILE,用的是fd,所以没有C语言提供的缓冲区!

操作系统的缓冲区

C语言层面有对应的缓冲区,系统也有,只不过系统层面的缓冲区是非常复杂的,是在file结构体里面的。

我们再写一个字符串首先拷贝到了语言层面的缓冲区,通过file,wirte写入到了内核缓冲区,至于什么时候从内核缓冲区写到硬盘对应的文件中,这个就需要看操作系统自己决定了(这个和用户毫无关系),有些时候缓存满了之后才会去写到对应的位置。

那么如果操作系统在自己的内核缓冲区又很多数据没来得及写入到指定位置就崩溃了呢?这就会导致数据丢失。

那么有没有什么解决的办法呢?

这个函数调用之后,强制让file对应的内核缓冲区持久到磁盘上!

相关文章
|
3月前
|
Linux 数据安全/隐私保护 Windows
命令方式:window向linux传文件
【10月更文挑战第6天】本文介绍了如何在Linux系统中通过命令`ip a`获取IP地址,并在Windows系统下使用CMD命令行工具和SCP命令实现文件传输。示例展示了如何将D盘中的`mm.jar`文件上传至IP地址为192.168.163.122的Linux系统的/up/目录下,最后在Linux系统中确认文件传输结果。
307 65
|
3月前
|
运维 安全 Linux
Linux中传输文件文件夹的10个scp命令
【10月更文挑战第18天】本文详细介绍了10种利用scp命令在Linux系统中进行文件传输的方法,涵盖基础文件传输、使用密钥认证、复制整个目录、从远程主机复制文件、同时传输多个文件和目录、保持文件权限、跨多台远程主机传输、指定端口及显示传输进度等场景,旨在帮助用户在不同情况下高效安全地完成文件传输任务。
401 5
|
3月前
|
Linux Shell 数据库
Linux文件查找新姿势:总有一种你没见过
【10月更文挑战第18天】文件查找是Linux用户提升工作效率的重要技能。本文介绍了几种实用的文件查找方法,包括基础的`find`命令、快速的`locate`和`mlocate`、高效的`fd`工具、以及结合`grep`和`rg`进行内容搜索。此外,还提供了编写Shell脚本和使用图形界面工具的建议,帮助你更灵活地管理文件。
87 3
|
22天前
|
Linux Shell 网络安全
Kali Linux系统Metasploit框架利用 HTA 文件进行渗透测试实验
本指南介绍如何利用 HTA 文件和 Metasploit 框架进行渗透测试。通过创建反向 shell、生成 HTA 文件、设置 HTTP 服务器和发送文件,最终实现对目标系统的控制。适用于教育目的,需合法授权。
54 9
Kali Linux系统Metasploit框架利用 HTA 文件进行渗透测试实验
|
8天前
|
Ubuntu Linux Go
golang编译成Linux可运行文件
本文介绍了如何在 Linux 上编译和运行 Golang 程序,涵盖了本地编译和交叉编译的步骤。通过这些步骤,您可以轻松地将 Golang 程序编译成适合 Linux 平台的可执行文件,并在目标服务器上运行。掌握这些技巧,可以提高开发和部署 Golang 应用的效率。
67 14
|
7天前
|
存储 NoSQL Linux
linux积累-core文件是干啥的
核心文件是Linux系统在程序崩溃时生成的重要调试文件,通过分析核心文件,开发者可以找到程序崩溃的原因并进行调试和修复。本文详细介绍了核心文件的生成、配置、查看和分析方法
34 6
|
9天前
|
存储 NoSQL Linux
linux之core文件如何查看和调试
通过设置和生成 core 文件,可以在程序崩溃时获取详细的调试信息。结合 GDB 等调试工具,可以深入分析 core 文件,找到程序崩溃的具体原因,并进行相应的修复。掌握这些调试技巧,对于提高程序的稳定性和可靠性具有重要意义。
48 6
|
2月前
|
Linux 开发工具 Perl
在Linux中,有一个文件,如何删除包含“www“字样的字符?
在Linux中,如果你想删除一个文件中包含特定字样(如“www”)的所有字符或行,你可以使用多种文本处理工具来实现。以下是一些常见的方法:
44 5
|
2月前
|
安全 Linux 数据安全/隐私保护
在 Linux 系统中,查找文件所有者是系统管理和安全审计的重要技能。
在 Linux 系统中,查找文件所有者是系统管理和安全审计的重要技能。本文介绍了使用 `ls -l` 和 `stat` 命令查找文件所有者的基本方法,以及通过文件路径、通配符和结合其他命令的高级技巧。还提供了实际案例分析和注意事项,帮助读者更好地掌握这一操作。
55 6
|
2月前
|
Linux
在 Linux 系统中,`find` 命令是一个强大的文件查找工具
在 Linux 系统中,`find` 命令是一个强大的文件查找工具。本文详细介绍了 `find` 命令的基本语法、常用选项和具体应用示例,帮助用户快速掌握如何根据文件名、类型、大小、修改时间等条件查找文件,并展示了如何结合逻辑运算符、正则表达式和排除特定目录等高级用法。
130 6