【Linux】基础IO-上

1、共识原理

1、文件 = 内容 + 属性

2、文件分为打开的文件和没打开的文件

3、打开的文件是谁打开的? 答案是:进程!---本质是研究进程和文件的关系

文件被打开必须先被加载到内存,一个进程可以打开多个文件。因此,在OS内部一定存在大量被打开的文件。OS要管理这些被打开的文件--先组织,再描述---再内核中,一个被打开的文件必须有自己的文件打开对象,包含文件的很多属性。struct XXX{文件属性;struct XXX *next};  对打开文件的管理就是对链表的增删查改。

4、没打开的文件:在哪里放着?我们最关注什么问题?

首先,没有被打开就没有进程去访问它,它就只能静静的呆在存储介质(磁盘)中。没有被打开的文件非常多,文件如何被放置好方便我们进行增删查改。----文件如何存储

2、回顾C文件接口

#include <stdio.h>
#include <string.h>int main()
{FILE *fp = fopen("log.txt", "w");//w方法,向文件写之前会对文件进行清空//a方法,也是写入,但是它的追加写入//打开文件的路径和文件名,当不写路径的时候是在当前路径下新建一个文件吗?if(fp == NULL){perror("fopen");return 1;}const char *massage = "hello Linux message";//strlen(message) + 1 ? 为什么??//不需要+1,因为字符串末尾是\0,是C语言的规定和文件没有关系。 如果+1的话就会将字符串末尾的\0显示的写入到文件中,而且显示出来的是乱码。  因此,写入时只需要将字符串的内容写进去就okfwrite(message, strlen(message),  1, fp);//向文件写入数据 1代表写入的个数。fprintf(fp, "%s: %d\n", message, 1234);fclose(fp);return 0;
}

什么是当前路径?指的是进程的当前路径。当一个进程要运行时,就要直到它是从哪里来的,就是它的可执行程序是从哪里来的,万一要形成临时文件,我要在那个路径下新建。进程基本属性里是包含它的当前路径是什么的。cwd里面包含了当前进程的工作路径。

那么。如果我更改了当前进程的cwd,就可以把文件新建到其他目录。

chdir("新路径");  //修改当前路径

新建文件带路径就按照带的绝对路径来,不带就按进程的相对路径来。

新建文件不用带路径,因为这个进程有当前路径,文件就会被新建到当前路径。

w:写入之前都会对文件进行清空处理。

a方法,也是写入,但是它的追加写入

C语言会默认打开三个 输入输出流 (其实就是文件):

(C程序在默认启动的时候,会打开三个标准输入输出流)
#include<stdio.h>

extern FILE *stdin; //标准输入,,读键盘  键盘设备

extern FILE *stdout;//标准输出,,  显示器文件

extern FILE *stderr;//标准错误,,,显示器文件

C++三个输入输出流:
cin &&  cout  &&  cerr

因此,我们想写入的时候也可以向文件流中直接写入,因为你打开了一个文件,所以直接向文件流写入就可以直接写到文件里。

eg:fwrite(message, strlen(message),  1, stdout);

3、系统文件IO

文件其实是在磁盘上,磁盘是外部设备,访问磁盘文件其实是在访问硬件!

用户是不允许直接访问硬件的,必须要通过操作系统,对硬件进行访问。但是,又因为OS不相信任何人,它必须向用户提供接口,让用户安全的访问硬件。 因此,OS必须提供系统调用。几乎所有的库只要访问硬件设备就必须要进行封装!!

printf/fprintf/fscanf/fwrite/fread/fgets/gets/....库函数  他们的底层都封装了系统调用。

int open(const char *pathname, int flags,  mode_t mode);

第一个参数:文件的路径  绝对相对都可以

第二:权限  flags是int类型,32个比特位。 O_RDONLY 以只读方式打开   O_WRONLY 以写方式打开  O_RDWR以读写方式打开   O_CREAT 打开时不存在创建一个  O_APPEND 以追加的方式打开  上面这些宏只有一个比特位

 flags给我们提供很多的宏,这些宏里面只有一个比特位,这些宏,可以用或组合,然后再进行按位与 找到对应的标志位(即相应的功能),就可以达到想要的效果。(下面的代码有解释)

三:

补充:比特位级别的传参方式(原理):

OS向系统当中传递参数的方式

#include<stdio.h>
#include<unistd.h>
#include<string.h>#define ONE (1<<0)//1  1左移0位
#define TWO (1<<1)//2  1左移1位
#define THREE (1<<2)//4 1左移2位
#define FOUR (1<<3)//8  2^3//只有一个flags,通过传递不同的标志位,来调不同的功能
void show(int flags)
{if(flags&ONE) //按位与flages   内部用按位与的方式进行条件检测printf("hello function1");if(flags&TWO) //按位与flagesprintf("hello function2");if(flags&THREE) //按位与flagesprintf("hello function3");if(flags&FOUR) //按位与flagesprintf("hello function4");}int main()
{show(ONE);show(TWO);show(ONE|TWO);//在外部相当于只有一个比特位移,我们用或组合一次传两个标志位show(ONE|TWO|THREE);show(ONE|TWO|THREE|FOUR):return 0;
}

试着验证一下open函数

#include<stdio.h>
#include<unistd.h>
#include<string.h>//open需要用到的
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>int main()
{umask(0);//设置为0,不然他会默认进去值//O_WRONLY只能写  要是文件不存在就必须用O_CREAT先新建一个,//但是Linux中,新建一个文件必须写清楚权限是什么 不写的话就会出现乱码权限 0666int fd = open("log.txt",O_WRONLY|O_CREAT,0666);//以只写方式打开if(fd < 0)//open 打开失败返回-1{printf("open file error\n");return 1;}const char *message = "hhhhhhhhhhhhhhhhhhhhhhhhhhh";//如果第一次写入上面的,第二次写入aaa  那么结果就是 aaahhhhhhhhhhhhhhhhhhhhhhhh//从开始写, 覆盖前面的  不对其他的做清空//但是c语言用w进行写入,会对之前的做清空write(fd, message, strlen(message));close(fd);return 0
}

 int fd = open("log.txt",O_WRONLY|O_CREAT|O_TRUNC,0666);//以写方式打开,不存在就创建,写入时先进行清空。

 int fd = open("log.txt",O_WRONLY|O_CREAT|O_APPEND,0666);//追加写

printf/fprintf/fscanf/fwrite/fread/fgets/gets/....库函数  他们的底层都封装了系统调用。

FILE *fp = fopen("log.txt", "w"); //fopen是库函数  w是清空

它的底层调用的系统调用是: 

int fd = open("log.txt",O_WRONLY|O_CREAT|O_TRUNC,0666);

FILE *fp = fopen("log.txt", "a");//a 是追加写

它的底层调用的系统调用是:

 int fd = open("log.txt",O_WRONLY|O_CREAT|O_APPEND,0666);//追加写

不同的语言会有不同的文件操作接口,但是它底层对应的原理都是一样的

那么在数据类型上,FILE * 类型和  int 有什么关系呢???

4、访问文件的本质

一个进程可以打开多个文件,那么文件就需要被管理--先组织,再描述。  

int open(const char *pathname, int flags, mode_t mode);

open的返回值类型是int 其实就是数组的下标,  我们可以看到当write的时候, write(fd, message, strlen(message))  我们传入的第一个参数是fd  我们要向哪个文件写入就是通过数组下标找到对应的文件。   (上面的图就可以看出将进程和文件关联起来了)

int fd1 = open("log.txt",O_WRONLY|O_CREAT,0666);
int fd2 = open("log.txt",O_WRONLY|O_CREAT,0666);
int f3d = open("log.txt",O_WRONLY|O_CREAT,0666);
printf("fd1:\n", fd1);
printf("fd2:\n", fd2);
printf("fd3:\n", fd3);

打印结果是 3 4 5  我们自己创建的文件 第一个文件描述符是从3 开始的,  那0 1 2 下标呢??

其实 0 1 2 在进程启动的时候已经默认被打开了     C语言下,

stdin--键盘 (标准输入)           0

stdout--显示器(标准输出)      1

stderr--显示器 (标准错误)      2

#include <sys/stat.h>
#include <fcntl.h>int main()
{const char *msg = "hello Linux\n"write(1, msg, strlen(msg));write(2, msg, strlen(msg));return 0;
}

./myproc

hello Linux

hello Linux

#include <sys/stat.h>
#include <fcntl.h>int main()
{char buffer[1024];//  read并不知道读的是不是字符串ssize_t s = read(0, buffer, sizeof(buffer));//从文件描述符0读 0代表的是键盘文件if(s < 0)  return 1; //读失败了//读成功了//C语言要将读上来的信息当成字符串   read读的是字节 它读的是一个一个的字符放在缓冲区 //所以,你想让它读字符串,你就得加\0buffer[s] = '\0';printf("echo : %s\n", buffer);return 0;
}

./myproc

(等待键盘输入)   aaabbbccc

(打印出) aaabbbccc

那么,进程启动时打开三个标准输入输出流,是C语言的特性吗?? 答案是:不是!是OS的特性!即进程默认会打开键盘、显示器、显示器

那么为什么OS要这样干? 在计算机开机的时候 显示器和键盘就已经被打开了。而且一般编程的时候都需要显示器和键盘,因此就先会被默认打开。  所以,当一个进程运行时,只需要将对应的文件填到0 1 2 下标的指针数组里就ok了。

FILE *fp = fopen("log.txt", "w");  C语言FILE是什么??

其实是C库自己封装的结构体!因为Linux访问文件必须通过文件描述符,所以,这个这个结构体里面肯定封装了文件描述符!!   那么,0 1 2 里面必定封装了文件描述符

#include <sys/stat.h>
#include <fcntl.h>int main()
{printf("stdin->fd: %d\n", stdin->_fileno);printf("stdout->fd: %d\n", stdout->_fileno);printf("stderr->fd: %d\n", stderr->_fileno);return 0;
}

stdin->fd: 0 
stdout->fd: 1
stderr->fd:  2

因此,in out  err 已经默认将0 1 2打开了   因此自己新建的文件只能从3开始

库函数封装了系统调用,包括两个层面的封装,a、库函数封装了系统调用接口 b、数据类型层面,文件FILE *这个类型也必定包含文件描述符。

struct file 结构体里面很多文件属性,另外,它还包含引用计数count。因为一个文件可以被多个进程打开,当flose(1),假如关闭文件描述符为1的文件,那么就对这个文件进行--  将文件描述符表的1下标的指针置为空,判断文件的count是否为0,是0就回收这个struct file.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/56493.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

NVR小程序接入平台/设备EasyNVR多个NVR同时管理的高效解决方案

在当今的数字化安防时代&#xff0c;视频监控系统的需求日益复杂和多样化。为了满足不同场景下的监控需求&#xff0c;一种高效、灵活且兼容性强的安防视频监控平台——NVR批量管理软件/平台EasyNVR应运而生。本篇探讨这一融合所带来的创新与发展。 一、NVR监测软件/设备EasyNV…

mysql 13 MySQL基于规则的优化

01.条件化简 我们编写的查询语句的搜索条件本质上是一个表达式&#xff0c;这些表达式可能比较繁杂&#xff0c;或者不能高效的执行&#xff0c; MySQL的查询优化器会为我们简化这些表达式。为了方便大家理解&#xff0c;我们后边举例子的时候都使用诸如 a 、 b 、 c 之类的简…

shell——正则表达式入门

目录 一、常规匹配 二、特殊字符 ^ $ . * 字符区间 \ 三、示例 shell中总是会需要对文本字符串做各种各样的剪切拼接等操作&#xff0c;除了 basename 和 dirname 这种简单的函数外&#xff0c;还可以用正则表达式&#xff0c;定义模糊匹配的筛选规则 一、常规匹配 管…

Pyqt5设计打开电脑摄像头+可选择哪个摄像头(如有多个)

目录 专栏导读库的安装代码介绍完整代码总结 专栏导读 &#x1f338; 欢迎来到Python办公自动化专栏—Python处理办公问题&#xff0c;解放您的双手 &#x1f3f3;️‍&#x1f308; 博客主页&#xff1a;请点击——> 一晌小贪欢的博客主页求关注 &#x1f44d; 该系列文…

【C++】——list 容器的解析与极致实现

人的一切痛苦&#xff0c;本质上都是对自己的无能的愤怒。 —— 王小波 目录 1、list 介绍 2、list的使用 2.1 list 的构造 2.2 iterator 的使用 2.3 list 的修改 2.4一些特殊接口 2.5 迭代器失效问题 3、实现list 3.1底层结构 结点类 list类 迭代器类 3.2功能接…

【优选算法篇】在分割中追寻秩序:二分查找的智慧轨迹

文章目录 C 二分查找详解&#xff1a;基础题解与思维分析前言第一章&#xff1a;热身练习1.1 二分查找基本实现解题思路图解分析C代码实现易错点提示代码解读 1.2 在排序数组中查找元素的第一个和最后一个位置解题思路1.2.1 查找左边界算法步骤&#xff1a;图解分析C代码实现 1…

git clone报错fatal: pack has bad object at offset 186137397: inflate returned 1

git clone报错fatal: pack has bad object at offset 186137397: inflate returned 1 逐步拷贝 https://stackoverflow.com/questions/27653116/git-fatal-pack-has-bad-object-at-offset-x-inflate-returned-5 https://www.cnblogs.com/Lenbrother/p/17726195.html https://…

在UE引擎中使用spine动画(1)

注意事项&#xff0c;spine的版本必须和UE插件的版本相同。 1.最重要的是“修改骨架名称。&#xff08;影响在UE引擎中的资产名称&#xff09; 2.导出操作&#xff08;把非必要的数据取消掉&#xff0c;可能会影响UE导入&#xff09;。 3.纹理打包&#xff08;一般默认&#…

程序员:数字时代的先锋

随着科技的不断进步&#xff0c;程序员这一职业群体逐渐成为社会中不可或缺的一部分。他们以智慧和汗水为世界带来更多的便捷与创新。今天&#xff0c;我们将庆祝1024程序员节&#xff0c;这是一个向全球程序员们表达敬意和感激的节日。让我们一同走进程序员的内心世界&#xf…

数字分组求偶数和

问题描述 小M面对一组从 1 到 9 的数字&#xff0c;这些数字被分成多个小组&#xff0c;并从每个小组中选择一个数字组成一个新的数。目标是使得这个新数的各位数字之和为偶数。任务是计算出有多少种不同的分组和选择方法可以达到这一目标。 numbers: 一个由多个整数字符串组…

[ 钓鱼实战系列-基础篇-7 ] 一篇文章教会你搭建邮件钓鱼服务器-1

&#x1f36c; 博主介绍 &#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 _PowerShell &#xff0c;很高兴认识大家~ ✨主攻领域&#xff1a;【渗透领域】【数据通信】 【通讯安全】 【web安全】【面试分析】 &#x1f389;点赞➕评论➕收藏 养成习…

js.杨辉三角和分发饼干

1&#xff0c;链接&#xff1a;118. 杨辉三角 - 力扣&#xff08;LeetCode&#xff09; 题目&#xff1a; 给定一个非负整数 numRows&#xff0c;生成「杨辉三角」的前 numRows 行。 在「杨辉三角」中&#xff0c;每个数是它左上方和右上方的数的和。 示例 1: 输入: numRows …

动态规划-子序列问题——300.最长递增子序列

1.题目解析 题目来源&#xff1a;300.最长递增子序列——力扣 测试用例 2.算法原理 1.状态表示 首先创建一个与数组大小相同的dp表&#xff0c;此时dp[i]表示的是&#xff1a;以第i个位置为结尾的所有子序列中最长递增子序列的长度 2.状态转移方程 此时第i个位置一定是最长递…

国家能源集团携手海康威视研发攻克融合光谱煤质快检技术

10月24日&#xff0c;在国家能源集团准能集团黑岱沟露天煤矿&#xff0c;安装于准能选煤厂785商品煤胶带机中部的煤质快检核心设备&#xff0c;正在对当天装车外运的商品煤煤质进行实时检测。仅两分钟后&#xff0c;涵盖发热量、水分、灰分、硫分等多项指标的数据信息已传输到到…

六.python面向对象

学过C或者Java的同学一定了解过面向对象的相关内容&#xff0c;编程语言一般分为两种设计方式&#xff1a;面向对象、面向过程&#xff0c;早期的编程语言多是面向过程的&#xff0c;由多个过程组合在一起&#xff0c;而Python在设计的时候就是一种面向对象的语言&#xff0c;因…

Room新手入门

Room Room 是所谓的 ORM&#xff08;对象关系映射&#xff09;库 依赖 在项目级 build.gradle 文件中&#xff0c;在 ext 代码块中定义 room_version。 ext {kotlin_version "1.6.20"nav_version "2.4.1"room_version 2.4.2 }在应用级 build.gradle…

【JavaEE初阶】深入理解TCP协议中的封装分用以及UDP和TCP在网络编程的区别

前言 &#x1f31f;&#x1f31f;本期讲解关于TCP/UDP协议的原理理解~~~ &#x1f308;上期博客在这里&#xff1a;【JavaEE初阶】入门视角-网络原理的基础理论的了解-CSDN博客 &#x1f308;感兴趣的小伙伴看一看小编主页&#xff1a;GGBondlctrl-CSDN博客 &#x1f525; …

【Spring】关于Spring中aware相关接口的作用

Aware 接口的回调方法是在 Bean 实例化之后调用的。具体来说&#xff0c;这些回调方法是在依赖注入完成后&#xff0c;但在 Bean 完全初始化之前调用的。这是 Spring 容器管理 Bean 生命周期的一部分 完成了属性赋值之后&#xff0c;Spring会执行一些回调&#xff0c;包括&…

java中Set,Map,List集合的比较(不包含增删改查函数方法)

目录 1. 集合的简介2. List3. Set4. Map5. 比较5.1 结构特点5.2 实现类5.3 区别 6. 其他问题6.1 集合与数组的区别6.2 哪些集合类是线程安全的 7. 参考链接 1. 集合的简介 所有的集合类和集合接口都在java.util包下。 在内存中申请一块空间用来存储数据&#xff0c;在Java中集…

C++加载sqlite3数据库文件

db数据库文件简单轻便&#xff0c;形式上可以像excel数据那样&#xff0c;但是解析的时候却有很大的灵活性。使用Python解析db数据已经是很简单的事情&#xff0c;使用专门的数据库可视化工具&#xff0c;通过sql语句查询内容也是有手就行&#xff0c;也许C也不难&#xff0c;但…