Linux 文件系统:文件描述符、管理文件

目录

一、三个标注输入输出流

二、文件描述符fd

1、通过C语言管理文件—理解文件描述符fd

2、文件描述符实现原理

3、文件描述符0、1、2

4、总结

三、如何管理文件

1、打开文件的过程

2、内核空间的结构

struct task_struct(PCB)

struct files_struct和struct file *fd_array[]

struct file

3、深入到内核空间的操作 

fopen()的操作流程:

fwrite()操作流程


一、三个标注输入输出流

在Linux系统中,存在一个核心概念:“一切皆文件”。这意味着系统中的几乎所有东西,包括硬件设备如键盘和显示器,都被抽象为文件。这种设计使得与这些设备的交互变得统一和简化,因为你可以使用标准的文件操作API来与它们交互。

尽管如此,我们通常不会直接“打开”键盘或显示器文件来进行读写操作。相反,C和C++程序通常通过标准输入输出流来与这些设备交互。这是因为C/C++的标准库(如stdio.h)为我们提供了一组抽象的输入输出功能,这些功能背后默认与键盘和显示器等设备的文件描述符相连接。

当你启动一个C或C++程序时,运行时环境(如操作系统的shell)会自动为程序打开三个基本的文件流:

  • 标准输入(stdin):通常与键盘关联,用于读取输入。在程序中,它被表示为FILE* stdin;
  • 标准输出(stdout):通常与显示器关联,用于输出信息。在程序中,它被表示为FILE* stdout;
  • 标准错误(stderr):也通常与显示器关联,但用于输出错误信息。在程序中,它被表示为FILE* stderr;

这三个流在程序开始执行时就已经打开并可用,因此可以直接使用如printfscanffgets等函数进行输出和输入操作,而无需手动打开任何文件。这些函数内部会处理与标准输入输出流相关的所有细节,使得与用户的交互变得简单直接。

简而言之,虽然Linux下一切皆文件,包括键盘和显示器,但是在C/C++程序中,我们通过标准的输入输出流(stdin、stdout、stderr)来与这些设备进行交互,而无需直接打开或操作它们的文件描述符。这些流在程序启动时由运行时环境自动为我们打开和配置。

二、文件描述符fd

1、通过C语言管理文件—理解文件描述符fd

在C语言的世界里,FILE 是一个关键的概念,用于表示文件流。它实际上是一个结构体,由C标准库提供,内含多种成员,旨在抽象和管理文件操作的复杂性。这个结构体封装了文件的各种信息,如缓冲状态、当前读写位置等,让开发者能够通过一系列标准库函数,如 fopenfwritefread 等,来方便地进行文件操作。

        然而,当我们深入到操作系统的层面,尤其是在UNIX或类UNIX系统中,会发现操作系统本身并不直接认识 FILE 结构体。对于操作系统而言,文件是通过文件描述符(File Descriptor, 简称fd)来识别和管理的。文件描述符是一个非常底层的概念,它是一个整数值,代表了进程中打开文件的唯一标识。

        因此,虽然在使用C标准库进行文件操作时我们操作的是 FILE 类型的指针,底层实现这些功能的时候,C库函数实际上会转换成操作系统理解的文件描述符。这意味着,尽管我们在编程时与 FILE 打交道,C标准库在背后会处理与文件描述符相关的所有细节,确保我们的文件操作最终能够被操作系统正确执行。这一层抽象既隐藏了底层的复杂性,也提供了更丰富、更易用的接口给程序员。

2、文件描述符实现原理

通过对open函数的学习,我们知道了文件描述符就是一个小整数,其内部实现是通过位图的原理,具体实现方式如下:
#include <stdio.h>// 定义标志位,用int中的不重复的一个bit,就可以标识一种状态
#define ONE   0x1    //0000 0001
#define TWO   0x2    //0000 0010
#define THREE 0x4    //0000 0100// 显示函数,根据标志位输出不同的消息
void show(int flags) {if (flags & ONE)  printf("你好,第一种状态\n");if (flags & TWO)printf("你好,第二种状态\n");if (flags & THREE)printf("你好,第三种状态\n");
}int main() {// 分别显示不同的状态show(ONE);show(TWO);show(ONE | TWO); // 使用按位或运算符组合标志位show(ONE | TWO | THREE);//0000 0001 | 0000 0010=0000 0011show(ONE | THREE);return 0;
}[hbr@VM-16-9-centos exercise_func]$ ./mytest 
你好,第一种状态
-----------------------------------------
你好,第二种状态
-----------------------------------------
你好,第一种状态
你好,第二种状态
-----------------------------------------
你好,第一种状态
你好,第二种状态
你好,第三种状态
-----------------------------------------
你好,第一种状态
你好,第三种状态

3、文件描述符0、1、2

当我们使用fileno函数提取标准输入、输出、错误的文件描述符,可以看到

#include <stdio.h>
int main()
{printf("stdin: %d\n", fileno(stdin));printf("stdout: %d\n", fileno(stdout));printf("stderr: %d\n", fileno(stderr));return 0;
}
[hbr@VM-16-9-centos interface_func]$ ./myfile 
stdin: 0
stdout: 1
stderr: 2

在Linux进程中,文件描述符0、1、和2分别用于标准输入(stdin)、标准输出(stdout)、和标准错误(stderr)的缺省打开。这三个文件描述符为进程通信与数据输入输出提供了基础。当你从键盘输入时,默认情况下,这些输入数据通过文件描述符0(标准输入)进入程序;

  • 当程序需要输出信息到屏幕时,它会使用文件描述符1(标准输出)和文件描述符2(标准错误),其中标准输出用于常规信息,标准错误专用于输出错误信息。

从文件描述符3开始,是进程打开或创建的其他文件和资源的描述符。

  • 当一个进程通过如open系统调用打开或创建新文件时,分配给该文件的文件描述符将是当前未被使用的最小的正整数文件描述符。
  • 这意味着,如果进程没有打开除标准输入、输出和错误外的其他文件,那么新打开的文件将使用文件描述符3。

通过下面程序就明白了:

[hbr@VM-16-9-centos exercise_func]$ cat testC.c 
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>int main() 
{umask(0);int fd1 = open("log1.txt", O_WRONLY|O_CREAT|O_APPEND,0666); printf("open success, fd1: %d\n", fd1);int fd2 = open("log2.txt", O_WRONLY|O_CREAT|O_APPEND,0666); printf("open success, fd2: %d\n", fd2);int fd3 = open("log3.txt", O_WRONLY|O_CREAT|O_APPEND,0666); printf("open success, fd3: %d\n", fd3);int fd4 = open("log4.txt", O_WRONLY|O_CREAT|O_APPEND,0666); printf("open success, fd4: %d\n", fd4);close(fd1);close(fd2);close(fd3);close(fd4);return 0;
}
[hbr@VM-16-9-centos exercise_func]$ ll
total 40
-rw-rw-r-- 1 hbr hbr   72 Mar 18 21:19 makefile
-rwxrwxr-x 1 hbr hbr 8664 Mar 18 23:20 mytest
-rw-rw-r-- 1 hbr hbr  954 Mar 18 23:29 testC.c
[hbr@VM-16-9-centos exercise_func]$ make
gcc -std=c99 -o mytest testC.c
[hbr@VM-16-9-centos exercise_func]$ ./mytest 
open success, fd1: 3
open success, fd2: 4
open success, fd3: 5
open success, fd4: 6
[hbr@VM-16-9-centos exercise_func]$ ll
total 40
-rw-rw-rw- 1 hbr hbr    0 Mar 18 23:30 log1.txt
-rw-rw-rw- 1 hbr hbr    0 Mar 18 23:30 log2.txt
-rw-rw-rw- 1 hbr hbr    0 Mar 18 23:30 log3.txt
-rw-rw-rw- 1 hbr hbr    0 Mar 18 23:30 log4.txt
-rw-rw-r-- 1 hbr hbr   72 Mar 18 21:19 makefile
-rwxrwxr-x 1 hbr hbr 8512 Mar 18 23:30 mytest
-rw-rw-r-- 1 hbr hbr  954 Mar 18 23:29 testC.c

这个编号系统继续下去,对于每一个新打开的文件或资源(如网络套接字),内核都会分配一个唯一的、递增的文件描述符。这使得进程可以同时管理多个输入输出流,包括文件读写、网络通信等。

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

4、总结

在Linux系统中,文件标识符(File Identifier)通常指的是文件描述符(File Descriptor)。文件描述符是一个非负整数,用于唯一标识一个已打开的文件或I/O流。

  1. 标识打开文件:文件描述符是操作系统用来标识已打开文件的机制。每个打开的文件都会有一个对应的文件描述符。

  2. 整数值:文件描述符是一个非负整数,通常从0开始递增。标准输入、标准输出和标准错误的文件描述符分别是0、1和2,从文件描述符3开始,是进程打开或创建的其他文件和资源的描述符。

  3. 系统调用参数:文件描述符通常作为参数传递给系统调用,比如readwriteclose等,以指定要操作的文件。

  4. 资源管理:操作系统会维护一个文件描述符表,用于跟踪每个进程打开的文件及其对应的文件描述符。这个表中记录了文件描述符和文件之间的映射关系。

  5. 文件操作:通过文件描述符,程序可以进行文件的读取、写入和其他操作。文件描述符是进行文件 I/O 操作的关键。

  6. 标准文件描述符:在Linux系统中,通常有三个标准文件描述符:0(标准输入)、1(标准输出)和2(标准错误),它们分别对应键盘输入、终端输出和错误信息输出。

三、如何管理文件

一个进程不仅可以打开多个文件,而且这种情况非常普遍,形成了一对多(1:n)的关系。所以,在操作系统的内部,为了有效地管理众多被打开的文件,确实需要对这些文件进行适当的组织和管理。

操作系统会为每一个被打开的文件创建一个结构体(通常称为 struct file 或类似的名称),该结构体包含了关于这个文件的几乎所有信息,如文件的属性、位置指针、读写权限等。如果存在大量的被打开的文件,操作系统会通过数据结构,如双链表,来组织这些 struct file 的实例,以便高效管理和访问。

 文件描述符fd在内核中的表现,可以被理解为指向这些 struct file 实例的指针数组的下标。

1、打开文件的过程

  • 当一个进程请求打开一个文件时,操作系统会首先在内核中创建一个对应的 struct file 对象。
  • 接着,它会在文件描述符表(这是一个每个进程都有的、指向 struct file 实例的指针数组)中寻找一个未被使用的条目,并将新创建的 struct file 对象的引用存放在那里。最后,操作系统将这个条目的索引(即文件描述符)返回给进程。
  • 因此,当进程需要进行文件操作时,它会提供文件描述符作为参数。操作系统通过这个文件描述符,查找进程的文件描述符表,从而找到对应的 struct file 对象,进而进行具体的文件操作。这种机制既简化了文件操作的接口,也保证了操作的安全性和效率。

2、内核空间的结构

struct task_struct(PCB)

  • 每个运行中的进程在内核中都有一个struct task_struct实例,其中包含了进程的所有信息,包括它的文件描述符表,通过files指针指向struct files_struct

struct files_structstruct file *fd_array[]

  • struct files_struct维护了一个文件描述符表fd_array[],这个表是一个指针数组,每个指针指向一个struct file实例。文件描述符(fd)实质上是这个数组的索引。

struct files_struct是Linux内核中的一个结构体,它的主要作用是管理和跟踪一个进程打开的所有文件描述符。在Linux系统中,当进程打开一个文件时,内核会为该文件分配一个文件描述符(fd),这是一个非负整数,用作索引来访问进程打开的文件。文件描述符为进程与打开的文件之间的交互提供了一个简单的抽象。

  • 具体来说,struct files_struct包含了以下关键信息:
  • count: 一个引用计数,表示有多少个地方引用到这个files_struct。这是用于内存管理和确保结构体在不再被需要时可以被正确地释放。
  • fdt: 指向一个fdtable结构的指针,该结构实际上保存了文件描述符的数组(fd_array)以及与之相关的一些其他数据,如文件描述符的最大数量等。
  • fd_array: 通常作为fdtable的一部分,这是一个指针数组,每个指针指向一个struct file结构体实例。每个struct file实例代表一个打开的文件,包含了文件的当前状态、位置偏移量、以及文件操作的方法等信息。
  • 文件描述符的分配和回收机制files_struct还管理着文件描述符的分配和回收,确保每次打开文件时都能分配一个唯一的最小可用文件描述符,并在文件关闭时回收该描述符。
  • 通过管理每个进程的文件描述符表,struct files_struct为进程提供了对打开文件的有效访问和控制,使得进程可以执行读写、查询状态以及执行其他文件操作。

struct file

  • struct file代表一个打开的文件的所有信息,包括文件的状态、当前偏移量、与文件相关的操作函数等。它是实际执行读写操作的基础。

3、深入到内核空间的操作 

FILE *stdout = fopen("显示器",“w");-> open->1
stdout -> FILE* -> FILE->fileno(1)

  • FILE *stdout = fopen("显示器", "w");

    • 用户程序请求打开"显示器"(在实际应用中,stdout通常预定义且指向标准输出)进行写操作。fopen函数内部调用open系统调用,并将返回的文件描述符(fd)封装在一个FILE结构体中。这个结构体提供了一个高级接口来进行后续的文件I/O操作。

fopen()的操作流程:

fopen->open->检查并分配fd->struct file->在struct file *fd_array[fd]指向struct file->fopen从open获得fd->创建FILE*

  1. 接收参数fopen()函数被调用时,接收两个参数:文件路径(path)和模式(mode)。模式指定了文件的访问类型(如读取、写入、追加等)。

  2. 高级抽象:通过FILE *指针,fopen()为程序员提供了一个高级的文件操作抽象。FILE *封装了文件描述符(fd)以及其他进行文件I/O所需的信息,比如缓冲区的地址、缓冲区大小、文件的当前位置指针等。

  3. 系统调用open()获取fdfopen()内部会调用系统调用open(),向操作系统请求打开指定路径的文件。

    1. 内核处理:操作系统内核接收到open系统调用后,会执行以下操作:

      • 内核会检查进程的权限以确定是否允许打开指定文件。
      • 如果权限检查通过,内核会为该文件分配一个文件描述符。
  4. 创建struct file结构体:一旦open系统调用被执行,内核首先会进行权限检查、解析路径等操作。如果所有检查都通过,内核会创建一个struct file实体来代表这个新打开的文件。

  5. 进程打开的文件会被加入到文件描述符表:open函数返回文件描述符fd之后,进程打开的文件会被加入到文件描述符表struct file *fd_array[]中。这意味着文件描述符表会在文件被成功打开后更新,以便进程可以通过文件描述符来访问该文件。

    1. 文件描述符表:在task_struct中,有一个指向files_struct的指针,files_struct维护着进程打开的所有文件的信息,包括文件描述符表fd array[]。由此获取到文件描述符(fd),并将其作为索引,访问到对应的文件信息。

  6. 创建FILE结构体实例

    1. 一旦struct file实体创建完成,并且open系统调用成功,内核会将相应的文件描述符(fd)返回给用户空间的调用者。这个文件描述符实际上是struct file实体在进程文件描述符表中的索引。

    2. 一旦fopenopen系统调用获取到文件描述符(fd),它会在用户空间分配并初始化一个FILE结构体实体,这个实体封装了文件描述符和其他高级I/O操作所需的信息,如缓冲区等。
    3. fopen之后返回的是一个指向FILE结构体的指针,供用户程序进行后续的文件操作。
  • 当一个进程调用 open() 等系统调用来打开文件时,实际上并不需要直接访问该进程的 task_struct 结构体。文件的打开操作是基于进程的文件描述符表进行的,这个表存储了进程打开的文件以及它们对应的文件描述符。
  • 文件描述符表是由进程的 files_struct 结构体间接引用的,而 files_struct 结构体是存储在进程的 task_struct 结构体中的。因此,虽然文件的打开操作涉及到文件描述符表,但并不需要直接访问进程的 task_struct 结构体,因为文件描述符表是通过 task_struct 间接引用的。这种间接引用的设计使得操作系统能够有效地管理进程的文件描述符和文件操作,同时保持进程信息的封装和隔离。

fwrite()操作流程

fwrite()-> FILE* -> fd -> write -> write(fd,...) -> 自己执行操作系统内部的write方法 ->能找到进程的task struct->*fs -> files struct->fd arrayl]->fd array[fd]->struct file->内存文件被找到了! ->进行写入操作。

  1. 首先,fwrite()函数接收一个FILE*类型的指针,这是C语言标准库提供的文件操作的高级抽象。通过FILE*指针获得背后关联着一个文件描述符(fd),它是一个低级的概念,直接与操作系统的文件系统接口相连。
  2. fwrite()被调用时,它最终会通过一系列转换和底层调用,调用到操作系统的write()函数,具体形式为write(fd, ...)。这个write()函数是系统调用,直接与内核交互,执行具体的写入操作。
  3. 访问task_struct:每个进程在操作系统内部都有一个代表它的task_struct结构体。当进程执行write()调用时,系统已经通过当前的执行上下文(比如CPU的当前执行线程关联的进程)直接访问到了该进程的task_struct。这不是通过文件描述符完成的,而是基于当前正在执行的进程上下文。
  4. task_struct是Linux内核中的进程控制块(PCB),包含了进程的所有信息。在task_struct中,有一个指向files_struct的指针,这个files_struct维护着进程打开的所有文件的信息,包括一个文件描述符表fd array[]
  5. fd array[]中,通过文件描述符fd作为索引,可以找到对应的struct file实例。struct file包含了文件的详细信息,包括文件的当前状态、位置偏移量以及如何操作这个文件的方法等。
  6. 找到了struct file之后,操作系统就能够确定具体要操作的内存中的文件。随后,它执行写入操作,将数据从用户空间传输到内核空间,最终写入到文件中。

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

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

相关文章

你真的了解SpringBoot注解?阿里巴巴面试告诉你答案!

如有疑问或者更多的技术分享,欢迎关注我的微信公众号“知其然亦知其所以然”! 大家好,我是小米!今天我来和大家分享一下在阿里巴巴面试中常见的一个问题:SpringBoot注解。SpringBoot作为当今流行的Java开发框架,注解是其灵魂所在,熟练掌握这些注解对于应对面试非常有帮…

腾讯云服务器入站规则端口开放使用指南(CentOS系统)

第一步&#xff1a;开放安全组入站规则 来源处0.0.0.0/0是对IPv4开发&#xff0c;::/0是对IPv6开放&#xff1b; 协议端口按照提示填写即可。云服务器防火墙开放 第三步&#xff1a;本地防火墙开放 sudo firewall-cmd --zonepublic --add-port你的端口号/tcp --perma…

【c++】类和对象

主页&#xff1a;醋溜马桶圈-CSDN博客 专栏&#xff1a;c_醋溜马桶圈的博客-CSDN博客 gitee&#xff1a;mnxcc (mnxcc) - Gitee.com 目录 1.面向过程和面向对象初步认识 面向过程&#xff08;C语言&#xff09; 面向对象&#xff08;C&#xff09; 2.类的引入和定义 2.1 类…

OpenCV图像阈值分割、二值化

在OpenCV中图像总共有5种图像二值化的方法&#xff0c; 在进行图像二值化时最重要的就是确定分割的阈值&#xff0c;阈值确定的方法主要有两类&#xff1a;全局阈值和自适应阈值。而在全局阈值和自适应阈值下面又有很多方法&#xff0c;本文将对这些方法进行详细讲解 OpenCV图…

FPGA——DDR3的IP核

FPGA——DDR3的ip核 IP核配置基于MIG核代码基于AXI接口的DDR3 IP核配置 1 2 3 4 5 6 基于MIG核代码 控制MIG核的信号进行读写 module MIG_APP_Drive(input i_ui_clk ,input i_ui_rst ,input init_calib_…

Legacy|电脑Windows系统如何迁移到新安装的硬盘?系统迁移详细教程!

前言 前面讲了很多很多关于安装系统、重装系统的教程。但唯独没有讲到电脑换了新的硬盘之后&#xff0c;怎么把旧系统迁移到新的硬盘上。 今天小白就来跟各位小伙伴详细唠唠&#xff1a; 开始之前需要把系统迁移的条件准备好&#xff0c;意思就是在WinPE系统下&#xff0c;可…

AI赋能:加速MWORKS.Sysplorer仿真设计的新途径

近年来&#xff0c;人工智能技术的应用逐渐兴起&#xff0c;为仿真领域带来了新的机遇。随着现代工程系统的复杂性日益增加&#xff0c;MWORKS.Sysplorer作为一种广泛应用于系统仿真和设计的工具&#xff0c;面临着模型日益庞大和计算资源有限的挑战。为了解决这一问题&#xf…

【开发】SpringBoot 整合 Redis

目录 前言 1. Redis 的下载及安装 1.1 Redis 的下载 1.2 安装 Redis 1.3 启动 Redis 2. 创建 SpringBoot 项目整合 Redis 2.1 环境要求 2.2 SpringBoot项目构建 2.2.1 方式一 2.2.2 方式二 2.3 在 pom.xml 文件中导入依赖坐标 2.4 在 application.properties 中加…

编辑命令行提示符 prompt,支持显示 git 分支、标签等信息

实时在 Linhieng/current–Microsoft.PowerShell_profile.ps1 上更新 打开 $Profile 文件&#xff0c;将下面内容粘贴到其中即可&#xff1a; <# 负责打印 git 分支相关信息支持输出以下信息&#xff1a;当前分支&#xff0c;或者是 hash 值当前目录是否为 git 子目录当前…

定位线上最耗CPU的线程

定位线上最耗CPU的线程 准备工作 启动一个程序。 arthas-demo是一个简单的程序&#xff0c;每隔一秒生成一个随机数&#xff0c;再执行质因数分解&#xff0c;并打印出分解结果。 curl -O https://alibaba.github.io/arthas/arthas-demo.jar java -jar arthas-demo.jar[root…

计算机网络的组成

目录 <计算机网络的组成> 1.网络硬件 1)主机Host 2)终端Terminal 3)通信控制处理机 4)传输介质 5)网络连接设备 2.网络软件 1)网络操作系统 2)网络协议软件 3)网络管理软件 4)网络通信软件 5)网络应用软件 3通信子网和资源子网 <计算机网络的组成> 无…

组建公司办公网络

一 认识网络传输介质的分类 网络传输介质主要分为有线传输介质和无线传输介质两大类&#xff0c;它们在网络建设和数据传输中扮演着至关重要的角色。下面是这两类传输介质的详细分类&#xff1a; 有线传输介质 双绞线&#xff08;Twisted Pair&#xff09;&#xff1a;这是最…

PHP与Spring Boot在实现功能上的比较

原文地址&#xff1a;PHP与Spring Boot在实现功能上的比较 - Pleasure的博客 下面是正文内容&#xff1a; 前言 最近在学Spring Boot后端框架&#xff0c;在功能实现上发现了与PHP存在着一定的可比性。 这篇就单独拎出来进行比较一下。 正文 就例举一些功能上的重叠以及大致的…

【Web】浅聊Hessian反序列化之打Spring AOP——JNDI

目录 前言 简单分析 EXP 前言 前文&#xff1a;【Web】浅聊Java反序列化之Rome——关于其他利用链-CSDN博客 前文里最后给到一条HotSwappableTargetSource利用链&#xff0c;就是我们今天PartiallyComparableAdvisorHolder链子的前半段(触发恶意类的toString方法)&#xf…

Redis中的String编码转换底层原理及6.0新特性

String编码转换底层原理 String对象为什么把大于39字节或者44字节的字符串编码为raw&#xff0c;小于的时候编码为embstr? 在Redis3.2以前的版本中,SDS作为字符串类型中存储字符串内容的结构&#xff0c;源码如下&#xff1a; 3.2版本SDS结构 struct sdshdr {// 记录buf数…

华岳M9制造企业管理软件业务流程 2/4

华岳M9制造企业管理软件业务流程 2/4 步骤3 初始一、应收账款初始余额二、应付账款初始余额三、出纳账项初始余额四、会计账项初始余额五、盘点入库六、存货细目七、存货属性设置八、存货存量控制九、存货价格管理十、月末处理 步骤4 技术一、存货目录二、存货细目三、仓库绑定…

nginx代理服务器配置

nginx代理服务器配置 需要配置环境需求 1、一台1.1.1.1服务器&#xff0c;一台2.2.2.2服务器 前端包路径在1.1.1.1 /etc/dist 下 后端服务在2.2.2.2 上 暴露端口为9999 2、需求 现在需要访问 1.1.1.1:80访问到2.2.2.2 上面的9999后端服务 3、配置nginx ①&#xff1a;在…

【深度学习实践】面部表情识别,深度学习分类模型,mmpretrain用于分类的实用教程,多任务网络头

文章目录 数据集数据集的进一步处理转换training.csv转换validation.csv 剔除无法使用的图片数据选择mmpretrain框架来训练配置四个文件改写base model改写base datasetsschedulesdefault_runtime 总配置开始训练训练分析考虑在网络上增加facial_landmarks回归head考虑是否可以…

B树B+树,字典树详解,哈夫曼树博弈树

目录 B树&#xff1a;B-Tree B树 字典树&#xff1a;Trie Tree 哈夫曼树 博弈树 B树&#xff1a;B-Tree 多路平衡搜索树 1.M阶B树&#xff0c;就是M叉&#xff08;M个指针&#xff09;。 2.每个节点内记录个数<M-1。 3.根节点记录个数>1。 4.其余节点内记录个数&…

人工智能技术的不当利用与风险

目录 前言1 视频篡改技术的滥用1.1 虚假信息传播与社会动荡1.2 对公众信任的破坏与舆论混乱 2 隐私泄露与监视风险2.1 个人信息安全与数据滥用风险2.2 社会稳定与个人自由权利的平衡 3 虚假评论与信息传播3.1 舆论操纵与社会意识形态的影响3.2 对信息可信度与公众信任的威胁 结…