【Linux】文件描述符和重定向

目录

一、回顾C文件

二、系统文件I/O

2.1 系统调用 open 

2.2 标志位传参

2.3 系统调用 write

2.4 文件描述符fd

2.5 struct file

2.6 fd的分配规则

2.7 重定向

2.7.1 基本原理:

2.7.2 系统调用 dup2

2.8 标准错误


一、回顾C文件

文件 = 内容 + 属性

  1. 对文件的操作:a.对内容操作  b.对属性操作
  2. 内容是数据,属性其实也是数据 —— 存储文件,必须既存储内容,又存储属性数据 —— 默认文件是在磁盘中的。
  3. 要访问一个文件的时候,要先把这个文件打开。
    把含有打开文件的代码(fopen)编译成可执行程序,并将程序运行起来,在执行fopen函数时才会打开这个文件。进程是Linux中最常见的文件打开者。
  4. 文件打开前,是普通的磁盘文件。打开后,文件被加载到内存。
  5. 一个进程可以打开多个文件、多个进程可以打开多个文件。加载到内存中被打开的文件,可能会存在多个。
  6. 加载磁盘上的文件,一定会涉及到磁盘设备,该操作由OS执行。
  7. 操作系统在运行中,可能会打开很多个文件,也需要管理很多文件,就需要“先描述再组织”
  8. 一个文件要被打开,一定要先在内核中形成被打开的文件对象,该对象包含文件的很多属性。例如 struct file
  9. 于是对打开文件的管理,就变成了对链表的增删查改。
  10. 对文件管理的学习,就要研究被打开的文件在内存中的表现,和没有被打开的文件在磁盘中如何存储。

fopen函数

        #include <stdio.h>

        FILE *fopen(const char *filename, const char *mode);

参数:

  • filename:指向包含文件路径的字符串指针。
  • mode:指向包含文件打开模式的字符串指针。

文件打开模式:

  • r:以读取方式打开文件,文件必须存在。
  • w:以写入方式打开文件,如果文件已存在,会将文件长度截断成0,从文件开头处写入;文件不存在则创建文件。类似echo "..." > log.txt
  • a:以追加方式打开文件,写入数据将添加到文件末尾。类似echo "..." >> log.txt
  • +:允许读写文件。
  • b:以二进制模式打开文件(通常用于非文本文件)。
  • t:以文本模式打开文件(默认模式)。

二、系统文件I/O

综上所述,文件是在磁盘中的,要被访问就要先被打开,打开文件的本质是将文件加载到内存里,加载的过程由操作系统完成,因此一个进程需要通过操作系统打开文件。操作系统管理硬件,会提供许多系统调用接口。C语言打开文件的接口,底层一定封装了系统调用接口。
w 和 a 方式打开文件,底层调用的就是open。

2.1 系统调用 open 

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:标志位,指定打开文件的选项。
    O_RDONLY: 只读打开
    O_WRONLY: 只写打开
    O_RDWR : 读,写打开
                       这三个常量,必须指定一个且只能指定一个
    O_CREAT : 若文件不存在,则创建它。需要使用mode选项,来指明新文件的访问权限
    O_APPEND: 追加写。
    O_TRUNC:如果文件已存在,则截断文件内容。
  • mode:指定文件权限的模式,仅在 O_CREAT 标志被设置时使用。

返回值:

  • 文件成功打开,返回一个非负整数的文件描述符。
  • 文件打开失败,返回 -1,并且可以通过 errno 获取错误信息。

注:

  • open 函数具体使用哪个,和具体应用场景相关。如目标文件不存在,需要open创建,此时第三个参数表示创建文件的默认权限,否则使用两个参数的open。
  • open 创建打开文件时如果flags设置为O_WRONLY | O_CREAT | O_TRUNC,且没有设置mode权限,文件被创建为具有默认权限的文件,通常是 0644。
  • 创建文件时,权限为:mode & ~umask(权限掩码),可使用系统调用umask()设置文件创建时的权限掩码。可以直接设置umask(0),这样设置的mode就是新创建文件的权限。但是这种方法不推荐,推荐使用系统默认的umask。

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

           mode_t umask(mode_t mask);

  • fopen的w方式打开相当于open选项的O_WRONLY | O_CREAT | O_TRUNC,即按照写方式打开,如果文件不存在就创建它,打开时会先清空文件内容。

  • fopen的a方式打开相当于open选项的O_WRONLY | O_CREAT | O_APPEND

  • 由此可见,C语言提供的库函数会封装某些底层的系统调用。

  • open创建新文件时先有struct file,再去同步磁盘

#include <stdio.h>    
#include <sys/types.h>    
#include <sys/stat.h>    
#include <fcntl.h>    
#include <string.h>    
#include <unistd.h>    
    
    
int main()    
{    
    //FILE *fp = fopen("log.txt","w");    
    //...    
    //fclose(fp);    
    
    //umask(0);    
    int fd = open("log.txt",O_WRONLY | O_CREAT | O_TRUNC, 0666);    
    if(fd < 0)    
    {    
        perror("open");                                                                                          
        return 1;    
    }    
    const char* msg = "aaaaaaaa\n";    
    write(fd, msg, strlen(msg));//strlen不需要+1
                                                                                                                 
    int fd1 = open("log1.txt",O_WRONLY | O_CREAT | O_TRUNC, 0666);
    int fd2 = open("log2.txt",O_WRONLY | O_CREAT | O_TRUNC, 0666);
    int fd3 = open("log3.txt",O_WRONLY | O_CREAT | O_TRUNC, 0666);
    int fd4 = open("log4.txt",O_WRONLY | O_CREAT | O_TRUNC, 0666);

    printf("fd1 : %d\n",fd1);
    printf("fd2 : %d\n",fd2);
    printf("fd3 : %d\n",fd3);
    printf("fd4 : %d\n",fd4);

    close(fd1);
    close(fd2);
    close(fd3);
    close(fd4);
    return 0;
}

 

2.2 标志位传参

一般标志位是两态的数据(0/1),那么就可以用 flags 中32个不同的比特位传参。这样就可以实现同时传递多个比特位。 

#include <stdio.h>    
    
#define Print1 1        //0001    
#define Print2 (1<<1)   //0010    
#define Print3 (1<<2)   //0100    
#define Print4 (1<<3)   //1000    
    
void Print(int flags)    
{    
    if(flags&Print1) printf("hello 1\n");    
    if(flags&Print2) printf("hello 2\n");    
    if(flags&Print3) printf("hello 3\n");    
    if(flags&Print4) printf("hello 4\n");    
}    
    
int main()    
{    
    Print(Print1);    
    Print(Print1 | Print2);    
    Print(Print1 | Print2 | Print3);    
    Print(Print1 | Print2 | Print3 | Print4);                                                                    
    return 0;    
} 

 

2.3 系统调用 write

write :用于向文件描述符指定的文件或设备写入数据。

       #include <unistd.h>

       ssize_t write(int fd, const void *buf, size_t count);

参数:

  • fd:文件描述符,用于指定要写入的文件或设备。
  • buf:指向要写入数据的缓冲区的指针。
  • count:要写入的字节数。

返回值:

  • 写入成功,返回实际写入的字节数。
  • 写入失败,返回 -1,并且可以通过 errno 获取错误信息。

注:

  • 当我们想向一个文件中写入字符串的时候,count的值不需要strlen()+1,因为 \0 结尾是C语言的规定,不是文件的规定!
  • 使用默认的写方式打开文件(O_WRONLY),write写入内容时,没有将文件原内容清空,而是覆盖式的写入。
  • 如果想写入文件时把文件清空,需要在标志位选项上加上O_TRUNC,表示截断文件内容。

2.4 文件描述符fd

文件描述符fd(file descriptor)是一个非负整数,用于标识一个打开的文件。

内存中有许多进程和进程打开的文件,于是操作系统就要解决进程和打开文件对应关系的维护问题!

struct file 是被打开文件的描述结构体。操作系统在内核中,为进程设置了一个指针files,指向结构体 struct files_struct,该结构体包含一个指针数组struct file* fd_array[],数组中的每个元素都是一个指向 struct file 的指针,当进程打开一个文件时(open),OS创建了一个struct file,将该file结构体的地址填入到指针数组中,此时open会向上层返回填入数组位置的下标,这个下标就是文件描述符files_struct就是进程文件描述符表


使用write时,第一个参数就是fd,就可以根据fd找到对应的 struct file。

在上面示例的fd数字为4、5、6、7,(3是第一个fd)没有0、1、2。

原因:

进程在运行时,默认打开3个文件:

  1. 标准输入(键盘)        stdin        0
  2. 标准输出(显示器)    stdout      1
  3. 标准错误(显示器)    stderr       2

它们在进程文件描述符表中以及占据了前3个,所以后续文件从下标3开始。

stdin、stdout、stderr是 FILE* 类型,在C语言中 FILE 是一个结构体类型。因为操作系统访问文件只认文件描述符,所以FILE结构体中也必定要封装文件描述符。结果是FILE结构体中确实有文件描述符,名称为 _fileno

    printf("stdin->fd: %d\n",stdin->_fileno);    
    printf("stdout->fd: %d\n",stdout->_fileno);    
    printf("stderr->fd: %d\n",stderr->_fileno);    
    
    FILE *fp = fopen("log.txt","w");    
    printf("fp->fd: %d\n",fp->_fileno);    
    fclose(fp);   

OS/C语言为什么默认要把0,1,2,stdin,stdout,stderr打开呢?

答:是为了让程序员默认进行标准的输入输出代码编写!

如何理解“一切皆文件”?
在Linux中,所有的输入和输出都被抽象为文件。这意味着无论是从键盘输入、从文件读取,还是向屏幕输出、向文件写入,都使用文件描述符来处理。
那么如何使用文件描述符来处理? ——每个文件描述符对应着一个文件,即对应着一个strcut file。strcut file封装了与文件操作相关的函数指针,如 read、write,分别指向对应硬件的读和写方法(硬件被操作系统“先描述再组织”,每个硬件设备对应的结构体有它自己的读和写方法,因为硬件的作用就是为了实现某些读或者写操作)。从此之后读取硬件不需要使用硬件的读写方法,而是使用 file 结构体中的函数指针调用。这一结构体层称作VFS(虚拟文件系统),用户不再需要关注底层硬件的差异,只用关注文件(因为读写方法全都一样)。

这种操作类似于C++中的多态。虚拟文件系统看作基类,硬件对应子类,基类是一层虚方法,不实现具体的方法,而是由子类实现具体的方法。

补充:

  • 文件操作系统调用:所有的文件操作,如读取(read)、写入(write)、打开(open)、关闭(close)等,都是通过系统调用进行的。这些系统调用与文件操作密切相关。
  • 用户空间和内核空间:文件操作接口在用户空间和内核空间之间提供了一个统一的接口。用户空间程序通过文件描述符和系统调用与内核空间交互,实现文件操作。
  • 设备驱动程序:设备驱动程序是内核的一部分,它们将硬件设备与文件系统中的设备文件关联起来。驱动程序通过文件操作接口与用户空间程序交互。

通过“一切皆文件”的概念,Linux系统提供了一个统一的方式来处理不同的输入和输出,这使得系统更加模块化、易于管理和扩展。需要注意的是,虽然从用户空间的角度来看,所有输入和输出都是通过文件描述符处理的,但在内核中,这些操作的实现可能会依赖于特定的设备驱动程序和硬件接口。

虚拟文件系统(Virtual File System,VFS)是内核的一个抽象层,它提供了一个统一的接口,使得不同的文件系统可以被内核和用户空间应用程序以一致的方式访问。VFS的主要作用是将底层的文件系统实现细节抽象化,从而允许用户空间应用程序和内核中的其他模块无需关心具体文件系统的实现细节。

2.5 struct file

在Linux内核中,struct file 是一个关键的数据结构,用于表示打开的文件。它包含了与文件操作相关的信息,如文件操作函数指针、文件状态、文件访问权限等。struct file 是内核中文件系统抽象层(VFS)的一部分,它为用户空间程序和内核中的其他模块提供了一个统一的接口,用于执行文件操作。

类似于task_struct,struct file 在磁盘中不存在,而是打开文件时,操作系统在内核中创建的struct file的节点,属于内核数据结构,专门用来管理被打开文件。这个文件不止指磁盘文件,还包括磁盘设备本身、键盘显示器等。

struct file 结构体包含了指向文件缓冲区的指针(f_mapping 字段),这些缓冲区用于缓存文件操作的数据,以提高文件访问的效率。 

当内核要读取磁盘中的文件数据,根据冯诺依曼体系结构,需要先把文件数据加载到文件缓冲区(内存)。内核需要读取文件数据时,它会首先检查文件缓冲区中是否已经缓存了所需的数据。

  • 如果读取文件数据时发现数据不在文件缓冲区中,此时发生缺页中断,将磁盘中的数据加载到缓冲区中。
  • 如果数据在缓冲区中,内核可以直接从缓冲区中读取,而无需触发缺页中断。
  • 无论读写,都要先把数据加载到文件缓冲区中。
  • 我们在应用层进行数据的读写本质是将内核缓冲区中的数据进行来回拷贝

文件操作方法集 f_op

f_op 是一个struct file 中的字段,它指向一个 struct file_operations 数据结构。这个数据结构包含了与文件操作相关的函数指针集合。

2.6 fd的分配规则

  • 进程默认已经打开了0,1,2,我们可以直接使用0,1,2进行数据的访问!
  • 文件描述符的分配规则是,寻找最小的,没有被使用的数据的位置,分配给指定的打开文件!(文件描述符可以被复用)
  • 文件关闭时,fd_array中对应的数据被清空。内核会将其标记为可复用,并在下次需要打开文件时重新分配给新的文件。
  • 当一个进程通过 fork 创建一个子进程时,子进程会继承父进程的文件描述符表。子进程可以使用与父进程相同的文件描述符来访问相同的文件。

2.7 重定向

2.7.1 基本原理:

文件描述符1删除时,无法打印内容到显示器。新创建的文件的fd为1,再向1打印就是向这个文件中打印。(这个过程叫做输出重定向)

    close(1);    
    int fd = open("log.txt", O_CREAT | O_WRONLY | O_TRUNC, 0666);    
    if(fd < 0)    
    {    
        perror("open");    
        return 1;    
    }    
    printf("fd: %d\n",fd);    
    printf("stdout->fd: %d\n",stdout->_fileno);                                                                  
    fflush(stdout);    
    close(fd);  

现象:不向显示器打印,而是向文件打印。 

原因: close(1)后,新打开的文件的fd为1,printf函数只认标准输出(stdout),而stdout的_fileno为1,所以printf函数向1号文件描述符对应的文件打印,不管1号文件对应的是不是显示器。
(注:printf("fd: %d\n",fd);   相当于 fprintf(stdout, "fd: %d\n",fd);   )

所以完成重定向功能,只要更改fd_array内容(下标不变)即可。

重定向的本质,其实就是修改文件描述符表特定数组下标的内容。

如果没有 fflush() ,打印的内容不会打印到对应的文件中。因为C语言提供了两种缓冲区:用户级缓冲区和内核级缓冲区。使用printf、fprintf时,要打印的数据先拷贝在用户级缓冲区,然后fflush()再将缓冲区数据拷贝到fd指定文件里。(在后面缓冲区部分讲述)

如果将上面open的选项改为:O_CREAT | O_WRONLY | O_APPEND ,代码的工作就变成了追加重定向

输入重定向如下:(本来应该从键盘上输入)

    close(0);    
    int fd = open("log.txt", O_RDONLY);    
    if(fd < 0)    
    {    
        perror("open");    
        return 1;    
    }    
    
    char buffer[1024];    
    fread(buffer, 1, sizeof(buffer), stdin);    
    printf("%s\n",buffer);    
    close(fd);   

以上的重定向操作都需要关闭原本的文件、再打开新的文件,这种操作有些繁琐而且还容易出错。

2.7.2 系统调用 dup2

Linux中提供了一个系统调用 dup2 ,用于复制一个已存在的文件描述符到另一个已存在的文件描述符。实现文件描述符表级别的数组内容的拷贝

#include <unistd.h>

int dup2(int oldfd, int newfd, int flags);

参数:

  • oldfd:要复制的文件描述符。
  • newfd:目标文件描述符,dup2 会将 oldfd 的内容复制到这个位置。
  • flags:可选参数,用于指定复制行为。通常使用 0,表示复制后关闭 oldfd。

返回值:

  • 成功,dup2 返回新的文件描述符值。
  • 失败,返回 -1,并且可以通过 errno 获取错误信息。

功能:

  • 将 oldfd 指向的文件描述符的内容复制到 newfd 指向的位置,如果 newfd 已经打开,它会先关闭 newfd。这意味着 oldfd 和 newfd 最终指向同一个文件描述符。
    // 打开文件    
    int fd = open("log.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);    
    if (fd == -1) {    
        perror("open failed");    
        return 1;    
    }    
    
    printf("%d\n",stdout->_fileno); 
   
    // 将标准输出重定向到文件描述符    
    if (dup2(fd, stdout->_fileno ) == -1) {    
        perror("dup2 failed");    
        close(fd);    
        return 1;    
    }    
    
    // 关闭原始文件描述符    
    close(fd);                                                                                                   
    
    // 输出内容到文件    
    printf("Hello, World!\n");    

    // 关闭文件描述符    
    close(stdout->_fileno); 

我们向标准输出写入一些内容,这些内容实际上会被写入 log.txt 文件,因为标准输出现在指向这个文件。

2.8 标准错误

在Linux中,标准错误(stderr)通常用于输出错误消息和警告信息,而不是程序的常规输出。

  • 标准错误流被分配文件描述符2
  • 可以通过重定向操作符 2> 或 2>> 将标准错误重定向到文件
  • 标准错误流和标准输出流是独立的,可以分别重定向到不同的目的地。

如果要将标准错误重定向到标准输出,可以使用 2>&1

类似于 ls -a -l > log.txt ,将打印的内容重定向输出到log.txt文件,可以理解成
ls -a -l 1>log.txt ,即把输入到 1(stdout) 的内容输入到 log.txt。那么 2>&1 就是把要输出到2(stderr)的内容重定向输入到1对应的内容中。

./mybin 1 > log.txt 2>&1  或 ./mybin > log.txt 2>&1  
结果:把mybin的标准输入和标准输出都重定向到 log.txt 中

为什么要有标准错误?

答:

  1. 区分正常输出和错误输出:标准错误用于输出程序执行过程中的警告和错误信息,而标准输出用于输出程序的正常输出和结果。这样的区分使得用户和开发者可以更容易地识别和处理程序的错误,而不需要区分正常输出和错误输出。
  2. 多目的地输出:标准错误允许程序将错误输出重定向到不同的目的地,如文件或终端。例如,如果程序的输出被重定向到一个文件,而错误信息仍然需要显示在终端上,可以使用 2>&1 操作符将错误输出重定向到标准输出。
  3. 调试和日志记录:标准错误流通常被用于输出调试信息和日志记录。由于标准错误输出可以被重定向到文件,这使得记录程序执行过程中的错误和警告信息变得更加容易。
  4. 交互式和脚本环境:在交互式环境中,标准错误输出通常会显示在终端上,这有助于用户立即识别和处理程序执行过程中的问题。在脚本环境中,标准错误输出可以被重定向到一个日志文件,以便在脚本执行后查看错误信息。
  5. 多进程和子进程:子进程会继承父进程的标准错误流。这意味着如果父进程的标准错误被重定向,子进程的标准错误也会被重定向到相同的位置。

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

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

相关文章

3分钟,学会一个 Lambda 小知识之【流API】

之前给大家介绍的 Lambda 小知识还记得吗&#xff1f;今天再来给大家介绍&#xff0c; 流API 的相关知识要点。 流API Stream是Java8中处理集合的关键抽象概念&#xff0c;它可以指定你对集合的&#xff0c;可以执行查找、过滤和映射等数据操作。 Stream 使用一种类似用 SQ…

资料如何打印更省钱

在日常工作和学习中&#xff0c;我们经常需要打印各种资料。然而&#xff0c;随着打印成本的不断提高&#xff0c;如何更省钱地打印资料成为了大家关注的焦点。今天&#xff0c;就为大家分享一些资料打印的省钱技巧&#xff0c;并推荐一个省钱又省心的打印平台。 首先&#xff…

冥想的时候怎么专注自己

冥想的时候怎么专注自己&#xff1f;我国传统的打坐养生功法&#xff0c;实际最早可追溯到五千年前的黄帝时代。   每天投资两个半小时的打坐&#xff0c;有上千年之久的功效。因为当你们打坐进入永恒时&#xff0c;时间停止了。这不只是两个半小时&#xff0c;而是百千万亿年…

深入探讨黑盒测试:等价类划分与边界值分析

文章目录 概要黑盒测试等价类划分边界值分析 设计测试用例小结 概要 在软件开发领域&#xff0c;测试是确保产品质量的关键步骤之一。而黑盒测试方法作为其中的一种&#xff0c;通过关注输入与输出之间的关系&#xff0c;而不考虑内部实现的细节&#xff0c;被广泛应用于各种软…

使用git系统来更新FreeBSD ports源码

FreeBSD跟其它系统相比一大特色就是ports系统。 The Ports Collection is a set of Makefiles, patches, and description files. Each set of these files is used to compile and install an individual application on FreeBSD, and is called a port. By default, the Po…

5 款免费好用的精品软件推荐!

AI视频生成&#xff1a;小说文案智能分镜智能识别角色和场景批量Ai绘图自动配音添加音乐一键合成视频https://aitools.jurilu.com/ 1.系统优化软件 - Wise Care 365 Wise Care 365 -全球最快的系统优化软件&#xff01;精简系统、管理启动项、清理和优化注册表、清理个人隐私…

基于51单片机的冰箱控制系统设计( proteus仿真+程序+设计报告+原理图+讲解视频)

基于51单片机冰箱控制系统设计( proteus仿真程序设计报告原理图讲解视频&#xff09; 基于51单片机冰箱控制系统设计 1. 主要功能&#xff1a;2. 讲解视频&#xff1a;3. 仿真4. 程序代码5. 设计报告6. 原理图7. 设计资料内容清单&&下载链接资料下载链接&#xff1a; …

Debian mariadb 10.11 XXXX message from server: “Too many connections“

问题表现 报错信息&#xff1a;Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: Data source rejected establishment of connection, message from server: "Too many connections" 处理步骤 1、尝试能不能通过终端登录&…

从头理解transformer,注意力机制(下)

交叉注意力 交叉注意力里面q和KV生成的数据不一样 自注意力机制就是闷头自学 解码器里面的每一层都会拿着编码器结果进行参考&#xff0c;然后比较相互之间的差异。每做一次注意力计算都需要校准一次 编码器和解码器是可以并行进行训练的 训练过程 好久不见输入到编码器&…

SpringBoot中@Value注入失败

首先&#xff0c;不支持static的 解决&#xff1a;使用setter方法进行属性的赋值,并且setter方法不能有static 生成set/get方法就可以了&#xff0c;然后Value 放在set上

《系统架构设计师教程(第2版)》第10章-软件架构的演化和维护-07-软件架构维护

文章目录 1. 软件架构知识管理1.1 概念1.2 架构知识的获取1.3 作用1.4 架构知识管理的现状 2 软件架构修改管理3 软件架构版本管理4. 示例4.1 背景4.2 数据获取4.3 数据计算4.4 结果分析4.4.1 圈复杂度 (CCN)4.4.2 扇入扇出度 (FFC)4.4.3 模块间耦合度 (CBO)4.4.4 模块的响应 (…

x264 场景切换检测算法分析

x264 编码器场景切换 在 x264 编码器中,场景切换(Scene Cut)检测是一个重要的特性,它用于识别视频中不同场景之间的过渡点。这些过渡点通常是视觉上显著不同的帧,比如从一个镜头切换到另一个镜头。在这些点插入关键帧(I帧)可以提高视频的随机访问性和编码效率。 入口函…

vue 百度地图点击marker修改marker图片,其他marker图片不变。

解决思路&#xff0c;就是直接替换对应marker的图片。获取marker对象判断点击的marker替换成新图片&#xff0c;上一个被点击的就替换成老图片。 marker.name tag;marker.id i; //一定要设置id&#xff0c;我这里是设置的循环key值&#xff0c;要唯一性。map.addOverlay(mark…

SSRF(服务器端请求伪造)的学习以及相关例题(上)

目录 一、SSRF的介绍 二、漏洞产生的原因 三、利用SSRF可以实现的效果&#xff08;攻击方式&#xff09; 四、SSRF的利用 五、SSRF中的函数 file_get_content() 、fsockopen() 、curl_exec() 1.file_get_content()&#xff1a; 2.fsockopen(): 3.curl_exec()&#xff1…

【鸿蒙开发】第二十四章 IPC与RPC进程间通讯服务

1 IPC与RPC通信概述 IPC&#xff08;Inter-Process Communication&#xff09;与RPC&#xff08;Remote Procedure Call&#xff09;用于实现跨进程通信&#xff0c;不同的是前者使用Binder驱动&#xff0c;用于设备内的跨进程通信&#xff0c;后者使用软总线驱动&#xff0c;…

算法设计与分析(超详解!) 第三节 贪婪算法

1.贪心算法基础 1.贪心算法的基本思想 贪心算法是从问题的某一个初始解出发&#xff0c;向给定的目标推进。但它与普通递推求解过程不同的是&#xff0c;其推动的每一步不是依据某一固定的递推式&#xff0c;而是做一个当时看似最佳的贪心选择&#xff0c;不断地将问题实例归…

【选择结构程序设计-谭浩强适配】(适合专升本、考研)

无偿分享学习资料&#xff0c;需要的小伙伴评论区或私信dd。。。 无偿分享学习资料&#xff0c;需要的小伙伴评论区或私信dd。。。 无偿分享学习资料&#xff0c;需要的小伙伴评论区或私信dd。。。 完整资料如下&#xff1a;纯干货、纯干货、纯干货&#xff01;&#xff01;…

uni-app跨端兼容

1.样式兼容 小程序端不支持*选择器&#xff0c;可以使用&#xff08;view,text&#xff09; 页面视口差异(tabar页、普通页) H5端默认开始scoped 例如骨架屏样式出现问题&#xff0c;需要将之前的样式拷贝到骨架屏中 提示&#xff1a;H5端是单页面应用&#xff0c;scoped隔离…

【吊打面试官系列】Java高并发篇 - 如何创建守护线程?

大家好&#xff0c;我是锋哥。今天分享关于 【如何创建守护线程&#xff1f;】面试题&#xff0c;希望对大家有帮助&#xff1b; 如何创建守护线程&#xff1f; 使用 Thread 类的 setDaemon(true)方法可以将线程设置为守护线程&#xff0c;需要注意的是&#xff0c;需要在调用 …

家政服务新体验——家政小程序开发,让生活更轻松!

一、引言 随着现代生活节奏的加快&#xff0c;家政服务已经成为越来越多家庭不可或缺的一部分。然而&#xff0c;传统家政服务方式往往存在预约不便、服务质量参差不齐等问题。为了解决这些问题&#xff0c;我们精心打造了一款家政小程序&#xff0c;为您带来全新的家政服务体…