【Linux】基础IO ——中(1)

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

int main()
{
	close(1);
	int fd = open("log.txt", O_WRONLY|O_CREAT, 0644);
	if(fd < 0)
	{
		perror("open");
		return 1;	
	}
	fprintf(stdout, "hello world!: %d\n", fd);
	close(fd);
		
	return 0;
}


close 1 后,1 就不再表示显示器文件,而 open log.txt 后,1 就表示 log.txt 文件,所以 fprintf 并不会往显示器上输出,而是会往 log.txt 里输出,可是 log.txt 中没有内容。通常数据流动过程是:先把语言上的数据写到用户层的缓冲区 ➡ 然后数据通过文件描述符在操作系统内核中,找到自己的 task_struct ➡ 然后通过 task_struct 中的 struct files_struct* files 指针找到 struct files_struct 中 struct files* fd_array[] 以文件描述符为下标的位置 ➡ 然后再通过下标的内容找到要写的 struct_file,并把用户层缓冲区的数据拷贝到内核层缓冲区 ➡ 操作系统再由自己的刷新策略和时机通过磁盘驱动刷新到磁盘设备。注意因为用的是 C,所以这里的用户层缓冲区是 C 提供的,如果是其它语言,那么用的缓冲区就是其它语言提供的。所以之所以操作系统没有由用户层把数据刷新到内核层是因为现在 1 指向的是磁盘文件。显示器是行刷新策略,磁盘是全缓冲策略,这两种策略既可以被用户层采纳,也可以被内核层采纳。

上层只要把数据写到用户层缓冲区中就不用管了,剩下的就由操作系统来完成,所以对用户来讲,就完成了用户层和内核层之间的完全解耦。而用户要自己拷贝数据到内核层,还需要提升权限,效率太低。

所以用户层中存在缓冲区可以让用户和底层之间的差异屏蔽掉,以此来提升效率。同理内核层中存在缓冲区也有着解耦、提高效率的意义。


💦 1.2 测试用例2

#include<stdio.h> 
#include<string.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>

int main()
{
    //c call
    printf("hello printf\n");
    fprintf(stdout, "hello fprintf\n");
    fputs("hello fputs\n", stdout);
    //system call
    const char\* byh = "hello write\n";
    write(1, byh, strlen(byh));
                                       
    fork();

    return 0;                          
}                                      


注意观察,当我们重定向之后,为什么除了write以外都打印了两次呢?
请添加图片描述

我们发现往显示器上输出的结果是合理的,但是往普通文件上输出的结果却很诡异,它输出了 7 条信息,且使用 C 语言接口的都输出了两次,使用系统调用接口的输出了一次。

我们就能理解重定向后,刷新策略由行刷新变为全缓冲,也就是说 fork 时,数据还在 C 缓冲区中,所以重定向后,C 接口的数据输出了两份;而向显示器输出时,因为显示器的刷新策略是行刷新,且这里的每条数据都有 \n,所以每执行完 printf,数据就立马刷新出来,最后 fork 时便无意义了。

而重定向后,系统接口没有受影响的原因是 write 会绕过语言层缓冲区,写到内核层缓冲区,而其实只要是数据都要写时拷贝,但大部分情况只针对用户数据,对于内核数据,数据属于操作系统不会写时拷贝,属于进程会写时拷贝,但这种情况很少考虑,现在我们就认为写时拷贝主要拷贝的是用户数据。

通常我们不建议所语言接口和系统接口混合使用,因为可能会出现一些难以理解的现象。


💨 2. dup2

  • 输出重定向
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>

//输出重定向
int main()
{
    int fd = open("log.txt", O_WRONLY|O_CREAT, 0644);
    if(fd < 0)
    {
        perror("open");
        return 1;
    }

    dup2(fd, 1);//此时再写入就不是标准输出,而是fd 
    const char\* msg = "hello dup2->output\n";
    int i = 0;
    while(i < 5)
    {
        write(1, msg, strlen(msg));
        i++;
    }

    close(fd);

    return 0;
}

请添加图片描述

  • 输入重定向
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>

//输入重定向
int main02()
{
    int fd = open("log.txt", O_RDONLY);
    if(fd < 0)
    {
        perror("open");
        return 1;
    }

    dup2(fd, 0);//此时再读就不是从标准输入读,而是fd
    char buffer[1024];
    ssize\_t sz = read(0, buffer, sizeof(buffer) - 1);
    if(sz > 0)                                       
    {
        buffer[sz] = 0;
        printf("%s", buffer);
    }

    close(fd);

    return 0;
}


请添加图片描述

  • 追加重定向
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>

//追加重定向 
int main03()
{
    int fd = open("log.txt", O_WRONLY|O_APPEND);
    if(fd < 0)
    {
        perror("open");
        return 1;
    }

    dup2(fd, 1);//此时再写入就不是标准输出,而是fd
    const char\* msg = "hello dup2->append\n";
    int i = 0;
    while(i < 5)
    {
        write(1, msg, strlen(msg));
        i++;
    }                                            

    close(fd);

    return 0;                                    
}                                                

请添加图片描述
echo 是一个进程;“hello world” 默认是调用 printf 或 write 往显示器上输出;log.txt 是调用 open 使用 O_WRONLY|O_CREAT 打开;> 是调用 dup2,将默认标准输出 1 的内容改为 log.txt;

<< 就是 dup2(fd, 0),且 open 文件的方式是 O_RDONLY;

>>同 >,都是 dup2(fd, 1),只不过它打开文件的方式是 O_WRONLY|O_APPEND;

将来 fork,创建子进程,子进程会以父进程的大部分数据为模板,子进程进行程序替换时,并不会影响曾经打开的文件,也就不会影响重定向对应的数据结构。


💨 3. 理解文件系统

上文咱们一直在谈论打开的文件,那如果一个文件没有被打开呢?它静静的躺在磁盘上。那咱们现在就要了解一下磁盘上的文件系统。首先要了解磁盘结构,这有助于咱们形象理解“把数据刷新到磁盘”这种话,而不是一听而过。

💦 3.1 了解磁盘结构

请添加图片描述
磁盘是计算机中的一个机械设备。

这个磁盘的盘片就像光盘一样,数据就在盘片上放着,只不过光盘是只读的,磁盘是可读可写的。

机械硬盘的寻址的工作方式:盘片不断旋转,磁头不断摆动,定位到特定位置。

请添加图片描述
类比磁带,我们可以把磁盘盘片想象成线性结构。

站在OS角度,我们就认为磁盘是线性结构,要访问某一扇区,就要定位数组下标LBA(logic block address);要写到物理磁盘上,就要把LBA地址转化成磁盘的三维地址(磁头,磁道,扇区)。这种关系类似于我们之前的虚拟地址空间和物理内存。

请添加图片描述


💦 3.2 inode

为了能解释清楚inode我们先简单了解一下文件系统

在这里插入图片描述
Linux ext2文件系统,上图为磁盘文件系统图(内核内存映像肯定有所不同),磁盘是典型的块设备,硬盘分区被划分为一个个的block。一个block的大小是由格式化的时候确定的,并且不可以更改。例如mke2fs的-b选项可以设定block大小为1024、2048或4096字节。而上图中启动块(Boot Block)的大小是确定的

  • Block Group:ext2文件系统会根据分区的大小划分为数个Block Group。而每个Block Group都有着相同的结构组成。政府管理各区的例子
  • 超级块(Super Block):存放文件系统本身的结构信息。记录的信息主要有:bolck 和 inode的总量,未使用的block和inode的数量,一个block和inode的大小,最近一次挂载的时间,最近一次写入数据的时间,最近一次检验磁盘的时间等其他文件系统的相关信息。Super Block的信息被破坏,可以说整个文件系统结构就被破坏了
  • GDT,Group Descriptor Table:块组描述符,描述块组属性信息,有兴趣的同学可以在了解一下
  • 块位图(Block Bitmap):Block Bitmap中记录着Data Block中哪个数据块已经被占用,哪个数据块没有被占用
  • inode位图(inode Bitmap):每个bit表示一个inode是否空闲可用。
  • i节点表:存放文件属性 如 文件大小,所有者,最近修改时间等
  • 数据区:存放文件内容

将属性和数据分开存放的想法看起来很简单,但实际上是如何工作的呢?我们通过touch一个新文件来看看如何工
作。

最全的Linux教程,Linux从入门到精通

======================

  1. linux从入门到精通(第2版)

  2. Linux系统移植

  3. Linux驱动开发入门与实战

  4. LINUX 系统移植 第2版

  5. Linux开源网络全栈详解 从DPDK到OpenFlow

华为18级工程师呕心沥血撰写3000页Linux学习笔记教程

第一份《Linux从入门到精通》466页

====================

内容简介

====

本书是获得了很多读者好评的Linux经典畅销书**《Linux从入门到精通》的第2版**。本书第1版出版后曾经多次印刷,并被51CTO读书频道评为“最受读者喜爱的原创IT技术图书奖”。本书第﹖版以最新的Ubuntu 12.04为版本,循序渐进地向读者介绍了Linux 的基础应用、系统管理、网络应用、娱乐和办公、程序开发、服务器配置、系统安全等。本书附带1张光盘,内容为本书配套多媒体教学视频。另外,本书还为读者提供了大量的Linux学习资料和Ubuntu安装镜像文件,供读者免费下载。

华为18级工程师呕心沥血撰写3000页Linux学习笔记教程

本书适合广大Linux初中级用户、开源软件爱好者和大专院校的学生阅读,同时也非常适合准备从事Linux平台开发的各类人员。

需要《Linux入门到精通》、《linux系统移植》、《Linux驱动开发入门实战》、《Linux开源网络全栈》电子书籍及教程的工程师朋友们劳烦您转发+评论

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以点击这里获取!

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值