文件描述符0,1,2+lseek()+共享文件覆盖解决

news/2024/4/27 19:28:34/文章来源:https://blog.csdn.net/weixin_47173597/article/details/127537329

1.文件描述符0,1,2

  • 程序开始运行时,有三个文件被自动打开,打开时分别使用了这三个文件描述符
  • 依次打开的三个文件分别是 /dev/stdin,/dev/stdout,/dev/stderr

/dev/stdin 标准输入文件

  1. 程序开始运行时,默认会调用open(“/dev/stdin”, O_RDONLY)打开该文件,返回文件描述符是0

  2. /dev/stdin这个文件代表了键盘-----使用0这个文件描述符就可以从键盘输入数据

#include <unistd.h>
#include<stdio.h>
int main(){char buf[30]={0};read(0,buf,30);printf("buf=%s\n",buf);return 0;}

运行结果—自己输入

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qXfNLC5r-1666777566320)(/home/guojiawei/.config/Typora/typora-user-images/image-20221024172559259.png)]

在Linux下,应用程序通过OS API以文件形式操作底层硬件

不管是读键盘,还是向显示器输出文字显示,都是以文件形式来读写的

在Linux下一切皆文件,说的就是这么个意思

scanf下层调用的就是read,read自动使用0来读数据,自然就可以从键盘读到数据。
scanf(“%s”, sbuf) C库函数
|
|
read(0, rbuf, ***)

在scanf函数之前,close(0),会发生错误-------直接不让你输入,scanf底层实现需要调用read

int main(){char buf[30]={0};close(0);scanf("%s",buf);   printf("buf=%s\n",buf);return 0;
}

/dev/stdout:标准输出文件

  1. 程序开始运行时,默认open(“/dev/stdout”, O_WRONLY)将其打开,返回的文件描述符是1
  2. dev/stdout这个文件代表了显示器—通过1这个文件描述符,可以将数据写(打印)到屏幕上显示
write(1, buf, strlen(buf))

buf数据显示到屏幕上的,数据中转过程:

write应用缓存buf ——> open /dev/stdout时开辟的内核缓存——> 显示器驱动程序的缓存——> 键盘

#include <unistd.h>
#include<stdio.h>
int main(){write(1,"hello,world\n",12);return 0;
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-L0FEYXlX-1666777566322)(/home/guojiawei/.config/Typora/typora-user-images/image-20221024174949480.png)]

因为程序开始运行时,就默认打开了代表了显示器/dev/stdout文件,然后1指向这个打开的文件。

printf下层调用的是write函数,write会使用1来写数据,

既然1所指文件代表的是显示器,自然就可以将数据写到显示器了

 int a=100;write(1,&a,sizeof(a));//注意是取地址
//输出是ASCII编码
/*下面是转换成数字*/
int main(){int a=72;write(1,&a,sizeof(a));//注意是取地址char buf[2]={0};buf[0]=a/10+'0';buf[1]=a%10+'0';write(1,buf,2);return 0;
}
//也就是printf通过指定%d、%f等,自动将其换为对应的字符,然后write输出,完全不用自己来转换。

/dev/stderr:标准出错输出文件

默认open(“/dev/stderr”, O_WRONLY)将其打开,返回的文件描述符是2

通过2这个文件描述符,可以将报错信息写(打印)到屏幕上显示

int main(){int a=72;write(2,&a,sizeof(a));//注意是取地址write(2,"hello",5);return 0;
}
1和2啥区别?

使用这两个文件描述符,都能够把文字信息打印到屏幕。如果仅仅是站在打印显示的角度,其实用哪一个文件描述符都能办到。

1、2:有各自的用途

1:专用于打印普通信息
2:专门用于打印各种报错信息

使用write输出时:

  • 如果你输出的是普通信息,就使用1输出。
  • 如果你输出的是报错信息,就使用2输出
int main(){perror("fail");return 0;
}

执行成功,但是:

int main(){close(2);perror("fail");return 0;
}

会出错,close(1)不出错

2.lseek函数

lseek函数是设置偏移量的值,也就是改变文件指针的位置,从而实现对文件的随机存取。

lseek所需的头文件和函数原型

#include <sys/types.h>
#include <unistd.h>off_t lseek(int fd, off_t offset, int whence);
  • offset----偏移量,以字节为单位,可正可负
  • whence----粗定位,有三个符号常量
    • SEEK_SET-----距离文件起始位置
    • SEEK_CUR-----文件当前位置
    • SEEK_END----距离文件结束位置

不过当whence被指定为SEEK_SET时,如果offset被指定为负数的话,是没有意义:
因为已经到文件头上了,在向前移动就越界了,不再当前文件的范围内了

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include<stdio.h>
int main(){int fd=open("mm.txt",O_RDWR|O_CREAT|O_EXCL,0666);//在文件从‘a’写到‘z’for(char i='a';i<='z';i++)write(fd,&i,sizeof(char));lseek(fd,3,SEEK_SET);//偏移文件头char c;read(fd,&c,1);printf("%c",c);close(fd);return 0;
}

返回值

  • 调用成功-------返回当前读写位置相对于文件开始位置的偏移量(字节)
  • 调用失败-------返回-1,并给errno设置错误号

od -c 文件:以字符形式查看文件内容。

3.文件描述符表

当open打开文件成功后,会创建相应的结构体(数据结构),用于保存被打开文件的相关信息,对文件进行读写等操作时,会用到这些信息,这个数据结构就是“文件描述符表”

进程表:task_struct

  • 每一个进程运行起来后,Linux系统都会为其在内存中开辟一个task_struct结构体
  • task_struct专门用于存放进程在运行过程中,所涉及到的所有与进程相关的信息
    其中,文件描述符表就被包含在了task_struct中

  • 这里文件描述符表的fd值存放打开的文件

  • i节点信息就是文件属性信息(ls -lah来查看)

  • 文件长度就是文件大小

文件状态标志

-------就是open文件时指定的O_RDONLY、O_WRONLY、O_RDWR、O_TRUNC、O_APPEND、O_CREAT、O_EXCL、O_NONBLOCK、O_ASYNC等。

-------作用:读写文件时,会先检查“文件状态标志”,看看有没有操作权限,然后再去操作文件

4.共享操作文件

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include<stdio.h>
#include<stdlib.h>   
//把文件名设置成宏
#define FILE_NAME "io.txt"
void print_error(char* str){perror(str);exit(-1);
}
int main(){int fd1=0;int fd2=0;fd1=open(FILE_NAME,O_RDWR);if (fd1==-1) print_error("1 open fail!");fd2=open(FILE_NAME,O_RDWR);   if (fd2==-1) print_error("2 open fail!");int i=0;while(i++<100){write(fd2,"world\n",6);sleep(1);//防止写入速度过快write(fd1,"hello\n",6);}return 0;
}

fd1和fd2会发生相互覆盖的情况

printf(“fd1=%d,fd2=%d\n”,fd1,fd2);-----一个是3,一个是4

在同一进程里面,一旦某个文件描述符被用了,在close释放之前,别人不可能使用,所以指向同一文件的描述符不可能相同

覆盖原因:

正是由于不同的文件描述符,各自对应一个独立的文件表,在文件表中有属于自己的“文件位移量”,开始时都是0。各自从0开始写,每写一个字节向后移动一个字节,他们写的位置是重叠的,因此肯定会相互的覆盖。

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include<stdio.h>
#include<stdlib.h>   
//把文件名设置成宏
#define FILE_NAME "io.txt"
void print_error(char* str){perror(str);exit(-1);
}
int main(){int fd1=0;int fd2=0;fd1=open(FILE_NAME,O_RDWR|O_APPEND);if (fd1==-1) print_error("1 open fail!");fd2=open(FILE_NAME,O_RDWR|O_APPEND);   if (fd2==-1) print_error("2 open fail!");printf("fd1=%d,fd2=%d\n",fd1,fd2);int i=0;while(i++<100){write(fd2,"world\n",6);sleep(1);//防止写入速度过快write(fd1,"hello\n",6);}return 0;
}

注意:两个都得指定O_APPEND,只指向其中一个,依然覆盖

多个进程操作同一个文件

不同进程打开同一文件时,各自使用的文件描述符值可能相等,比如我们例子中的1和2进程,它们open后的描述符就相等。

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>   
//把文件名设置成宏
#define FILE_NAME "io.txt"
void print_error(char* str){perror(str);exit(-1);
}
int main(){int fd1=0;fd1=open(FILE_NAME,O_RDWR|O_TRUNC);if (fd1==-1) print_error("1 open fail!");printf("fd1=%d\n",fd1);int i=0;while(i++<30){write(fd1,"World\n",6);sleep(1);//防止写入速度过快}return 0;
}

file1.c和file2.c只有write语句不同,一个是hello,一个是world.然后各自执行

同样的,指定O_APPEND标志,写操作时,使用文件长度去更新文件位移量,保证各自操作时,都在文件的尾部操作,就不会出现相互覆盖的情况。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2cJQIjuD-1666777566323)(/home/guojiawei/.config/Typora/typora-user-images/image-20221026174521450.png)]

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

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

相关文章

requests模块和openpyxl模块

第三方模块的下载和使用 1,第三方模块就是别人大神们已经写好的模块,功能特别强大。我们如果像使用第三方模块就先要进行下载。下载完成后 才可以在python中直接调用2.下载方式一:pip工具pip工具注意每个解释器都有pip工具 如果我们的电脑上有多个版本的解释器那么我们在使用…

模型交易平台--信用卡客户消费行为分析

业务问题&#xff1a; 据《中国银行卡产业发展蓝皮书&#xff08;2021&#xff09;》显示&#xff0c;截至2020年末&#xff0c;中国信用卡累计发卡量为11.3亿张&#xff0c;其中6个月内有使用记录的活卡为7.4亿张&#xff0c;有近4亿张信用卡处于“睡眠”状态。 睡眠信用卡…

【JUC】12.读写锁与StampedLock[完结]

文章目录1. 什么是读写锁2. 锁的演化历程3. 锁降级4. 锁降级的策略5. StampedLock简介6. StampedLock的特点7. StampedLock之传统读写8. StampedLock之乐观锁9. StampedLock缺点1. 什么是读写锁 读写锁是指ReentrantReadWriteLock类 该类能被多个读线程访问或者一个写线程访问…

第二节:数据类型与变量【java】

目录 &#x1f4c3;前言 &#x1f4d7;1.数据类型 &#x1f4d5;2. 变量 2.1 变量概念 2.2 语法格式 &#x1f4d9;3.整型变量 3.1 整型变量 3.2 长整型变量 3.3 短整型变量 3.4 字节型变量 &#x1f4d8;4.浮点型变量 4.1 双精度浮点型 4.2 单精度浮点型 &#…

拓端tecdat|用Python进行图像模糊处理和特征提取

全文链接&#xff1a;http://tecdat.cn/?p9015 原文出处&#xff1a;拓端数据部落公众号 在本文中&#xff0c;我将带您了解图像处理的一些基本功能。特征提取。但是这里我们需要更深入的数据清理。但是数据清理是在数据集&#xff0c;表格&#xff0c;文本等上完成的。如何在…

vue使用canvas实现手写电子签名;vue使用vue-esign插件实现手写电子签名;H5使用画布签名

功能&#xff1a; 1.兼容 PC 和 Mobile&#xff1b; 2.画布自适应屏幕大小变化&#xff08;窗口缩放、屏幕旋转时画布无需重置&#xff0c;自动校正坐标偏移&#xff09;&#xff1b; 3.自定义画布尺寸&#xff08;导出图尺寸&#xff09;&#xff0c;画笔粗细、颜色&#xff0…

Arduino UNO 可视化GT-24工业级无线透传

Arduino UNO 可视化GT-24工业级无线透传一、前言二、硬件要求三、参数基础四、原理剖析五、透传思路六、程序概要七、arduino使用接线八、成果展示一、前言 无线透传市面上较为常见的是基于蓝牙、esp的多种透传模块&#xff0c;今天介绍的则是用NRF24L01芯片构成的电路。&…

Python第七章作业

实例一: class Geese: 大雁类 def __init__(self,beak,wing,claw): print("我是大雁类!我有以下特征:") print(beak) print(wing) print(claw) def fly(self,state): print(state)**********调用方法**********beak_…

MyBatis中的reflection包(一)ObjectFactory,PropertyTokenizer, Invoker, Reflector

内容概要 reflection是MyBatis关于反射的工具包&#xff0c;是实现其它功能的基石之一。这里我不准备贴上源码然而逐行解释&#xff0c;而是从需求分析的角度来复现。 ObjectFactory 现在有这样的需求&#xff1a;给你一个Class对象&#xff0c;要求你创建它的实例&#xff…

Kong自动注册kong-spring-boot-stater

前言 kong-spring-boot-stater框架是为了解决SpringBoot项目和kong网关的自动注册&#xff0c;虽然Kong网关有提供可视化管理后台的操作界面&#xff0c;但是在多服务、多环境的时候在管理后台挨个配置每个服务节点是比较麻烦的&#xff0c;所以这也是kong-spring-boot-stater…

图形写稿基础,含teaser figure的特殊排版方法

写在前面&#xff1a;这是第一次投稿后针对论文写作部分的总结。需要注意的是&#xff1a;老师提了意见&#xff0c;一定要快速改&#xff0c;否则会很恼人。 1. 图片展示 构图要美观&#xff0c;保证横平竖直&#xff1b;图片中文字保证和文章正文中文字一样大小&#xff1b;…

VUE |“ 登录页面”的权限以及接口问题

目录 一、功能需求 二、前提准备 三、具体实现 一、功能需求 今天写到项目的登录页面&#xff0c;我这边是没有后台数据接口的&#xff0c;所以我们用了Mock模拟了一个假的数据&#xff0c;那么我们应该怎么实现呢&#xff1f;我们先来看一下功能需要。 当我们退出登录…

系分 - 系统可靠性分析与设计

个人总结&#xff0c;仅供参考&#xff0c;欢迎加好友一起讨论 系分 - 系统可靠性分析与设计 考点摘要 可靠性相关基本概念&#xff08;★&#xff09;系统可靠性分析&#xff08;★★&#xff09;软件可靠性设计&#xff08;★★&#xff09; 系统故障类型 系统故障是指由…

09代码

实例1 def division():print(\n==========分苹果了===========\n)apple=int(input(请输入苹果的个数))children=int(input(请输入来了多少个小朋友))result=apple//childrenremain=apple-result*childrenif remain>0:print(apple,个苹果,平均分给,children,个小朋友,每人分…

网络爬虫及openyxl模块

网络爬虫及openyxl模块 一、第三方模块简介 1.第三方模块的用处python之所以在这么多的编程语言中脱颖而出的优点是有众多的第三方库函数,可以更高效率的实现开发2.第三方模块的使用 1.第三方模块必须下载才能使用格式:pip install 模块名 -i 源地址清华大学 :https://pypi.…

【cuda编程】CUDA的运行方式以及grid、block结构关系

文章目录1. CUDA基础知识1.1 程序基本运行顺序1.2 grid与block1.3 dim类型定义2. CUDA的第一个程序3. CUDA线程的组织结构——grid与block关系1. CUDA基础知识 1.1 程序基本运行顺序 一般来说&#xff0c;一个cpugpu的程序运行如下所示&#xff1a; 1.2 grid与block 从GPU至…

网络原理——No.4 传输层_TCP协议中的延迟应答, 捎带应答, 面向字节流与TCP的异常处理

JavaEE传送门JavaEE 网络原理——No.2 传输层_TCP的连接管理 网络原理——No.3 传输层_TCP的滑动窗口, 流量控制与拥塞控制 目录延迟应答捎带应答面向字节流粘包问题TCP 中的异常处理(连接异常)TCP 和 UDP 的应用场景延迟应答 一种提高传输效率的机制, 又是基于流量控制, 来引…

调度线程池ScheduledThreadPoolExecutor源码解析

实现机制分析 我们先思考下&#xff0c;如果让大家去实现ScheduledThreadPoolExecutor可以周期性执行任务的功能&#xff0c;需要考虑哪些方面呢&#xff1f; ScheduledThreadPoolExecutor的整体实现思路是什么呢&#xff1f; 答&#xff1a; 我们是不是可以继承线程池类&am…

docker快速安装redis

一.背景 开发环境中&#xff0c;经常需要redis本地环境&#xff0c;方便开发。准备在本机的虚拟机里面准备一个redis环境。 二.版本信息 操作系统&#xff1a;Windows 10 家庭版 Oracle VM VirtualBox&#xff1a;版本 6.0.10 r132072 (Qt5.6.2) Ubuntu:16.04.6-desktop-a…

STM32CubeMX学习笔记(44)——USB接口使用(HID按键)

一、USB简介 USB&#xff08;Universal Serial BUS&#xff09;通用串行总线&#xff0c;是一个外部总线标准&#xff0c;用于规范电脑与外部设备的连接和通讯。是应用在 PC 领域的接口技术。USB 接口支持设备的即插即用和热插拔功能。USB 是在 1994 年底由英特尔、康柏、IBM、…