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 0011
    show(ONE | THREE);
    return 0;
}

[hbr@VM-16-9-centos exercise_func]$ ./mytest 
你好,第一种状态
-----------------------------------------
你好,第二种状态
-----------------------------------------
你好,第一种状态
你好,第二种状态
-----------------------------------------
你好,第一种状态
你好,第二种状态
你好,第三种状态
-----------------------------------------
你好,第一种状态
你好,第三种状态

3、文件描述符0、1、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之后,操作系统就能够确定具体要操作的内存中的文件。随后,它执行写入操作,将数据从用户空间传输到内核空间,最终写入到文件中。

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

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

相关文章

hcia复习总结9

NAT 在ip地址空间中&#xff0c;A,B,C三类地址中各有一部分地址&#xff0c;他们被称为私有地址&#xff08;私网IP地址&#xff09;&#xff0c;其余的所有地址都被称为公有地址&#xff08;公网IP地址&#xff09; A&#xff1a;10.0.0.0-10.255.255.255--相当于一个A类网络…

LeetCode 热题 100

未完待续… 一、哈希 1、两数之和 # 暴力解&#xff0c;时间复杂度&#xff1a;o(n^2) class Solution(object):def twoSum(self, nums, target):""":type nums: List[int]:type target: int:rtype: List[int]"""for i in range(len(nums)):fo…

检查约束

Oracle从入门到总裁:​​​​​​https://blog.csdn.net/weixin_67859959/article/details/135209645 检查约束 检查约束指的是在数据列上设置一些过滤条件&#xff0c;当过滤条件满足的时候才可以进行保存&#xff0c;如果不满足则出现错误。例如&#xff0c;设置年龄的信息…

【C语言】猜数字游戏

代码如下&#xff1a; #define _CRT_SECURE_NO_WARNINGS 1 #include <stdio.h> #include <stdlib.h> #include <time.h> void game() {int r rand() % 100 1;int guess 0;while (1){printf("请猜数字>:");scanf("%d", &guess…

【vscode】vscode重命名变量后多了很多空白行

这种情况&#xff0c;一般出现在重新安装 vscode 后出现。 原因大概率是语言服务器没设置好或设置对。 以 Python 为例&#xff0c;到设置里搜索 "python.languageServer"&#xff0c;将 Python 的语言服务器设置为 Pylance 即可。

Vue3学习日记 Day4 —— pnpm,Eslint

注&#xff1a;此课程需要有Git的基础才能学习 一、pnpm包管理工具 1、使用原因 1.1、速度快&#xff0c;远胜过yarn和npm 1.2、节省磁盘空间 2、使用方式 2.1、安装方式 npm install -g pnpm 2.2、创建项目 pnpm create vue 二、Eslint配置代码风格 1、环境同步 1、禁用Pret…

OpenCV Steger算法提取条纹中心线

文章目录 一、简介二、实现代码三、实现效果参考资料一、简介 Steger 算法是一种常用的图像边缘检测算法,可以用于提取图像中的中心线或边缘信息。它的理论假设是:条纹的亮度是按照高斯分布呈现的,即中心亮两侧渐暗。 其计算过程如下所述: 1、首先,我们需要计算每个点Hess…

Apache Doris 2.1 核心特性 Variant 数据类型技术深度解析

在最新发布的 Apache Doris 2.1 新版本中&#xff0c;我们引入了全新的数据类型 Variant&#xff0c;对半结构化数据分析能力进行了全面增强。无需提前在表结构中定义具体的列&#xff0c;彻底改变了 Doris 过去基于 String、JSONB 等行存类型的存储和查询方式。为了让大家快速…

Spring Boot 实现程序的优雅退出

&#x1f3f7;️个人主页&#xff1a;牵着猫散步的鼠鼠 &#x1f3f7;️系列专栏&#xff1a;Java全栈-专栏 &#x1f3f7;️个人学习笔记&#xff0c;若有缺误&#xff0c;欢迎评论区指正 目录 前言 优雅停机是什么 SpringBoot如何实现优雅停机 合理杀死进程 法一&…

【nfs报错】rpc mount export: RPC: Unable to receive; errno = No route to host

NFS错误 问题现象解决方法 写在前面 这两天搭建几台服务器&#xff0c;需要使用nfs服务&#xff0c;于是六台选其一做服务端&#xff0c;其余做客户端&#xff0c;搭建过程写在centos7离线搭建NFS共享文件&#xff0c;但是访问共享时出现报错&#xff1a;rpc mount export: RPC…

基于单片机的家庭烟雾报警系统

摘要:本文主要针对家庭等小型应用场所, 提出基于以单片机CC2530 作为控制器的智能烟雾报警系统,通过MQ-2 气体传感器来检测烟雾浓度,在单片机的A/D模块转化后,并配合蜂鸣元器件实现声音报警功能。 【关键词】烟雾报警 单片机 烟雾传感器 由于科技的发展以及各类家电走入…

sentinel系统负载自适应流控

系统负载自适应流控 规则配置 规则创建 public class SystemRule extends AbstractRule {private double highestSystemLoad -1;private double highestCpuUsage -1;private double qps -1;private long avgRt -1;private long maxThread -1; }SystemRule类包含了以下几…

unity发布安卓获取读取权限

一、Player Settings 设置 Player Settings>Player>Other Settings> Android > Write Permission > External (SDCard). 二、代码 using System.Collections; using System.Collections.Generic; using System.IO; using UnityEngine; using UnityEngine.Andr…

配置LVS NAT模式

配置LVS NAT模式 环境准备 client1&#xff1a;eth0->192.168.88.10&#xff0c;网关192.168.88.5lvs1: eth0 -> 192.168.88.5&#xff1b;eth1->192.168.99.5web1&#xff1a;eth1->192.168.99.100&#xff1b;网关192.168.99.5web2&#xff1a;eth1->192.168…

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

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

力扣-1351 统计有序矩阵中的负数

给你一个 m * n 的矩阵 grid&#xff0c;矩阵中的元素无论是按行还是按列&#xff0c;都以非严格递减顺序排列。 请你统计并返回 grid 中 负数 的数目。 示例 1&#xff1a; 输入&#xff1a;grid [[4,3,2,-1],[3,2,1,-1],[1,1,-1,-2],[-1,-1,-2,-3]] 输出&#xff1a;8 解释&…

基于stable diffusion的IP海报生成

【AIGC】只要10秒&#xff0c;AI生成IP海报&#xff0c;解放双手&#xff01;&#xff01;&#xff01;在AIGC市场发展的趋势下&#xff0c;如何帮助设计工作者解放双手。本文将从图像生成方向切入&#xff0c;帮助大家体系化的学习Stable diffusion的使用&#xff0c;完成自有…

游戏发行商的重要意义:武汉灰京文化的角色和任务

游戏发行商在游戏产业中扮演着重要的角色&#xff0c;他们不仅是游戏内容的传播者&#xff0c;还肩负着众多重要任务。武汉灰京文化作为一家游戏发行商&#xff0c;具有重要意义&#xff0c;他们在测试、本地化、版本移植、分销平台关系、品牌战略制定、法务支持等方面承担着多…

【异质集成】高k复杂氧化物栅介质在GaN HEMTs上的异质集成

COMMUNICATIONS ENGINEERING | (2024) 3:15论文阅读。 文章讨论了高k复杂氧化物栅介质在宽带隙高电子迁移率晶体管&#xff08;HEMTs&#xff09;上的异质集成。 其核心内容的总结如下&#xff1a; 研究背景&#xff1a; 异质集成不同晶体材料因其在高性能多功能电子和光子…

mybatis-plus 的saveBatch性能分析

Mybatis-Plus 的批量保存saveBatch 性能分析 目录 Mybatis-Plus 的批量保存saveBatch 性能分析背景批量保存的使用方案循环插入使用PreparedStatement 预编译优点&#xff1a;缺点&#xff1a; Mybatis-Plus 的saveBatchMybatis-Plus实现真正的批量插入自定义sql注入器定义通用…