【Linux】基础IO——文件描述符

news2024/7/4 6:13:56

目录

  • 什么是文件描述符
  • 标准输入、输出、错误的返回值类型FILE*的理解
  • 进程中文件描述符的分配规则
    • 重定向的原理
    • 重定向的实际使用方法dup2
  • 如何理解缓冲区

什么是文件描述符

在基础IO的上一篇博客里有提到过,系统调用open与close的返回值问题:

成功返回文件描述符;失败返回-1;

写一段简单的代码来打印文件描述符:

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

#define LOG "log.txt"

int main()
{
    umask(0);
    int fd = open(LOG, O_CREAT | O_WRONLY, 0666);
    if(fd == -1)
    {
        printf("fd: %d, errno: %d, errstring: %s\n", fd, errno, strerror(errno));
    }
    else
    {
        printf("fd: %d, errno: %d, errstring: %s\n", fd, errno, strerror(errno));
    }
    close(fd);
    return 0;
}

运行结果:
在这里插入图片描述
发现返回值文件描述符fd = 3,这是为什么呢?
——因为任何一个进程,在启动的时候,默认会打开三个文件标准输入、标准输出、标准错误

若在C、C++中,这三者的对应关系如下:

语言标准输入标准输出标准错误
Cstdinstdoutstderr
C++cincoutcerr

标准输入、标准输出、标准错误其实都是文件
而stdin、stdout、stderr都是文件在语言层的表现。(cin、cout、cerr也一样,但是是一个类)
(查询可以看到,其实这三者数据类型都是文件类型)
在这里插入图片描述
而标准输入、标准输出、标准错误对应的文件分别是:
标准输入——设备文件:键盘文件
标准输出——设备文件:显示器文件
标准错误——设备文件:显示器文件

所以打开文件的时候,文件描述符是3的原因其实就是:
0、1、2默认对应的是标准输入、标准输出、标准错误!
因为012已经默认被操作系统占用了,所以我们自己打开文件的文件描述符就是3!

文件描述符文件
0标准输入
1标准输出
2标准错误

文件描述符对应的数字0、1、2、3…到底是什么?
——先说结论:

结论:文件描述符本质就是数组的下标。

解析:
文件存储在磁盘当中,当打开一个文件的时候,OS都会在内核中创建一个struct file结构体来描述该文件,充当这个打开的文件。struct file结构体中包含着文件的大部分属性,就好像struct task_struct结构体中包含着进程的大部分舒心。对文件的增删查改就转换成了对链表的增删查改。只要找到了file,就可以得到所有文件属性和内容。
而文件是用户通过进程(调用系统调用接口)让OS打开的,我们在执行代码的时候,能最直接找到的就是进程的PCB,所以我们要维护进程和被打开文件之间的对应关系,即要维护好进程和被打开文件的映射关系:
一个进程可以打开多个文件——进程 : 被打开的文件 == 1 : n,OS为了能让进程快速找到对应的文件,在内核中定义了一个数据结构struct files_struct,在该结构体中包含着一个数组struct file* fd_array[],其中包含的类型都是file*。当从磁盘加载一个文件时OS就要创建为该文件创建一个对应的对象,然后OS就要在当前执行打开操作的进程的struct files_struct内部的数组中从上到下进行遍历,找到一个没有被使用位置,将新加载的文件的结构体对象的地址填入到数组中,这样就建立了struct files_struct与被打开文件之间的对应关系。同时在进程的结构体当中,又包含了一个struct file_struct *files;指针,在进程初始化的时候,会为该进程创建struct files_struct对象,并将地址存在struct file_struct *files指针内,这样就建立了进程与被打开文件之间的映射关系。
最后在应用层返回的文件描述符就是上面说的数组的下标,所以当使用read、write、close这些系统调用接口的时候,必须在参数列表传入文件对应的文件描述符,通过结构体对象的映射关系来找到对应的文件进行操作。
以上就是对文件描述符本质是数组的下标的理解!
对应关系如下图所示:
在这里插入图片描述


对文件写入、读取操作的进一步理解:
每个文件都要匹配一个缓冲区,比如当我们使用write(3, buffer, xxx)向缓冲区写入操作时,所做的工作过程是:我们调用了write函数 = => OS识别到了系统调用 = => 找到进程对应的pcb = => 找到file_struct结构体 = => 在file_struct结构体中找到fd_array[]数组,通过传入的文件描述符找到索引 = => 找到匹配的文件结构体file,找到后从用户层将对应的数据拷贝到缓冲区。调用结束后返回写了多少字节。而拷贝的数据什么时候由缓冲区刷新到外设磁盘,由OS自主决定。(过程如下如所示)
所以我们的IO类read、write函数本质是拷贝函数!(用户空间与内核空间进行数据的来回拷贝)
在这里插入图片描述


如何理解"一切皆文件"?

计算机有很多的硬件外设,比如键盘、显示器、网卡、磁盘等,并且这些硬件都有与之匹配的驱动程序来实现与硬件的交互,比如读写操作(read、write)。
以键盘为例:读取方法(read_keyboard();)站在内存的视角来看,就是要将数据从外设键盘读取到内存中;而键盘这个外设不需要写入方法(write_keyboard();),因为总不能从内存写入到键盘让键盘自己动,即让键盘驱动中的写入操作只有声明,函数体是空的、什么都不做即可。
与键盘类似的,计算机所有的外设都有IO操作,即驱动都有read与write功能。
再举个例子:显示器设备,在显示器显示数据,就是将数据写入到了显示器外设,所以显示器也有写入方法(write_screen();),而读取方法(read_screen();)则不需要,因为计算机是从键盘读取的数据,只是回显到了显示器上。
所有的硬件设备都是不同的,那么在Linux下一切皆文件是怎么做到的呢,是如何将不同的硬件设备都看做是文件的呢?——当打开一个硬件设备的时候,在OS内部也同样要创建一个struct file结构体对象,来描述这个硬件文件,其中包含了该文件的权限、大小、以及各种其他属性,除此之外还包含两个函数指针int (*readp)(int fd, char buffer, int size);int (*writep)(int fd, char buffer, int size);,同时这个文件的struct file结构体对象中也有一小块缓冲区。读写的两个函数指针就指向属于自己的硬件驱动中的读写方法,并且每一个硬件外设在被打开时都有一个这样的结构体对象,其中都会将属于自己的读写方法的地址填入自己的函数指针中。
在进程的struct files_struct中的数组中保存着使用的外设文件对应的结构体地址,在进程通过该数组(文件描述符)访问对应的外设时,在进程看来,所有的外设都是struct file文件结构体对象。数据拷贝放入struct file的缓冲区中,在需要进行硬件设备的读写时,只需要通过函数指针调用对应底层驱动中的方法来完成读写即可,根本不用关心底层实现的差异。而OS也有读写操作,它的读写操作本质就是拷贝,只需要将上层应用层的数据拷贝到缓冲区中,然后通过指针调用底层不同的读写方法就可以将数据放到对应的外设中,所以在文件对象struct file以上来看,就实现了Linux下一切皆文件!不用关心底层的差异化。
我们都是通过进程的方式来进行OS的访问的!而进程只能看到文件(struct file),所以才有了一切皆文件。
这其实就是使用C语言来设计面向对象的思想(多态特性)。
在这里插入图片描述


标准输入、输出、错误的返回值类型FILE*的理解

在这里插入图片描述
在这里插入图片描述
标准输入输出错误、fopen的类型都是FILE*,那么FILE是什么?
——FILE是一个结构体。


FILE是谁提供的?
——是C语言提供的。可以进入/usr/include/stdio.h文件中进行查看:
在这里插入图片描述
查看的结果:发现是_IO_FILE,类型后续会被重命名:typedef struct _IO_FILE FILE; 位置也在/usr/include/stdio.h
在这里插入图片描述
在这里插入图片描述


FILE与struct file有关系吗?FILE结构体中封装了什么?
——没有关系。并且这个结构体中必定封装了fd(系统调用的文件描述符),因为返回值为FILE*类型的例如fopen、fclose、fwrite、fread这些C的文件操作接口底层都调用了open、close、write、read这样的系统调用接口。
/usr/include/libio.h有一句:int _fileno; //封装的文件描述符
在这里插入图片描述

总结:

  • 因为IO相关函数与系统调用接口对应,并且库函数封装系统调用,所以本质上,访问文件都是通过fd访问的,所以C库当中的FILE结构体内部,必定封装了fd

并且既然返回值的FILE*是一个指针,我就可以通过指针来查看_fileno,并且结果与前面说的一致,012描述符被stdin、stdout、strerr占用,我们自己打开的文件描述符为3:

//头文件略
#define LOG "log.txt"
int main()
{
	printf("%d\n",stdin->_fileno);
	printf("%d\n",stdout->_fileno);
	printf("%d\n",stderr->_fileno);
	FILE* fp = fopen(LOG, "w");
	printf("%d\n", fp->_fileno);
	return 0;
}

进程中文件描述符的分配规则

在进程中,文件描述符的分配规则:
在文件描述符表中,会将最小的、没有被使用的数组元素分配给新文件。

重定向的原理

首先写一段测试代码:

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

#define LOG "log.txt"

int main()
{
    close(1);//关闭标准输出
    int fd = open(LOG, O_CREAT | O_WRONLY | O_TRUNC, 0666);

    printf("hello world!\n");
    printf("hello world!\n");
    printf("hello world!\n");
    printf("hello world!\n");
    printf("hello world!\n");

    return 0;
}

结果:
运行后发现没有在命令行打印,并且在生成的log.txt文件中发现了5行hello world!
在这里插入图片描述

解析:
首先因为012号文件描述符分别对应标准输入输出和错误,我们关闭1号描述符即关闭了stdout;
又因为文件描述符的分配规则:在文件描述符表中,会将最小的、没有被使用的数组元素分配给新文件。
所以当我们打开新的文件log.txt的时候,会将现在空出来的1号文件描述符分配给log.txt文件;
而printf默认向显示器打印,即默认输出到准输出流stdout,但是printf是根据数组下标来打印的,它并不知道对应数组下标的fd实际指向的是谁,此时1号文件描述符指向的是log.txt,所以就会发生打印输出到log.txt文件的现象,这不就是输出重定向吗?
输入重定向,同样的方法,以O_RDONLY(只读模式)打开文件,如果关闭0号文件描述符标准输入stdin,打开文件后会将文件地址填入0号下标,此时在进程中如果使用scanf输入,就不会从默认的标准输入stdin——键盘输入,而是直接从打开的文件中读取数据并输入,这不就是输入重定向吗?
而对于追加重定向,将输出重定向中打开文件的方式由O_TRUNC(对文件内容做清空)改为O_APPEND(追加)即可,所以输出重定向与追加重定向唯一的区别就是文件打开的方式不同。

重定向的原理:
上层无法感知的情况下,在OS内部,更改进程对应的文件描述符表中特定下标的指向!

在这里插入图片描述


输出重定向只会修改1号文件描述符对应的文件。下面写一段C++测试代码,分别向标准输出与标准错误打印,然后再输出重定向到一个文件log.txt,观察现象并分析:

#include <iostream>
#include <cstdio>//c++风格的c语言头文件

int main()
{
    //c
    printf("hello printf->stdout\n");
    fprintf(stdout, "hello fprintf->stdout\n");
    fprintf(stderr, "hello fprintf->stderr\n");

    //c++
    std::cout << "hello cout->cout" << std::endl;
    std::cerr << "hello cerr->cerr" << std::endl;
    return 0;
}

结果:
发现输出重定向后,只有fprintf指定文件流stdout的与cout的打印到了文件log.txt中,而stderr与cerr还依旧向显示器文件打印。
在这里插入图片描述
分析:
这是因为fprintf指定文件流stdout与cout对应的是1号文件描述符,我们输出重定向的时候修改的是1号文件描述符指向的文件,从指向stdout修改成了指向log.txt,所以这两者打印的时候虽然传入的是stdout,但是实际上只传入了1号文件描述符,1号文件描述符的指向它们并不知道,所以完成了输出重定向;而fprntf指定文件流stderr与cerr对应的2号文件描述符并没有进行修改,所以不会收到输出重定向的影响,依旧向显示器文件打印。
如果关闭2号文件描述符,再打开一个新的文件logError.txt那么此时就可以将标准错误打印到这个文件内。
以后文件量大的话,可以使用这种方法将错误消息都筛选到一个文件中,便于观察。

补充:输出重定向命令行操作

  1. 如果想在输出重定向的时候将标准输出和标准错误都输出到一个文件内,可以使用./文件名 > log.txt 2>&1(log.txt是输出的文件名,根据自己的文件名输入),意思是将1文件描述符指向的内容给2下标。
  2. 如果想分别把标准输出和标准错误分别放在两个文件内,可以使用./文件名 1>nor.txt 2>err.txt(nor.txt、err.txt是输出的文件名,根据自己的文件名输入),意思是将1号文件描述符的结果重定向到nor,将2号文件描述符对应的结果重定向到err。

重定向的实际使用方法dup2

前面讲原理时所用的方法:关闭相关的文件描述符指向的文件,然后再打开文件来实现重定向。这种方法平时不会使用,只是讲原理时使用。下面介绍重定向的实际使用方法:

头文件
#include <unistd.h>
函数
int dup2(int oldfd, int newfd);
参数
newfd与oldfd都是文件描述符,让newfd下标对应的内容拷贝oldfd下标的内容(相当于最后保留oldfd),比如fup2(fd, 1)就是输出重定向,1原来指向标准输出,修改为指向fd指向的文件。

下面写一段测试代码来通过dup2函数,将标准输出和标准错误分别重定向到不同的文件中。

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

#define LOG_NORMAL "logNormal.txt"
#define LOG_ERROR "logError.txt"

int main()
{
    int fd1 = open(LOG_NORMAL, O_CREAT | O_WRONLY | O_TRUNC, 0666);
    int fd2 = open(LOG_ERROR, O_CREAT | O_WRONLY | O_TRUNC, 0666);
    if(fd1 < 0)
    {
        perror("open");
        return 1;
    }
    else if(fd2 < 0)
    {
        perror("open");
        return 1;
    }

    dup2(fd1, 1);//1号文件描述符重定向到fd1
    dup2(fd2, 2);//2号文件描述符重定向到fd2

    fprintf(stdout, "hello world!\n");
    fprintf(stdout, "hello world!\n");
    fprintf(stdout, "hello world!\n");
    fprintf(stderr, "hello world!\n");
    fprintf(stderr, "hello world!\n");
    fprintf(stderr, "hello world!\n");

    close(fd1);
    close(fd2);
}

结果:
在这里插入图片描述


如何理解缓冲区

我们以前说的输出缓冲区、输入缓冲区还有内核结构体对象的缓冲区是一样的吗,位置在哪里,为什么要存在缓冲区。

1.刷新策略有哪几种?
——行缓冲、全缓冲、无缓冲。
C库会结合刷新策略,将缓冲区中的数据写入给OS。

2.显示器与普通文件的刷新策略分别是?

显示器采用的刷新策略:行缓冲
普通文件采用的刷新策略:全缓冲

3.存在缓冲区的意义是?
——存在缓冲区的意义是节省调用时间,因为如果直接使用系统调用,用一次数据刷新一次,调用系统调用然后数据刷新也是要消耗时间的,但是如果存在缓冲区,那么可以将多次的数据存储在一起,然后一次刷新,减少刷新频率,可以节省调用者的时间。

4.缓冲区的位置在哪里?
——在进行fopen打开文件的时候,会为我们生成一个FILE结构体,malloc一块空间,缓冲区就在FILE结构体中。

5.分析下面这段测试代码的结果,理解缓冲区。

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

int main()
{
    fprintf(stdout, "hello fprintf\n");

    const char* str = "hello write\n";
    write(1, str, strlen(str));

    fork();
    
    return 0;
}

结果:
直接运行结果正常输出,但是当输出重定向到log.txt文件的时候,为什么hello fprintf会多打印一次?下面进行解析理解。
在这里插入图片描述
解析:
当调用write这样的系统调用的时候,不经过C库,不存在缓冲区,直接调用write写给OS;
当调用fprintf这样的C库函数的时候,会经过C库,调用函数在C库中运行fprintf的时候会有缓冲区,数据先存在缓冲区中,又因为显示器的刷新策略是行缓冲,所以C库再以行缓冲的形式将数据刷新到标准输出屏幕。
所以当直接运行上面的程序的时候,会刷新两个字符串。
但是当重定向的时候,write依旧会直接写入给操作系统;fprintf则会将数据先写入到缓冲区中,又因为是重定向,不是向显示器写入,所以缓冲区的刷新策略会由行缓冲变为全缓冲,当缓冲区写满时才会刷新,但是我们只有一句hello fprintf\n,无法将缓冲区写满,所以在fork创建子进程的时候,write已经写入到了OS,而fprintf还在缓冲区中,缓冲区的这部分数据虽然在C库中,但是也属于父进程的数据,所以后续父子进程都要对缓冲区做刷新,因为刷新有先后顺序,在刷新的时候就会发生写时拷贝,所以缓冲区的数据就会刷新两次,自然会打印两条fprintf了。
在这里插入图片描述

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

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

相关文章

PyQGIS中一次性加载多个shp文件

目录 遍历添加多个图层 打印图层列表清单 打开QGIS Desktop 3.22.16&#xff0c;点击菜单栏 【设置】——>【Python控制台】 在Python控制台中点击【显示编辑器】按钮&#xff0c;打开Python编辑器 点击Python编辑器的第一个按钮 【打开脚本文件】&#xff0c;选择加载遍历…

2023年继续使用WordPress的6个最重要原因

为什么要使用 WordPress&#xff1f;我的网站不够好吗&#xff1f;为什么我需要从另一个平台切换到 WordPress&#xff1f; 在本文中&#xff0c;我们将分享您应该使用 WordPress 的最重要原因。我们还将涵盖您可以使用 WordPress 创建的所有不同类型的网站&#xff0c;并展示…

c高级(常用命令及软件安装与下载)

初始工作路径不在家目录下&#xff0c;在不切换路径的情况下&#xff0c;在家目录下创建一个subdir目录&#xff0c;在subdir这个目录下&#xff0c;创建subdir1和subdir2&#xff0c;并且把/etc/passwd拷贝到subdir1中&#xff0c;把/etc/group文件拷贝到subdir2中&#xff0c…

开源趣事~ 记给 OpenHarmony 提 PR 的那些事

大家好哇&#xff0c;许久不见&#xff0c;也感谢大家这么久一直以来的关注&#xff0c;也感谢在短视频盛行的今天&#xff0c;你们还能静下心来坚守文字的阵地。 说到这次的主题&#xff0c;参加鸿蒙项目的开源&#xff0c;也是小编第一次拥抱开源&#xff0c;就像是别人有困…

vue脚手架+elementUI,实现登录用户时的Loading...窗口

文章目录 App.vuevuex全局变量登陆成功Login组件使用AboutMe组件中关闭 登录失败情况login组件中关闭 改为aop思想的请求拦截器 App.vue 为了全局通用控制此标签&#xff0c;所以我建议把他放到App.vue文件中 <!--全局加载ing&#xff0c;保证不会在转换组件时被销毁-->…

Day965.从持续集成到持续部署 -遗留系统现代化实战

从持续集成到持续部署 Hi&#xff0c;我是阿昌&#xff0c;今天学习记录的是关于从持续集成到持续部署的内容。 只有做好任务分解和小步提交&#xff0c;才能放心大胆地 PUSH 代码&#xff0c;触发持续构建&#xff1b; 只有通过质量门禁&#xff0c;才能得到一个有信心的制…

【Spring MVC】Spring MVC的执行流程以及运行原理

文章目录 一、 什么是MVC&#xff1f;二、什么是SpringMVC&#xff1f;三、SpringMVC中的核心组件四、SpringMVC的执行流程五、关于DispatcherServlet的配置说明六、关于SpringMVC的配置文件以及常用部分注解解释七、参考资料 一、 什么是MVC&#xff1f; MVC 是 Model、View …

软件工程的基础

软件危机软件工程软件工程是将系统化的&#xff0c;严格约束的&#xff0c;可量化的方法应用于软件的开发&#xff0c;运行和维护&#xff0c;将工程应用于软件。 软件工程的三个要素&#xff1a;方法&#xff0c;工具&#xff0c;过程软件的生命周期&#xff0c;是指从从软…

Vue列表展示【第二篇】

&#x1f331; 1、vue列表展示案例 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>vueDemo02列表展示</title> </head> <body> <div id"xy"><!--原始展示方式…

深入篇【C++】类与对象:运算符重载详解 -(下)+日期类的实现

[TOC](深入篇【C】类与对象&#xff1a;运算符重载详解-(下&#xff09;日期类的实现&#x1f4af;干货满满&#xff01;) ⏰一.运算符重载 内置类型(int /double…… )是可以之间进行运算符之间的比较的&#xff0c;因为编译器知道它们之间的比较规则&#xff0c;可以之间转…

Linux下C/C++(端口扫描技术)

随着互联网使用的不断增加&#xff0c;网络攻击也在增加。互联网本质上已经成为现代。因此&#xff0c;了解互联网和使用互联网是非常重要的。网络技术的安全性在当今时代是非常重要和必要的。 在本文中&#xff0c;我们将讨论一个非常重要的主题&#xff0c;即可能破坏计算机…

es 7.x 通过DSL的常用查询语句

一 模拟造数 1.1 模拟数据 添加数据修改数据的请求方式为post http://localhost:9200/student/_doc/101 { "name":"zhangsan", "nickname":"zhangsan","sex":"男","age":30,"createTime":…

InnoDB线程模型

新版本结构演变 MySQL 5.7 版本 将 Undo日志表空间从共享表空间 ibdata 文件中分离出来&#xff0c;可以在安装 MySQL 时由用户自行指定文件大小和数量增加了 temporary 临时表空间&#xff0c;里面存储着临时表或临时查询结果集的数据Buffer Pool 大小可以动态修改&#xff0…

2.3 利用NumPy进行统计分析

2.3 利用NumPy进行统计分析 2.3.1 读/写文件1、二进制的文件读写2、读取文本格式的数据 2.3.2 使用数组进行简单统计分析1、排序2、去重与重复数据3、常用的统计函数 2.3.1 读/写文件 NumPy文件读写主要有二进制的文件读写和文件列表形式的数据读写两种形式 1、二进制的文件读…

代码随想录算法训练营第三十六天 | 重叠区间

435. 无重叠区间 文档讲解&#xff1a;代码随想录 (programmercarl.com) 视频讲解&#xff1a;贪心算法&#xff0c;依然是判断重叠区间 | LeetCode&#xff1a;435.无重叠区间_哔哩哔哩_bilibili 状态&#xff1a;能做出来&#xff0c;用的“补充(1)”的左边界排序&#xff0c…

SpringCloud —— eureka

目录 1.认识微服务 1.0.学习目标 1.1.单体架构 1.2.分布式架构 1.3.微服务 1.4.SpringCloud 1.5.总结 2.服务拆分和远程调用 2.1.服务拆分原则 2.2.服务拆分示例 2.2.1.导入Sql语句 2.2.2.导入demo工程 2.3.实现远程调用案例 2.3.1.案例需求&#xff1a; 2.3.2.注…

4. Mysql索引优化实战一

一条SQL在MySQL中是如何执行的 1. 示例表举一个大家不容易理解的综合例子 本文是按照自己的理解进行笔记总结&#xff0c;如有不正确的地方&#xff0c;还望大佬多多指点纠正&#xff0c;勿喷。 本节课内容&#xff1a; 索引下推优化详解Mysql优化器索引选择探究索引优化Order…

解决winstore下载mincraft 出现错误提示的问题,以及minecraft.exe出现此应用无法在你的电脑上运行的原因分析。

mincraft 点开显示此应用无法在你的电脑上运行&#xff0c;去C:\XboxGames\Minecraft Launcher\Content下看了下&#xff1a; 但是别人电脑上相同目录下一样的文件能运行&#xff0c;于是选择重装&#xff0c;但是刚开始下载就遇到这个问题&#xff1a; 商店提示这个 点开一…

Neural Network学习笔记2

torch.nn: Containers: 神经网络骨架 Convolution Layers 卷积层 Pooling Layers 池化层 Normalization Layers 正则化层 Non-linear Activations (weighted sum, nonlinearity) 非线性激活 Convolution Layers Conv2d torch.nn.Conv2d(in_channels, out_channels, ke…

Web缓存利用分析(三)

导语&#xff1a;前一篇文章介绍了Server Cache Poisoning在实际应用场景下&#xff0c;产生DOS攻击的利用方式。本篇文章则介绍Web Cache Deception在真实场景下的应用方式和测试情况。 前言 前一篇文章介绍了Server Cache Poisoning在实际应用场景下&#xff0c;产生DOS攻击…