Linux文件【系统调用接口及进程中对打开文件的管理操作】详细讲解

news2024/9/21 16:47:20

目录

一、open函数

1.介绍

2.open函数返回值

二、重定向

1.文件描述符的分配规则

2.重定向的本质

3.dup2系统调用

三、C语言库函数中的缓冲区及不同刷新模式

前言:

我们先来简单回顾一下C语言中的文件相关知识

● 打开文件的方式

               r                                       打开文本文件进行读取。流位于文件的开头。
               r+                                     用于读写。流位于文件的开头。
               w                 将文件截断为零长度或创建用于写入的文本文件。流位于文件的开头。

               w+                                    开放用于读写。

               a                  打开以附加(在文件末尾写入)。如果不存在,则创建该文件。流位于文件末尾。
               a+                可读取和附加(在文件末尾写入)。如果不存在,则创建文件。读取的初始文件位置在文件的开头,但输出总是附加到文件的末尾。
● stdin & stdout & stderr
C语言 默认会打开三个输入输出流,分别是 stdin, stdout, stderr
这三个流的类型都是 FILE*, fopen 返回值类型,文件指针

一、open函数

1.介绍

open man open

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
pathname: 要打开或创建的目标文件
flags: 打开文件时,可以传入多个参数选项,用下面的一个或者多个常量进行 运算,构成 flags
mode:权限掩码,对创建的文件进行权限管理。
参数 :
        O_RDONLY : 只读打开
        O_WRONLY: 只写打开
        O_RDWR    : 读,写打开
                              这三个常量,必须指定一个且只能指定一个
        O_CREAT   : 若文件不存在,则创建它。需要使用 mode 选项,来指明新文件的访问权限
        O_APPEND: 追加写
返回值:
        成功:新打开的文件描述符
        失败:-1
open 函数具体使用哪个,和具体应用场景相关,如目标文件不存在,需要 open 创建,则第三个参数表示创建文件 的默认权限 , 否则使用两个参数的 open

2.open函数返回值

在认识返回值之前,先来认识一下两个概念 : 系统调用 库函数
fopen fclose fread fwrite 都是 C 标准库当中的函数,我们称之为库函数( lib )。
而, open close read write 都属于系统提供的接口,称之为系统调用接口
回忆一下我们讲操作系统概念时,画的一张图

系统调用接口和库函数的关系,就如上图所示,所以我们可以认为文件系列的库函数都是对系统调用的封装,方便二次开发,实现代码的跨平台性
open的返回值通常叫做 文件描述符fd,文件描述符就是一个整数
文件描述符 0 & 1 & 2
● Linux 进程默认情况下会有 3 个打开的文件描述符,分别是标准输入 0 , 标准输出 1 , 标准错误 2.
● 0,1,2 对应的物理设备一般是:键盘,显示器,显示器

所以输入输出还可以采用如下方式:

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

int main()
{
    char buf[1024];
    ssize_t s = read(0, buf, sizeof(buf));
    if(s > 0)
    {
        buf[s] = 0;
        write(1, buf, strlen(buf));
        write(2, buf, strlen(buf));
    }
    return 0;
}

那么文件描述符是用来做什么的呢,为什么open函数返回的是文件描述符呢?
我面先看下面图:

根据上面图片,文件描述符就是从0开始的小整数。当我们打开文件时,操作系统在内存中要创建相应的数据结构来描述目标文件。于是就有了file结构体。表示一个已经打开的文件对象。而进程执行open系统调用,所以必须让进程和文件关联起来。每个进程都有一个指针*files, 指向一张表files_struct,该表最重要的部分就是包涵一个指针数组,每个元素都是一个指向打开文件的指针!所以,本质上,文件描述符就是该数组的下标。所以,只要拿着文件描述符,就可以找到对应的文件

那么对于其他系统调用接口read,write等大家直接man学习即可。

二、重定向

1.文件描述符的分配规则,重定向的本质

根据上图,在进程中打开一个文件就会在依次在fd_array指针数组中进行添加创建,我们看下面代码:

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

int main()
{
    int fd = open("myfile", O_RDONLY);
    if(fd < 0)
    {
        perror("open");
        return 1;
    }
    printf("fd: %d\n", fd);
    close(fd);
    return 0;
}

输出结果:fd:3。

关闭 0 或者 2,再看:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main()
{
    close(0);
    //close(2);
    int fd = open("myfile", O_RDONLY);
    if(fd < 0)
    {
        perror("open");
        return 1;
    }
    printf("fd: %d\n", fd);
    close(fd);
    return 0;
}
发现结果是: fd: 0 或者 fd 2 可见,文件描述符的分配规则:在 files_struct 数组当中,找到当前没有被使用的 最小的一个下标,作为新的文件描述符。

2.重定向的本质

那如果关闭 1 呢?看代码:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>

int main()
{
    close(1);
    int fd = open("myfile", O_WRONLY|O_CREAT, 00644);
    if(fd < 0)
    {
        perror("open");
        return 1;
    }
    printf("fd: %d\n", fd);
    fflush(stdout);
 
    close(fd);
    exit(0);
}
此时,我们发现,本来应该输出到显示器上的内容,输出到了文件 myfile 当中,其中, fd 1 。这种现象叫做输出重定向。常见的重定向有 :>, >>, <
那重定向的本质是什么呢?

重定向的本质是在指针数组中将对应下标位置的的指针所指向的内容进行修改,使其指向另一个文件,那么再向原先的文件下标中进行写入操作,操作就会打印到所指向的另一个文件中。

3.dup2系统调用

函数原型:

#include <unistd.h>
int dup2(int oldfd, int newfd);

代码示例:

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

int main() 
{
    int fd = open("log", O_CREAT | O_RDWR,0666);
    
    if (fd < 0) 
    {
        perror("open");
        return 1;
    }
    
    close(1);

    dup2(fd, 1);//将下标为1的数组内容更改成fd所指的内容,是1指向fd所指的文件

    printf("hello Linux!\n");
    
    return 0;
}
输出结果为创建一个log文件,里面包含“hello Linux!”字符串。
printf C 库当中的 IO 函数,一般往 stdout 中输出,但是 stdout 底层访问文件的时候,找的还是 fd:1, 但此时, fd:1 下标所表示内容,已经变成了log文件 的地址,不再是显示器文件的地址,所以,输出的任何消息都会往文件中写 入,进而完成输出重定向。

三、C语言库函数中的缓冲区

● 因为 IO 相关函数与系统调用接口对应,并且库函数封装系统调用,所以本质上,访问文件都是通过 fd 访问的。
● 所以 C 库当中的 FILE 结构体内部,必定封装了 fd。
来段代码在研究一下:
#include <stdio.h>
#include <string.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;
}
运行出结果:
hello printf
hello fwrite
hello write
但如果对进程实现输出重定向呢? ./hello > file , 我们发现结果变成了:
hello write
hello printf
hello fwrite
hello printf
hello fwrite
我们发现 printf fwrite (库函数)都输出了 2 次,而 write 只输出了一次(系统调用)。为什么?大概是和fork有关
● 一般 C 库函数写入文件时是全缓冲的,而写入显示器是行缓冲。
● printf fwrite 库函数会自带缓冲区,当发生重定向到普通文件时,数据 的缓冲方式由行缓冲变成了全缓冲。
● 而我们放在缓冲区中的数据,就不会被立即刷新,甚至是 fork 之后
● 但是进程退出之后,会统一刷新,写入文件当中。
● 但是 fork 的时候,父子数据会发生写时拷贝,所以当你父进程准备刷新的时候,子进程也就有了同样的 一份数据,随即产生两份数据。
● write 没有变化,说明没有所谓的缓冲区。
综上: printf fwrite 库函数会自带缓冲区,而 write 系统调用没有带缓冲区。另外,我们这里所说的缓冲区,都是用户级缓冲区。其实为了提升整机性能, OS 也会提供相关内核级缓冲区,不过不再我们讨论范围之内。 那这个缓冲区谁提供呢? printf fwrite 是库函数, write 是系统调用,库函数在系统调用的 上层 ,是对系统 调用的 封装 ,但是 write 没有缓冲区,而 printf fwrite 有,足以说明,该缓冲区是二次加上的,又因为是 C 语言,所以由 C 标准库提供。

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

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

相关文章

数图亮相第三届中国区域零售创新峰会:共绘零售新蓝图,携手迈向新征程

8月31日&#xff0c;备受瞩目的第三届中国区域零售创新峰会在历史悠久的湖北襄阳圆满落下帷幕。在这场零售行业的盛会上&#xff0c;数图信息科技作为重要参会企业&#xff0c;积极参与其中&#xff0c;与众多行业精英共聚一堂&#xff0c;共同擘画零售业的宏伟蓝图。以下是本次…

C程序设计——指针杂谈0

变量和常量讲的差不多了&#xff0c;这里先把指针再深入理解一下&#xff0c;如果你是C语言初学者&#xff0c;本节可能看不太懂&#xff0c;没关系可以以后再看。 变量 当定义变量的时候&#xff0c;本质是在内存中分配了一段空间&#xff0c;这段空间的大小与变量的类型相关…

GD - EmbeddedBuilder - 给已有工程换MCU

文章目录 GD - EmbeddedBuilder - 给已有工程换MCU概述不行的重现 笔记工程的.gdc文件内容中有MCU型号可以改 给已有工程换MCU的使用场景END GD - EmbeddedBuilder - 给已有工程换MCU 概述 一个现存的EmbeddedBuilder的工程&#xff0c;想换个MCU配置做实验&#xff0c;又不想…

极盾故事|某金融租赁机构应用数据保护新策略:“动态脱敏”“二次授权”

数据的流通使用是创新的动力&#xff0c;但安全和合规是不可逾越的底线。企业如何在这三者之间找到平衡点&#xff1f; 极盾科技&#xff0c;助力某金融租赁机构&#xff0c;基于极盾觅踪构建应用数据动态脱敏系统&#xff0c;实现10&#xff0b;核心应用系统的统一管理&#x…

库(Library)

库的定义 在Linux操作系统中&#xff0c;库&#xff08;Library&#xff09;是一段编译好的、可重用的代码&#xff0c;它能够被其他程序或应用程序在运行时调用。库可以提高代码的模块化&#xff0c;使得开发者可以共享和重用代码&#xff0c;从而提高开发效率&#xff0c;减少…

如何在 Ubuntu 24.04 上安装 MariaDB ?

MariaDB 是一个流行的开源关系数据库管理系统&#xff0c;它是 MySQL 的一个分支&#xff0c;它被广泛用于存储和管理数据。本指南将引导您完成在 Ubuntu 24.04 上安装 MariaDB 的步骤。 Step 1: Update Your System 首先更新系统&#xff0c;确保所有的软件都是最新的。 su…

PMP–一、二、三模、冲刺、必刷–分类–14.敏捷–技巧–刺探

文章目录 技巧一模反例不选“刺探”14.敏捷--流程&#xff1a;&#xff08;2&#xff09;每日站会&#xff08;15分钟、轮流开、提出问题、不解决问题&#xff09;&#xff1a;输入任务板/看板 → 输出任务板更新、燃尽图更新、障碍日志、产品增量&#xff1b;14.敏捷--方法--每…

树莓派扩展RGB点阵屏的使用

本篇来介绍一个树莓派的RGB 8x8点阵屏扩展板的使用。 1 RGB点阵屏 这里使用SunFounder的一个RGB 8x8树莓派扩展板&#xff0c;将其插接到树莓派中即可使用。 2 树莓派IIC配置 树莓派系统的安装&#xff0c;可参考之前的文章&#xff1a; 这个RGB点阵屏与树莓派直接使用IIC通…

Opencv中的直方图(2)计算图像的直方图函数calcHist()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 计算一组数组的直方图。 函数 cv::calcHist 计算一个或多个数组的直方图。用于递增直方图bin的元组的元素是从相同位置的相应输入数组中获取的。…

C++设计模式——Template Method模板方法模式

一&#xff0c;模板方法模式的定义 模板方法模式是一种行为型设计模式&#xff0c;它先定义了一个算法的大致框架&#xff0c;然后将算法的具体实现步骤分解到多个子类中。 模板方法模式为算法设计了一个抽象的模板&#xff0c;算法的具体代码细节由子类来实现&#xff0c;从…

Spring Boot中如何禁用Actuator端点安全性?

Spring Boot中如何禁用Actuator端点安全性&#xff1f; 1、为什么考虑禁用&#xff1f;2、如何禁用&#xff1f;方法一&#xff1a;自定义Security配置&#xff08;推荐&#xff09;方法二&#xff1a;绕过安全性&#xff08;不推荐&#xff09; 3、注意事项4、总结 &#x1f4…

Golang | Leetcode Golang题解之第393题UTF-8编码验证

题目&#xff1a; 题解&#xff1a; const mask1, mask2 1 << 7, 1<<7 | 1<<6func getBytes(num int) int {if num&mask1 0 {return 1}n : 0for mask : mask1; num&mask ! 0; mask >> 1 {nif n > 4 {return -1}}if n > 2 {return n}r…

AI机械键盘,罗技推出首款AI机械键盘K98M

在这个智能化日益普及的时代&#xff0c;我们的生活中充斥着各种智能设备。 从智能手机到智能家居&#xff0c;现在连键盘也加入了智能化的行列。罗技&#xff0c;作为知名的电脑配件制造商&#xff0c;最近推出了他们的首款AI机械键盘K98M。这款键盘集成了百度文心一言提供的…

C++设计模式——Observer观察者模式

一&#xff0c;观察者模式的定义 观察者模式是一种行为型设计模式&#xff0c;又被称为"发布-订阅"模式&#xff0c;它定义了对象之间的一对多的依赖关系&#xff0c;当一个对象的状态发生变化时&#xff0c;所有依赖于它的对象都会收到通知并自动更新。 观察者模式…

13、Django Admin创建两个独立的管理站点

admin文件 from .models import Epic, Event, EventHero, EventVillain from django.contrib.admin import AdminSiteclass EventAdminSite(AdminSite):site_header "Events管理"site_title "欢迎您&#xff01;"index_title "管理员"even…

AI自动生成PPT哪个软件好?如何自动生成专业级PPT?

新学期伊始&#xff0c;准备开学演讲稿的你是否还在为制作PPT而烦恼&#xff1f;别担心&#xff0c;现在有了AI的帮助&#xff0c;生成专业且吸引人的PPT变得轻而易举。 本文将为你揭秘4种高效的AI自动生成PPT的方法&#xff0c;让你在新学期的演讲中脱颖而出。无论是简洁明了…

畅游5G高速网络:联发科集成Wi-Fi6E与蓝牙5.2的系统级单芯片MT7922

这周末,除非外面下钞票,否则谁也拦不住我玩《黑神话悟空》(附:两款可以玩转悟空的显卡推荐) IPBrain平台君 集成电路大数据平台 2024年09月03日 17:28 北京 联发科一直以创新技术追赶市场需求…… “不努力向前游就会被海浪拍回岸边…” 芯片设计公司产品层出不穷,想要站…

Redis集群搭建以及用idea连接集群

一、redis的集群搭建&#xff1a; 判断一个是集群中的节点是否可用,是集群中的所用主节点选举过程,如果半数以上的节点认为当前节点挂掉,那么当前节点就是挂掉了,所以搭建redis集群时建议节点数最好为奇数&#xff0c;搭建集群至少需要三个主节点,三个从节点,至少需要6个节点。…

Datawhle X 李宏毅苹果书AI夏令营深度学习笔记之——卷积神经网络

卷积神经网络简介 卷积神经网络&#xff08;Convolutional Neural Network, CNN&#xff09;是一种深度学习模型&#xff0c;尤其擅长处理图像和视频等高维度的数据。CNN 通过模仿人类视觉系统的工作方式&#xff0c;自动学习数据中的空间层次结构&#xff0c;使得它在计算机视…

我找到了一个让ChatGPT稳定通过草莓测试的方法,百试百灵!

大家好&#xff0c;我是木易&#xff0c;一个持续关注AI领域的互联网技术产品经理&#xff0c;国内Top2本科&#xff0c;美国Top10 CS研究生&#xff0c;MBA。我坚信AI是普通人变强的“外挂”&#xff0c;专注于分享AI全维度知识&#xff0c;包括但不限于AI科普&#xff0c;AI工…