关于文件的一些概念

  1. 文件包含文件内容和文件属性
  2. 对文件的操作包括对文件内容的操作和对文件属性的操作,也有可能同时进行对文件内容和文件属性的操作
  3. 打开文件这个动作是指的将文件的属性和内容加载到内存中
  4. 磁盘上并不是所有的文件都会被加载到内存中
  5. 对文件的理解可以分为内存文件和磁盘文件
  6. 通常当我们在对文件进行操作(打开文件,访问文件....)时,实质上是程序加载到内存中变成的进程对文件进行操作
  7. 这里讨论的就是进程和“打开文件”的关系

C语言中的文件操作函数实际上是对系统接口的封装,在向文件写入数据时是对磁盘内进行写入,只有操作系统有资格向硬件磁盘写入。上层(用户)如何调用操作系统呢?要通过相应的系统接口!平时我们没有用到系统接口是因为C语言的函数都经过了封装。

为什么要进行封装?因为如果在程序中直接进行系统调用(linux环境),将代码放入到windows环境下就不会兼容。不具有跨平台特性。

为了更好地了解底层原理,所以我们有必要学一学文件级别的系统接口

1.系统接口

open

返回值小于0,打开失败。

flags代表打开方式,就像C语言中的r,w等等。不过这里要特殊一些。

O_RDONLY(只读), O_WRONLY(只写), O_RDWR(读写),O_APPEND(读写),O_CREAT(没有文件创建文件),O_TRUNC(如果原来文件中有内容,将内容全部清空)等,他们实际上都是宏,是系统传递表计位,是用位图结构进行传递的,每一个宏标记,一般只需要一个比特位是1,并且和其他的宏对应的标记不能相同如下例。所以他们也可以是或的形式。

mode代表文件初始权限。如果被打开的文件是第一次出现,那么要给上初始权限。否则文件的显示权限部分会出现乱码(不等于最终权限)

#include<stdio.h>
#define O_A 0b0001
#define O_B 0b0010
#define O_C 0b0100
#define O_D 0b1000int main()
{int a = 1;if(a==O_A)printf("O_A");else if(a==O_B)printf("O_B");else if(a==O_C)printf("O_C");elseprintf("O_D");
}
int fd = open("text.txt",O_RDONLY)//只读
int fd = open("text.txt",O_WRONLY)//只写
int fd = open("text.txt",O_RDWR)//读写
int fd = open("text.txt",O_RDONLY|O_CREAT)//只读,如果没有文件创建文件
int fd = open("text.txt",O_RDONLY|O_WRONLY|O_CREAT)//读写,如果没有文件创建文件int fd = open("text.txt",O_RDONLY|O_CREAT,0777);//初始权限给的是0777

write

#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<stdio.h>
#include<unistd.h>
#include<string.h>
int main()
{int fd = open("log.txt",O_RDWR|O_CREAT,0666);if(fd<0){perror("open");return 1;}const char* str = "hello world\n";write(fd,str,strlen(str));                                                                                                                                                         printf("%d\n",fd);close(fd);return 0;
}
//这里可以把字符串中的'\0'作为结束标志写入到文件中吗?答案是否定的,因为只有在C语言中
//'\0'是字符串结束的标志,在其他的打开方式下,'\0'可能会被识别成其他的字符!

read

#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<stdio.h>
#include<unistd.h>
#include<string.h>
int main()
{int fd = open("log.txt",O_RDWR|O_CREAT,0666);if(fd<0){perror("open");return 1;}char str[128];ssize_t s= read(fd,str,127);if(s>0){str[s] = '\0';printf("%s",str);                                                                                                                                                                }close(fd);return 0;
}

2.文件描述符

open函数的返回值fd就是文件描述符。每次打开函数后,fd都是从3开始的,那么0,1,2呢?这里可以直接说明:0是标准输入(键盘),1是标准输出(显示器),2是标准错误(显示器),每次都是默认打开的。不禁联想到C语言中的FILE* stdin,FILE* stdout,FILE* stderr。

验证:

linux下一切皆文件

被打开的文件在内存中,进程也在内存中。所以系统在运行过程中,会存在大量被打开的文件。操作系统要对这些文件进行管理。一个进程可以打开多个文件,那么操作系统如何让进程和其打开的文件映射起来呢?

不要有疑问,0,1,2->stdin,stdout,stderr,键盘,显示屏,虽然都是硬件,但是在linux中也可以作为文件管理。是怎样作为文件使用的呢?

文件描述符的分配规则

遍历files_struct的数组,找到一个最小的且未被使用的下标,分配给新的文件。

重定向

引入

先看一个有趣的代码:

#include<stdio.h>
#include<sys/types.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<unistd.h>
int main()
{close(1);int fd = open("log.txt",O_RDWR|O_CREAT,0666);if(fd < 0){perror("open");return 0;}fprintf(stdout,"这是一个进程");fflush(stdout);                                                                                                                                                                      close(fd);
}

最后它并不会在显示器上打印“这是一个进程”,而是将其写入到log.txt文件中。 那么为什么会出现这种现象?那是因为发生了重定向,本来应该是向某个文件输入,但是输入到另一个文件。

dup函数

上图中的所有数据都是内核数据结构,只有(系统调用)操作系统才有权限提供接口修改内核数据结构。最常用的就是dup函数。

比较常用的就是dup2函数,oldfd为原来文件的描述符,newfd为重定向输出的文件描述符。

#include<stdio.h>
#include<sys/types.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<unistd.h>
int main()
{int fd = open("log.txt",O_RDWR|O_CREAT,0666);if(fd < 0){perror("open");return 0;}//和上面的代码是等效的dup2(fd,1);fprintf(stdout,"这是一个进程");fflush(stdout);                                                                                                                                                                      close(fd);
}

理解缓冲区

缓冲区在哪?缓冲区并不在系统提供的接口中,缓冲区是由C语言提供的!被封装到了FILE中。

运行后会发现先打印的是hello write,过了5秒才打印了hello printf,因为hello printf首先被加载到了缓冲区,等到进程结束后才打印到了显示器上,而hello write则是直接被打印了出来,说明系统提供的接口write是没有缓冲区的。缓冲区的存在可以集中处理数据刷新,减少IO的次数,可以提高整机的效率。

在缓冲区刷新之前,关闭文件描述符会怎样?都关闭了门口了,肯定是打印不出来了呀!

什么时候刷新?

常规:无缓冲(立即刷新);行缓冲(逐行刷新)显示器文件;全刷新(缓冲区满,刷新)块设备对应的文件,磁盘文件。

特殊:进程退出;用户强制刷新(fflush(*****))

缓冲区在FILE内部,在C语言中,我们每一次打开一个文件,都要有一个FILE*返回,每个文件都有一个fd和属于他自己的语言级别缓冲区!

为什么会出现这种情况呢?要知道刷新的本质是把缓冲区的数据write到OS内部,清空缓冲区!缓冲区是由自己的FILE内部维护的,是属于父进程内部的数据区域!前面的write是OS直接写到stdout的,所以直接打印。而fprint等是语言级别的接口,他们要先将数据写入到属于自己FILE的缓冲区内等待刷新,而最后的fork()产生了子进程,这时他们的数据代码都是共享的,当父进程结束时,要刷新缓冲区,这时父进程缓冲区的数据要被清除,所以子进程发生写时拷贝要将原来共享的数据(缓冲区中的数据)拷贝到新的区域中,当父进程结束后它的缓冲区内容被刷新到显示器上,随后子进程结束也被刷新到显示器上。

标准错误和标准输出的区别

标准错误和标准输出都是向显示器打印,有什么区别呢?标准错误和标准输出是通过不同的文件描述符打印到显示器上的!

在发生重定向时,没有说明默认就是重定向标准输出,如果要重定向标准错误则要加上2:

意义在哪里?可以区分程序中的日常信息和错误信息!方便查找错误。cout和cerr,printf和perror要分开使用。

如果不想分开这两个内容,想重定向到一个文件中:

先将1重定向到all.txt中,再把1的地址给2,那么2也指向1所指的文件,2也完成了重定向。

Linux---文件描述符相关推荐

  1. [转帖]linux文件描述符文件/etc/security/limits.conf

    linux文件描述符文件/etc/security/limits.conf https://blog.csdn.net/fanren224/article/details/79971359 需要多学习 ...

  2. 玩转Linux文件描述符和重定向

    本文介绍linux中文件描述符与重定向的相关知识,文件描述符是与文件输入.输出相关联的整数,它们用来跟踪已打开的文件.有需要的朋友参考下. 原文出处:http://www.jbxue.com/arti ...

  3. linux文件描述符导致squid拒绝服务

    linux文件描述符导致squid拒绝服务   前几天因工作需要在RHEL4.8上面架设了一个squid双网代理,刚开始测试一切正常,然后就在前台负载均衡服务器把这个代理地址加上,运行一段时间后,客服 ...

  4. linux文件描述符、软硬连接、输入输出重定向

    引用链接:https://blog.csdn.net/qq769651718/article/details/79459346 文件描述符的作用: 文件描述符是linux操作系统中特有的概念.其相当于 ...

  5. linux文件描述符与标识符,文件描述符fd

    这里以问答的方式来讨论这个问题: 1. 文件描述符 fd 和文件指针 FILE *的关系? 文件描述符是什么?我们知道每一个进程都有一个自己的PCB(进程控制块),进程控制块的结构是: struct ...

  6. linux文件 i节点结构,Linux 文件描述符 文件表项 i节点结构

    Linux的VFS(虚拟文件系统)学习起来很痛苦,看源码不太明智,看完分析完就忘且太浪费时间,懂了后也无法应用在实际场合中.所以这里只是讨论下文件描述符,文件表项(file结构体)和inode,理清实 ...

  7. OS / Linux / 文件描述符以及 file 结构体

    零.前言 程序可以理解为硬盘上的普通二进制文件:进程是加载到内存中的二进制文件,除了加载到内存中的二进制文件外,还附有所有对于该二进制文件描述信息的结构体,描述该进程的结构体叫PCB(进程控制块),在 ...

  8. 玩转linux文件描述符和重定向,玩转Linux文件描述符和重定向

    本文介绍linux中文件描述符与重定向的相关知识,文件描述符是与文件输入.输出相关联的整数,它们用来跟踪已打开的文件.有需要的朋友参考下. 原文出处: linux下的文件描述符是与文件输入.输出相关联 ...

  9. linux 文件描述符

    linux中一切皆文件,socket,磁盘,线程,显示器,键盘等操作都是进行文件的操作. 文件类型 标记符 目录(directory) d 字符设备(character) c 块设备(block) b ...

  10. linux文件描述符有什么用,linux上的文件描述符3有什么特别之处?

    我的工作,那将在Linux和Mac OS X上运行的服务器应用程序它是这样的:linux上的文件描述符3有什么特别之处? 启动主要应用 控制器进程的叉 调用lock_down()在控制过程中 再次叉终 ...

最新文章

  1. python获取机器唯一标识_开发中常用工具 - 获取设备的唯一标识、UDID、UUID、keychain保存UUID、判断网络...
  2. C/C++刷题知识点总结
  3. numpy中reshape,multiply函数
  4. Powershell 批量替换文件
  5. 【API进阶之路6】一个技术盲点,差点让整个项目翻车
  6. Mybatis系列全解(六):Mybatis最硬核的API你知道几个?
  7. Debian Gnu/Linux 9 安装remmina软件过程记录
  8. Java面试题以及答案精选(架构师面试题)-Spring专题
  9. 解决MySQL删除和插入数据很慢的问题
  10. JWT教程_2 SpringSecurity与JWT整合
  11. 上传文件插件uploadify应用简单说明
  12. Apache Flink 的迁移之路,2 年处理效果提升 5 倍
  13. MCtalk对话尚德机构:与教研和大数据结合的AI,才是真功夫
  14. 电脑浏览器主页面被恶意劫持无法修改的解决方法
  15. Auto.js逆向分析-提取脚本文件(附源码)
  16. PPPoE拨号以及失败解决思路
  17. 计算机一级解释,独家秘笈计算机一级错题解释.ppt
  18. 回车与换行符的区别及python中使用
  19. 计算机不联网怎么计时,电脑为什么在断网后仍能准确显示时间?
  20. 地图与定位(五)高德地图服务一

热门文章

  1. 安卓手机怎么格式化_换手机了,怎么把安卓手机便签内容快速移到苹果手机里...
  2. 测试工程师如何快速上手新工作
  3. 使用 Packer 为Proxmox 自动化构建映像Ubuntu Server 20.04 虚拟机映像
  4. 基础算法-朴素贝叶斯分类器
  5. html设置盒子边框为齿状,CorelDRAW如何做出锯齿状相框
  6. android studio 项目两个build.gradle的解析
  7. 卡牌大师怎么玩_LOL2020卡牌大师符文出装推荐_2020卡牌大师玩法攻略介绍_求知软件网...
  8. Zabbix客户端的安装过程
  9. Wifi可以自动打开并连接指定的网络(Android)
  10. 西安学计算机的大学排名,西安计算机专业比较好的高校名单