2024年Linux最全13 Linux下的基础IO_typedef struct _io_file file,2024年最新Linux运维面试题内存优化

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

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

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

2.2.文件描述符fd(file descriptor)

可以看到打开成功后各自的返回值是3 4 5。。。
打开失败返回值则为-1
这些返回值叫做文件描述符,在系统层面是一个整数,从0开始。
其中:0为标准输入,1为标准输出,2为标准错误,这三个默认被打开

文件描述符的本质是数组下标,操作系统为每一个进程维护了一个文件描述符表,该表的索引值都从从0开始的,索引值都有一个指针指向对应的文件:

在这里插入图片描述

一个进程如何通过文件描述符找到文件:当进程执行write(4,"hello",5)时,进程先找到自己的PCB,PCB中包含文件描述符表指针,通过这个指针找到文件描述符表(struct files_struct),然后通过索引下标4找到对应的文件。

所以如果将文件描述符为0(标准输入)的关掉,则分配的下标为0:
在这里插入图片描述

在这里插入图片描述

可以看出文件描述符的分配规则为:在files_struct数组当中,找到当前没有被使用的最小的一个下标,作为新的文件描述符

另外,要记得关闭文件描述符,否则会造成文件描述符泄漏,因为文件描述符表(数组)是有上限的。

2.3.补充内容–函数指针访问硬件

不同的硬件访问方式是不一样的,对于外设访问的方式一般是读和写,但是实现代码是不一样的,此时进程就可以通过函数指针的方式来指向它们各自的实现方法:
在这里插入图片描述

在这里插入图片描述

2.4.重定向的实现原理

重定向有两个操作符分别是>(格式化重定向)和>>(追加重定向),其实现原理就是操作系统内核把标准输出(1)关掉(注意:标准错误没有关闭),然后打开对应的文件,此时该文件的下标就是1,应用层默认输入到下标为1的文件中,所以就输入到该文件中了。至于追加重定向则是在打开的时候加上选项O_APPEND

如果想把标准输出和标准错误都重定向,可以这样写./文件名 > 文件 2>&12>&1的作用是把文件描述符1的内容拷贝到文件描述符2里面,1已经指向了新文件,所以2也指向这个新文件。
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述


三、FILE

文件指针中那个FILE本质是一个结构体,这个结构体中一定是包含文件描述符(fd)的,因为访问文件都是通过fd访问的

下面是FILE的代码:
typedef struct _IO_FILE FILE; 在/usr/include/stdio.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
};

3.1. 行缓冲和全缓冲

对于下面的代码:

#include<stdio.h>
#include<string.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<fcntl.h>
#include<unistd.h>
int main()
{
 const char \*msg0="hello printf\n";
 const char \*msg1="hello fwrite\n";
 const char \*msg2="hello write\n";
 printf("%s", msg0);
 fwrite(msg1, strlen(msg0), 1, stdout);
 write(1, msg2, strlen(msg2));
 fork();
 return 0;
}

在这里插入图片描述

如果对结果重定向,printffwrite(库函数)都输出了2次,而write 只输出了一次(系统调用)。

刷新一般有三种方式: 第一个是对应系统调用而言,后两个是对应库函数而言

  • 无缓冲: 对数据进行操作时,不需要经过缓冲区,直接刷新在文件或显示器上。系统调用(wirte)没有附带缓冲区。
  • 行缓冲: 遇到\n就对缓冲区的数据进行刷新,这是对显示器而言。
  • 全缓冲: 缓冲区满了或者进程退出时才对数据进行刷新,这是对文件采取的刷新策略。

重定向影响了缓冲方式
显示器采用的是行缓冲(遇到\n就刷新)
文件采用的则是全缓冲(缓冲区数据写满才刷新)

没有重定向之前是往显示器写,而重定向则是往文件中写。

  • 一般C库函数写入文件时是全缓冲的,而写入显示器是行缓冲。
  • printf、fwrite库函数会自带缓冲区,当发生重定向到普通文件时,数据的缓冲方式由行缓冲变成了全缓冲
  • 全缓冲进程退出之后,会统一刷新,写入文件当中
  • fork的时候,父子数据会发生写时拷贝,数据被暂存在用户级缓冲区中,因此子进程也就有了同样的一份数据,即产生两份数据。
  • 由于父子进程的缓冲区中有两份一样的数据,所以会刷新两份。
  • write 没有刷新两份,说明没有所谓的用户级缓冲区。

我们这里所说的缓冲区,都是C标准库提供的用户级缓冲区printffwrite是库函数, write是系统调用,库函数在系统调用的“上层”, 是对系统调用的“封装”,因此write 没有用户级缓冲区,而 printffwrite有。
在这里插入图片描述

另外fflush函数也在C标准库中,将用户级的缓冲区往系统中刷新:
在这里插入图片描述

3.2. 在刷新前关闭文件描述符

前面说过,文件采用的是全缓冲,全缓冲进程退出之后,会统一刷新,写入文件当中。所以,在程序退出之前,数据一直都在用户级缓冲区中,如果在文件退出前关闭文件描述符,则不会写入文件中。

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>

int main()
{
	close(1);
	int fd = open("log.txt", O_CREAT|O_WRONLY);

	if (fd < 0){
		perror("open file fail");
		exit(-1);
	}
	
    const char\* msg3 = "hello fwrite\n";
	printf("hello printf\n");
	fprintf(stdout,"hello fprintf\n");
	fwrite(msg3, strlen(msg3), 1, stdout);

	close(fd);

	return 0;
}

在这里插入图片描述

在这里插入图片描述


四、dup 重定向

上面的重定向是将标准输出的fd关掉,然后打开重定向的文件。
而使用dup2函数就不需要关掉标准输出:
在这里插入图片描述

dup函数的作用是,返回一个新的文件描述符(可用文件描述符的最小值)newfd,并且新的文件描述符newfd指向oldfd所指向的文件表项。
比如:
在这里插入图片描述

在这里插入图片描述

文件描述符为1的文件通过dup重定向到文件描述符1上,那么也就相当于文件描述符3对应的文件也是显示器文件,那么向文件描述符3进行write,最终结果也会打印在显示器上。

4.1.使用dup2 完成重定向

dup2有两个参数oldfdnewfdnewfdoldfd的拷贝,将newfd重定向到oldfd,当整个函数调用成功后,会将newfd关掉,然后让newfd指向oldfd。总之,dup2函数的作用就是让newfd重定向到oldfd所指的文件表项上,如果出错就返回-1,否则返回的就是newfd。所以如果想让原本输出到显示器上的数据重定向到文件中,可以这样写:
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

4.2.重定向恢复

在进行重定向后,如果想要恢复到重定向之前的状态,可以在重定向之前用dup函数保留该文件描述符对应的文件表项,然后在需要恢复重定向的时候使用dup2重定向到原来的文件表项,以重定向后恢复标准输出为例,如下所示:

在这里插入图片描述

在这里插入图片描述

4.3.在my_shell中添加重定向功能

其实现原理是在子进程进行程序替换之前将标准输出(1)重定向到打开的文件中。

#include<stdio.h> 
#include<string.h>
#include<unistd.h>
#include<stdlib.h>
#include<ctype.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#define SIZE 256
#define NUM 16 //命令行参数的个数

void redirect(char\* cmd)
{
  int fd=-1;
  int redirect_count=0;//记录>的个数
  char\*file=NULL;
  char\*ptr=cmd;
  while(\*ptr)
  {
    if(\*ptr=='>')
    {
      \*ptr++='\0';
      redirect_count++;
      if(\*ptr=='>')
      {
        \*ptr++='\0';
        redirect_count++;
      }
      while(\*ptr!='\0'&&isspace(\*ptr))//跳过空格
      {
        ptr++;
      }
      file=ptr;//找到文件名
      while(\*ptr!='\0'&&!isspace(\*ptr))//清空文件名后面的空格
      {
        ptr++;
      }
      \*ptr='\0';
      
      if(redirect_count==1){
        //>
        fd=open(file,O_CREAT|O_TRUNC|O_WRONLY,0644);
      }
      else if(redirect_count==2){
        //>>
        fd=open(file,O_CREAT|O_APPEND|O_WRONLY,0644);   
      }
      else{
        //do nothing!
      }
      //文件已经打开,用dup2重定向
      dup2(fd,1);
      close(fd);
    }//end if
    else if(\*ptr=='<')
   { 
      //和重定向>类似
   }
    ptr++;
  }
  
}
int main()                                                                    
{
  char cmd[SIZE];
  const char\* cmd_line="[my\_shell@VM-0-16-centos ~]# ";
  while(1)
  {
    cmd[0]=0;

    printf("%s",cmd_line);
    fgets(cmd,SIZE,stdin);
    cmd[strlen(cmd)-1]='\0'; 
    pid\_t id=fork();
    if(id<0)
    {
      perror("fork error!\n");
      continue;


![](https://img-blog.csdnimg.cn/img_convert/9a8cb5f8c0ec69e6499adead0da6e95b.png)


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

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

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

2.  **Linux系统移植**

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

4.  **LINUX 系统移植 第2版**

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



![华为18级工程师呕心沥血撰写3000页Linux学习笔记教程](https://img-blog.csdnimg.cn/img_convert/59742364bb1338737fe2d315a9e2ec54.png)



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

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

内容简介

====

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



![华为18级工程师呕心沥血撰写3000页Linux学习笔记教程](https://img-blog.csdnimg.cn/img_convert/9d4aefb6a92edea27b825e59aa1f2c54.png)



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

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




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

**[需要这份系统化的资料的朋友,可以点击这里获取!](https://bbs.csdn.net/forums/4f45ff00ff254613a03fab5e56a57acb)**

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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值