操作系统:初识文件

目录

1.初识文件

2.文件操作

2.1.文件操作接口

2.2.文件描述符fd

3.缓冲区

3.1.简要介绍

3.2.重定向 

3.2.1.输出重定向

3.2.2.追加重定向 

 3.2.3.输入重定向

3.2.4.调用接口实现重定向

3.2.5.文件重定向的作用

3.3.用户缓冲区与内核缓冲区

3.4.缓冲区和重定向


我们在C语言学习时,初步接触过几个C语言的文件接口,也就是我们的文件学习仅仅停留在调用这几个接口的水平上,接下来我们就从文件的本质开始学习,直到理解操作系统的文件管理和文件系统!那么我们开始吧

1.初识文件

在计算机操作系统中,文件是有创建者所定义的、具有文件名的一组相关元素的集合,并且在实际的操作系统中,文件也是跟进程一样,拥有自己的内容和属性,内容的本质上就是数据,属性的本质也是数据。那么就有我们对文件的一系列操作,实际上就是对文件的相关数据进行操作。

比如:我们在Windows修改文件内容时,不仅文件的内容发生改变,文件的属性,例如:修改时间、大小等属性也可能发生改变。

文件 = 内容 + 属性 


对文件的最基本的访问就是打开文件,那什么是打开文件呢?

在操作系统中,打开文件的本质就是创建进程来访问文件。文件打开前就是一个存储在外存(磁盘)的数据集合,当我们访问时,需要将文件加载到内存中。没有被打开的文件依旧存留在外存里,这里我们不作考虑。

那么加载(打开)文件一定会涉及到访问磁盘硬件,也就是需要操作系统来实现将外存上的文件通过进程来搬运到内存中,再结合我们之前学习的进程管理模块,操作系统也会进行“文件管理”,况且实际场景下,一个进程可以打开(访问)多个文件,那么多个进程的情况下就需要我们将打开的文件进行管理,也就需要将进程和对应文件联系起来 。也就回到了“先描述再组织”!

2.文件操作

2.1.文件操作接口

C语言文件接口

打开文件

// 具体使用
FILE* fp = fopen("文件名", "打开方式");
  • 当文件名不存在时,会在当前目录下,创建一个以该文件名命名的文件
  • 打开方式:为 “r”时表示只读;"w"时表示先清空文件在写入;"a"表示追加写入


 系统调用接口

打开文件

int fd = open("文件名", "文件标志位");
int fd = open("文件名", "文件标志位", "文件权限");

这里我们讲一下文件标记位,我们知道一个int的整型是32位(1字节8个比特位),标志位一般为2个字节,那么就可以传入多个标志位给一个int类型,在通过一定的运算找到我们想要的操作。这里用一个demo来展示一下,32位省略写为4位

#include<stdio.h>

#define Print1 1      // 0001
#define Print2 (1<<1) // 0010
#define Print3 (1<<2) // 0100
#define Print4 (1<<3) // 1000

void show(int flags)
{
    if(flags & Print1 != 0)
        printf("hello 1\n");
    if(flags & Print2 != 0)
        printf("hello 2\n");
    if(flags & Print3 != 0)
        printf("hello 3\n");
    if(flags & Print4 != 0)
        printf("hello 4\n");  
}
int main()
{
    show(Print1);
    printf("\n");
    show(Print1 | Print2);
    printf("\n");
    show(Print1 | Print2 | Print3);
    printf("\n");
    show(Print4);
}

具体在Linux中我们通过宏替换来实现这些标记位操作

  • O_RDONLY   只读文件
  • O_WRONLY  只写文件
  • O_CREAT     文件不存在时,创建它
  • O_TRUNC     截断这个文件,清空文件内容
  • O_APPEND   在文件后追加

所以最终我们可以通过 或 操作来同时进行不同的选择。

例如:以只读的形式,打开log.txt文件,并且赋予0664权限,当文件不存在时自动创建。

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


写入文件

write("文件描述符", "传入的字符串", "字符串长度");

值得注意的是,这里wire只完成写入的工作,对于覆盖写入、追加写入、清空写入这三种情况不受wire的影响,需要通过open函数的标记位来实现!


当我们在研究系统调用接口时,我们发现C语言的接口也可以实现这些功能,并且C语言的接口调用更加得心应手。这是为什么呢?这里我们就要讲一下系统和语言的关系了!

我们知道操作系统是打开文件的那个角色,C语言并不能直接地访问文件,然而C语言也可以实现系统调用的接口,注定了语言级别这些上层的接口封装了底层的这些系统级别的接口!

2.2.文件描述符fd

当我们在辨析语言级别访问文件和系统级别访问文件时,我们发现open函数成功调用,最后用一个int fd 来接收,这个整型称为文件描述符。讲文件标识符之前我们需要知道,文件在操作系统中的表现。

我们在引子中提到:文件 = 内容 + 属性,并且进程运行的期间可能会涉及多个文件,需要被操作系统管理!也就是需要被描述成类似进程控制块(PCB)这样的数据结构。

因为一个进程能够实现多个文件,那么对应一个进程就需要一个容器来存放这些文件,我们之前在进程管理中并不陌生用地址来实现映射关系最后进行管理。在Linux中具体实现如图所示:

那么这样子就实现了初步的文件管理,但是究竟什么是文件描述符呢?我们看一下这个图,发现在这个存放新文件的指针数组对应的下标从3开始对应着新文件,也就是这个下标就是文件标识符!

更加清晰的解释:文件描述符就是从0开始的小整数,也就是进程管理文件的指针数组的下标。其中0,1,2分别对应着标准输入,标准输出,标准错误

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


接下来我们重点讲一下0,1,2

进程在创建时会默认打开3个文件,分别是标准输入、标准输出、标准错误。

我们在C语言中也有标准输入,标准输出,标准错误,我们知道在系统中这三个是文件,在C语言中类型也恰好为FILE* 也对应着文件!

FILE本质上是 typedef 后的struct FILE,也就是一个结构体对象。另外在C语言的FILE结构体中,内置着一个整型int _fileno也就是文件描述符

#include<stdio.h>
int main()
{
    printf("stdin->fd: %d\n", stdin->_fileno);
    printf("stdout->fd: %d\n", stdout->_fileno);
    printf("stderr->fd: %d\n", stderr->_fileno);

    FILE* newFile = fopen("log.txt", "w");
    printf("newFile->fd: %d\n", newFile->_fileno);
}

这里我们验证了0,1,2和新文件的从3开始,那么如果我们在创建新文件之前就关闭了stdin,stdout,stderr这三个文件,最终我们能看到什么结果呢?这里就不做演示了!

答:对于文件描述符的分配规则,在这个file_struct这个数组中找到没有被使用的最小的一个下标,作为新的文件描述符

另外,标准输入 对应着键盘这个硬件,标准输出 和 标准错误 对应着显示器。我们知道他们在操作系统中对应着文件,而文件往往对应着读写这两个功能,对于不同的硬件,读写功能的权限也不一定一样。也就是对于硬件的读写方法我们也需要进行对应,具体操作如图:

最终我们实现了将硬件抽象成文件!这也就是Linux一切皆文件的体现 

3.缓冲区

3.1.简要介绍

对于一个描述文件的结构体,不仅存放着关于文件的所有内容和属性,还拥有着一块属于自己的缓冲区。当我们在进行对文件的读取时,需要将文件数据加载到内存。如果需要进行写入时也是需要加载数据到内存中。而这这些数据一般存放在文件定义的这一块文件缓冲区中!

那么用户在应用层进行数据读写的本质就是:在内存进行数据的读写后,再进行内存与外存之间的拷贝,也就是 将内核缓冲区的数据进行来回的拷贝!!!

3.2.重定向 

I/O重定向功能可以改变输出内容发送的目的地,也可以改变输入内容的来源地。通常来说,输出内容显示在屏幕上,输入内容来自于键盘。但是使用I/O重定向功能可以改变这一惯例。

......相信大家并没有看懂,接下来我用几个小demo来体会一下重定向功能。

3.2.1.输出重定向

代码如下:我们的逻辑是关闭stdout文件,然后再进行打印,这时我们大概能猜出显示器没有打印结果,因为我们关闭了printf对应的显示器文件!

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

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

    printf("#############\n");
    printf("fd: %d\n", fd);
    printf("stdout->fd: %d\n", stdout->_fileno);
    fflush(stdout);
    close(fd);
}

如图是在Linux下的运行结果

我们发现当我们关闭1后,原本printf打印到的位置是显示器,而显示器打印出结果的本质是对显示器进行写入,因为我们这里关闭了1,并且新的文件的fd也随之设置为1,最终这些打印的内容就全部被写入进了log.txt文件中,这个就是输出重定向。


3.2.2.追加重定向 

那么当我们将打开文件方式由截断,改为追加时,也就是稍微修改一下这个demo

这时我们通过不断的调用这个进程,我们会发现会不断地往log.txt文件中打印一样的内容,那么这个时候我们应该能猜到这个追加重定向可以用来打印日志了吧!!!

 当我们把这几个demo中的fflush函数关闭时,就不存在这种情况了,而fflush函数是刷新当前缓冲区,那么我们也可以大胆猜测:重定向与缓冲区有关 


 3.2.3.输入重定向

我们将代码修改一下后,实现关闭键盘后对stdin进行只读

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

int main()
{
    close(0);
    int fd = open("log.txt", O_CREAT | O_RDONLY, 0666);

    char buffer[10000];
    fread(buffer, 1, sizeof(buffer), stdin);
    printf("%s\n", buffer);
    close(fd);
}

最后我们发现,在Linux的命令行中打印出来的内容是log.txt文件的内容,并且我们没有输入的功能,这也符合了我们提前关闭键盘的现象。

值得讨论的是:当我们关闭stdin后对应的fd = 0转移到了log.txt中(这是底层的实现,操作系统不关心),然而操作系统只通过fd来访问文件,所以最后fread进入系统调用时,就读取到了log.txt并写入进buffer中!


3.2.4.调用接口实现重定向

我们发现在上面的demo中我们需要不断的关闭文件,来实现重定向下标文件的替换,而C语言库提供了一个函数方便我们使用

int fd = open("文件名", "方式");

dup2(fd, "需要替换的文件下标");   // 替换stdin 就为 dup2(fd,0)

重定向:上层fd不变,底层fd指向的内容在改变,本质就是修改特性文件的下标内容,也就是实现文件描述符的数组内容的拷贝。

那重定向有什么作用呢?

3.2.5.文件重定向的作用

文件重定向是一种操作系统提供的功能,它可以将一个程序的输入或输出从标准设备(通常是键盘和显示器)重定向到文件中,或者从文件中读取输入或输出。文件重定向的作用主要有以下几个方面:

  1. 输入重定向:通过将输入从键盘改为从文件中读取,可以方便地批量处理大量输入数据。例如,可以使用"<"符号将文件作为输入重定向到程序中,程序将从文件中读取数据而不是等待用户输入。

  2. 输出重定向:通过将输出从显示器改为写入到文件中,可以将程序的输出结果保存到文件中,方便后续的处理和查看。例如,可以使用">"符号将程序的输出结果重定向到文件中,而不是在显示器上显示。

  3. 错误重定向:除了标准输入和标准输出之外,还有一个标准错误输出。错误重定向可以将程序的错误信息输出到文件中,以便后续的排查和分析。例如,可以使用"2>"符号将程序的错误信息重定向到文件中。

通过文件重定向,我们可以更加灵活地处理输入输出,方便地进行批量处理、结果保存和错误排查等操作。

 在实际开发场景中,我们借助文件重定向功能可以辅助我们查看程序进行的信息,比如打印日志、错误信息在不同的文件中

下面我们再用一个demo,这里的printf和perror的区别就是打印正常信息,错误信息的接口

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

int main()
{
    fprintf(stdout, "print log\n");
    fprintf(stderr, "print error\n");

    printf("正常输出信息\n");
    perror("错误输出信息\n");
}

接下来我们在Linux输入指令

./a.out 1> log.txt 2> log.txt.error

这样子我们就可以把对应的stdout和printf的正常输入信息重定向写入到log.txt中,将错误信息重定向写入到log.txt.error中。

 重定向可以帮助我们将我们在程序运行时的信息从平时输出显示屏到输出到文件中,便于实际开发时定位查询!同理我们也可以通过文件中的数据来实现写入,而不是从键盘中写入,这样子效率也增加了!

3.3.用户缓冲区与内核缓冲区

在3.1.中的简要介绍中提及,每一个进程维护的文件结构体中都会有一个文件缓冲区来实现内存和外存之间的数据拷贝!这一块缓冲区一般是通过malloc实现的,也就是缓冲区本质上就是由用户或者系统定义并设置的一块内存!

如图为:缓冲区的大致形式

我们之前在学习操作系统时,见过这样一个场景 :

printf("hello linux");
sleep(3);

当程序进入sleep时,我们发现并没有打印出hello Linux这一句话,这时为什么呢?因为printf中并没有'\n' 而 换行符不仅可以实现换行,也是缓冲区行刷新的标志,也就是sleep时,数据存留在缓冲区中。


操作系统定义缓冲区的目的:为了效率,提高资源的利用率

我们平时要送东西给身边的朋友,一般是找时间亲自送过去。那么如果我们要送东西给远在他乡的朋友呢?如果亲自送过去会不会有一点太麻烦?回归到现实,我们一般是通过快递站点来送给朋友,这样是不是减少了麻烦,提高了你(使用者)的效率。

从缓冲区的层面上来看,缓冲区时进行数据在内外存进行拷贝的场所,一般来说我们进行拷贝有两种方式

  • 我们在缓冲区完成了数据的增删查改后再进行内外存拷贝
  • 不专门设置缓冲区,我们修改一部分,就进行内外存拷贝一部分,一收到就发送

在大部分情况下我们都选择第一种方式,这样子可以减少内外存拷贝的次数减少资源的调度。这也很合理,没有快递公司,送东西是有一件就送一件的,一般都是收取够了快递后再发送......

缓冲区的刷新方式

  • 无缓冲(立即刷新)
  • 行缓冲(行刷新)
  • 全缓冲(缓冲区满了再刷新)

一般来说,进程退出时,一般会强制刷新缓冲区!也就是sleep后进程退出时,hello Linux也会打印出来。


所以综合上面的内容,我们就能够很好的理解:操作系统会自己创建一份内核缓冲区用来接收从用户缓冲区(语言级别缓冲区)传来的数据,并且内核缓冲区也是用来进行内外存拷贝的场所。

 我们来看一下C语言库中的FILE结构体,来看看里面缓冲区结构

在/usr/include/stdio.h对FILE的声明

typedef struct _IO_FILE FILE; 

在/usr/include/libio.h中对FILE的详细定义

struct _IO_FILE {
 int _flags; /* High-order word is _IO_MAGIC; rest is flags. */
#define _IO_file_flags _flags
 
 //缓冲区相关
 /* The following pointers correspond to the C++ streambuf protocol. */
 /* Note: Tk uses the _IO_read_ptr and _IO_read_end fields directly. */
 char* _IO_read_ptr; /* Current read pointer */
 char* _IO_read_end; /* End of get area. */
 char* _IO_read_base; /* Start of putback+get area. */
 char* _IO_write_base; /* Start of put area. */
 char* _IO_write_ptr; /* Current put pointer. */
 char* _IO_write_end; /* End of put area. */
 char* _IO_buf_base; /* Start of reserve area. */
 char* _IO_buf_end; /* End of reserve area. */
 /* The following fields are used to support backing up and undo. */
 char *_IO_save_base; /* Pointer to start of non-current get area. */
 char *_IO_backup_base; /* Pointer to first valid character of backup area */
 char *_IO_save_end; /* Pointer to end of non-current get area. */
 
 struct _IO_marker *_markers;
 
 struct _IO_FILE *_chain;
 
 int _fileno; //封装的文件描述符
#if 0
 int _blksize;
#else
 int _flags2;
#endif
 _IO_off_t _old_offset; /* This used to be _offset but it's too small. */
 
#define __HAVE_COLUMN /* temporary */
 /* 1+column number of pbase(); 0 is unknown. */
 unsigned short _cur_column;
 signed char _vtable_offset;
 char _shortbuf[1];
 
 /* char* _save_gptr; char* _save_egptr; */
 
 _IO_lock_t *_lock;
#ifdef _IO_USE_OLD_IO_FILE
};

3.4.缓冲区和重定向

我们通过上面的讲述明白了,对于我们实际输入一段信息,借助C语言接口是直接写入进C语言级别缓冲区中,当我们再进行重定向时,这时我们的数据就保留在C语言缓冲区中(通过fflush函数刷新缓冲区),而写入操作系统时,操作系统只会以文件标识符fd为判断,这时我们通过修改fd,然后刷新缓冲区,将本来需要写入进stdin、stdout、stderr的内容,转而写入我们想要写入的磁盘文件中。

这里缓冲区扮演的角色就是,C语言写入操作系统前,先通过重定向“骗过”操作系统,也正是有了用户级缓冲区和系统级缓冲区,我们才能够实现重定向功能。

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

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

相关文章

适合新手小白的wordpress详细安装教程

1、下载程序 到wordpress官方网站下载wordpress程序&#xff0c;官方下载地址&#xff1a;Download | WordPress.org China 简体中文。 下载最新版的wordpress程序 https://cn.wordpress.org/latest-zh_CN.zip 2、上传程序 上传程序前先确认主机是否符合安装的环境要求&…

小白必看从零开始学习,制作产品册技巧

作为初学者&#xff0c;我们往往缺乏相关经验和技巧&#xff0c;难以打造出令人眼前一亮的产品册。不过&#xff0c;别担心&#xff01;小编将为你揭示从零开始学习制作产品册的秘诀&#xff0c;让你轻松掌握技巧&#xff0c;成为产品册制作高手&#xff01; 一、明确目标&…

学成在线项目学习

技术栈 学成在线服务端基于Spring Boot构建&#xff0c;采用Spring Cloud微服务框架。 持久层&#xff1a;MySQL、MongoDB、Redis、ElasticSearch 数据访问层&#xff1a;使用Spring Data JPA 、Mybatis、Spring Data Mongodb等 业务层&#xff1a;Spring IOC、Aop事务控制、S…

外包干了5年,技术退步明显.......

先说一下自己的情况&#xff0c;大专生&#xff0c;18年通过校招进入杭州某软件公司&#xff0c;干了接近4年的功能测试&#xff0c;今年年初&#xff0c;感觉自己不能够在这样下去了&#xff0c;长时间呆在一个舒适的环境会让一个人堕落! 而我已经在一个企业干了四年的功能测…

LVGL线条和画布功能

线条部件 线条部件由多个点连接而成&#xff0c;它可用于修饰界面或者展示数据。 要注意这里的描述&#xff0c;线条是由多个点连接而成的。 线条部件只有一个组成部分&#xff1a;主体 LV_PART_MAIN 线条是由多个点连接而成的对象&#xff0c;用户可以使用 lv_point_t 类型的…

Day35 ● 860.柠檬水找零 ● 406.根据身高重建队列 ● 452. 用最少数量的箭引爆气球

● 860.柠檬水找零 class Solution:def lemonadeChange(self, bills: List[int]) -> bool:fiveten0for bill in bills:if bill5:five1elif bill10:five-1ten1elif bill20 and ten!0:five-1ten-1else:five-3if five<0:return Falsereturn True ● 406.根据身高重建队列 cl…

SpringBoot3的RabbitMQ消息服务

目录 预备工作和配置 1.发送消息 实现类 控制层 效果 2.收消息 3.异步读取 效果 4.Work queues --工作队列模式 创建队列text2 实体类 效果 5.Subscribe--发布订阅模式 效果 6.Routing--路由模式 效果 7.Topics--通配符模式 效果 异步处理、应用解耦、流量削…

javascript基础练习题之渔夫捕鱼

一、题目要求&#xff1a;根据用户输入的年、月、日判断是打鱼还是晒网。代码中使用了isLeapYear函数来判断输入的年份是否为闰年&#xff0c;getDays函数来计算输入日期是一年中的第几天&#xff0c;然后根据计算结果来确定是打鱼还是晒网。最后代码通过弹窗提示用户是打鱼还是…

Kimi和ChatGPT做古诗词阅读理解,谁更胜一筹?

前几天发过一篇Kimi整理会议的体验教程&#xff0c;没想到大家很感兴趣&#xff0c;这次再来拿Kimi做古诗词阅读理解看看&#xff0c;同时也对比下ChatGPT的效果。 ChatGPT是几乎家喻户晓的AI大模型&#xff0c;Kimi和它对比有哪些异同点呢&#xff1f; 首先它们都是基于对话…

黑群晖Docker安装aria2-pro

前言 最近买了星际蜗牛C款当Nas&#xff0c;来满足我的存储需求&#xff0c;在之前我写过一篇docker安装aria2-pro的文章&#xff0c;既然买了nas那当然也要安装一个aria2-pro做下载器 1.安装 Container Manager 套件 可以在套件中心搜索docker找到 2.下载aria2-pro镜像 打…

欧拉函数、快速幂、扩展欧几里得算法、中国剩余定理

数据结构、算法总述&#xff1a;数据结构/算法 C/C-CSDN博客 欧拉函数 欧拉函数&#xff08;Eulers totient function&#xff09;是一个与正整数n相关的数论函数&#xff0c;通常用φ(n)表示。定义为小于或等于n的正整数中与n互质的数的个数 int phi(int x) {int res x;for…

C语言例4-15:从键盘输入一个整数,求其绝对值并输出。

代码如下&#xff1a; //从键盘输入一个整数&#xff0c;求其绝对值并输出。 #include<stdio.h> int main(void) {int n;printf("输出一个整数&#xff1a; \n");scanf("%d",&n); //从键盘输入一个整数保存至变量nif(n<0) //…

找图识字模拟键鼠编程插件奥迦插件24.3.18

名称&#xff1a;奥迦插件24.3.18更新记录24.3.183 1.增加函数SetObjectNamesEncode2.修复按键函数在有些窗口不能按下方向键的问题命令功能介绍:奥迦插件在Windows 10操作系统上使用Visual Studio 2019编写,适用于所有较新的Windows平台,是一款集网络验证,深度学习,内核,视觉,…

出席2024亚太内容分发大会,火山引擎边缘云“加速”游戏体验升级

3月26日&#xff0c;第十四届亚太内容分发大会暨CDN峰会在北京成功举办&#xff0c;火山引擎边缘云产品架构高级总监许思安出席并以《火山引擎边缘云游戏行业解决方案&#xff0c;“加速”游戏体验升级》为主题&#xff0c;分享了火山引擎边缘云在游戏行业的思考和实践。同时&a…

01使用调试工具

文章目录 前言一、用openocd打开单片机二、利用4444端口向单片机写入hex文件三、利用3333端口和gdb进行调试四、之前我出的问题总结 前言 之前写了一篇关于在linux下搭建stm32标准库的文章后&#xff0c;有一些小伙伴们还是出现了一些奇奇怪怪的错误&#xff0c;这一篇文章就是…

多渠道整合策略全攻略:HubSpot出海CRM助力企业轻松获客

在全球化日益加速的今天&#xff0c;企业纷纷寻求出海机会&#xff0c;以拓展市场、增加客户基础。在这个过程中&#xff0c;一个强大的客户关系管理系统&#xff08;CRM&#xff09;显得尤为关键。作为业内领先的CRM解决方案提供商&#xff0c;HubSpot出海CRM以其强大的核心功…

5.1 物联网RK3399项目开发实录-Android开发之ADB使用(wulianjishu666)

物联网项目开发实例&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/11VQMhHfIL9mZhNlls4wmjw?pwd0gfa 1. ADB 使用 1.1. 前言 ADB&#xff0c;全称 Android Debug Bridge&#xff0c;是 Android 的命令行调试工具&#xff0c;可以完成多种功能&#xff0c;如跟踪系…

flutter Got socket error trying to find package nested at

flutter Got socket error trying to find package nested at xxx 报错信息&#xff1a;“Got socket error trying to find package nested at” 通常出现在Flutter尝试从pub.dev获取依赖包时&#xff0c;由于网络问题导致无法连接到pub.dev或者无法正确解析包的路径。 例如&…

LIS、LCS算法模型

文章目录 1.LCS算法模型2.LIS算法模型 1.LCS算法模型 LCS问题就是给定两个序列A和B&#xff0c;求他们最长的公共子序列。 在求解时&#xff0c;我们会设dp[i][j]表示为A[1 ~ i]序列和B[1 ~ j]序列中&#xff08;不规定结尾&#xff09;的最长子序列的长度。 if(a[i]b[i]) dp…

【算法刷题 | 二叉树 04】3.27(翻转二叉树、对称二叉树、完全二叉树的节点个数、平衡二叉树、完全二叉树的所有路径)

文章目录 6.翻转二叉树6.1问题6.2解法一&#xff1a;递归6.2.1递归思路&#xff08;1&#xff09;确定递归函数的参数和返回值&#xff08;2&#xff09;确定终止条件&#xff08;3&#xff09;确定单层递归的逻辑 6.2.2全部代码 6.3解法二&#xff1a;层序遍历 7.对称二叉树7.…