文件系统 文件描述符fd 重定向原理 缓冲区

news2024/9/22 5:31:54

文章目录

  • 基础的文件操作
  • 文件的系统调用接口
    • 位图
    • 向文件中写入
    • 标记位选项总结:
    • open的返回值
    • 文件描述符fd
    • fd==012与硬件的关系
      • read && stat
  • 重定向
    • dup2
  • 缓冲区的理解
  • 经典的例子

基础的文件操作

引子:

#include <stdio.h>

int main()
{
    FILE* fp = fopen("log.txt","w");  //打开文件
    if(NULL == fp)
    {
        perror("fopen");
        return 1;
    }
    fclose(fp);  //关闭文件
    return 0;
}

默认情况下,如果文件不存在,就创建了一个log.txt的文件
在这里插入图片描述
问题:创建文件的时候,只指定了文件名log.txt,系统怎么知道在pwd的路径下呢?
在这里插入图片描述
答:因为在运行文件操作的时候,已经变成了一个进程,默认结合进程所在路径。
我们要进行文件操作时,程序是跑起来的。文件打开和关闭,是CPU在执行我们的代码。
在这里插入图片描述

我们在windows创建一个新空文件,显示的0KB是内容为零,文件的属性(名字、创建时间等)是要在磁盘上占空间的
在这里插入图片描述
文件 = 属性 + 内容
在文件内部进行写入:(如果是往屏幕写入,stream==sdout)
在这里插入图片描述

#include <stdio.h>

int main()
{
    FILE* fp = fopen("log.txt","w");  
    if(NULL == fp)
    {
        perror("fopen");
        return 1;
    }
    fprintf(fp,"helloworld,%d,%s,%lf\n",10,"hello Linux",3.14);
    fclose(fp);
    return 0;
}

在这里插入图片描述
fopen函数中的w表示:
1.如果不存在,就在当前路径下,创建文件
2.默认打开文件的时候,就会先把目标清空
在这里插入图片描述
验证目标情况:把写文件和关闭文件注释掉

#include <stdio.h>

int main()
{
    FILE* fp = fopen("log.txt","w");
    if(NULL == fp)
    {
        perror("fopen");
        return 1;
    }
    //fprintf(fp,"helloworld,%d,%s,%lf\n",10,"hello Linux",3.14);
    //fclose(fp);
    return 0;
}

35KB的内存变成了0KB
在这里插入图片描述
以a方式打开文件:追加(appending),不会清空文件
在这里插入图片描述

#include <stdio.h>

int main()
{
    FILE* fp = fopen("log.txt","a");
    if(NULL == fp)
    {
        perror("fopen");
        return 1;
    }
    fprintf(fp,"helloworld,%d,%s,%lf\n",10,"hello Linux",3.14);
    fclose(fp);
    return 0;
}

在这里插入图片描述
上面两个操作跟输出重定向很像:把内容写入到log.txt文件中
echo "hello Linux" > log.txt
先清空再写入
在这里插入图片描述
所以重定向一定伴随着文件操作
可以直接用输出重定向创建文件(跟“w”功能一样)
在这里插入图片描述
其中>>是以"a"的方式打开

echo "hello Linux"  >> log.txt

文件 -> 硬盘 ->外设 -> 硬件 -> 向文件中写入,本质是向硬件中写入 -> 用户没有权利直接写入 -> OS是硬件的管理者 -> 通过OS写入 ->
OS必须给我们提供系统调用接口(OS不相信任何人) -> fopen/fwrite/fread/fprintf/scanf/printf/cin/cout… -> 我们用的C/C++/…其他语言都是对系统调用接口的封装

文件的系统调用接口

在这里插入图片描述
返回的是一个int ,被称为文件标识符,失败的话返回-1。查看返回值的信息/return val
在这里插入图片描述
先用看看效果:
O_WRONLY 表示只写 ;O_CREAT 表示没有log.txt就创建

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

int main()
{
    // system call
    int fp = open("log.txt",O_WRONLY | O_CREAT);
    if(fp < 0)
    {
        perror("open");
        return 1;
    }
    return 0;
}

创建是创建了,但发现权限有些不对,这个权限是个乱码
在这里插入图片描述
因为如果在Linux中新建一个文件,必须知道起始权限是什么!上面这段代码更多是操作已经被打开的文件
所以就有了第三个参数
在这里插入图片描述
这里涉及到了权限的相关知识 Linux权限中都有所描述

[sjl@hcss-ecs-1bcb 9_1]$ cat myfile.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main()
{
    // system call
    int fp = open("log.txt",O_WRONLY | O_CREAT,0666);  //0666表示读写读写读写权限
    if(fp < 0)
    {
        perror("open");
        return 1;
    }
    return 0;
}

但是发现是读写读写读的权限 0664
在这里插入图片描述
因为umask权限掩码,他会去与你设定的权限进行一些运算
可以直接使用系统当中的umask
在这里插入图片描述

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

int main()
{
    umask(0);
    // system call
    int fp = open("log.txt",O_WRONLY | O_CREAT,0666);
    if(fp < 0)
    {
        perror("open");
        return 1;
    }
    return 0;
}

权限就是 读写读写读写 0666
在这里插入图片描述
程序中的umask和系统中的umask,在程序中按照就近原则使用umask

位图

在这里插入图片描述
设计一个传递位图标记位的函数

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

#define ONE    1      // 1 0000 0001
#define TWO   (1<<1)  // 2 0000 0010
#define THREE (1<<2)  // 3 0000 0100
#define FOUR  (1<<3)  // 4 0000 1000

void print(int flag)
{
    if(flag&ONE)
        printf("one\n");  //可以替换成其他功能
    if(flag&TWO)
        printf("two\n");
    if(flag&THREE)
        printf("three\n");
    if(flag&FOUR)
        printf("four\n");
}

int main()
{

    print(TWO);
    printf("\n");

    print(ONE|TWO);
    printf("\n");

    print(ONE|TWO|THREE);
    printf("\n");

    print(ONE|FOUR);
    printf("\n");

    print(ONE|TWO|THREE|FOUR);
    printf("\n");
    return 0;
}

在这里插入图片描述
回到文件正文,open中的标记位有很多
O_RDONLY : 只读 ;O_WRONLY :只写 ;O_RDWR:读写
在这里插入图片描述

向文件中写入

C语言是fwrite接口,操作系统是write
在这里插入图片描述

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

int main()
{
    umask(0);
    // system call
    int fp = open("log.txt",O_WRONLY | O_CREAT,0666);
    if(fp < 0)
    {
        perror("open");
        return 1;
    }
    const char* message = "hello Linux\n";
    write(fp,message,strlen(message)); //\0是C语言,跟系统文件没关系,所以不用strlen(message)+1
    return 0;
}

在这里插入图片描述
把写入的字符串改一改

const char* message = "520";

发现是没清空,直接写入
在这里插入图片描述
清空的选项O_TRUNC:如果文件已经存在并且是常规文件,并且 open 模式允许写入
在这里插入图片描述
下面这段代码叫做:写方式打开,不存在就创建,存在就先清空

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

追加写入就是O_APPEND
在这里插入图片描述

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

标记位选项总结:

这三个常量,必须指定一个且只能指定一个

O_RDONLY只读
O_WRONLY只写
O_RDWR读写
O_CREAT若文件不存在,则创建它。需要使用mode选项,来指明新文件的访问权限
O_TRUNC清空后写入
O_APPEND追加写

open的返回值

先看现象

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

int main()
{
    int fda = open("loga.txt",O_WRONLY | O_CREAT | O_TRUNC ,0666);
    printf("fda:%d\n",fda);
    int fdb = open("logb.txt",O_WRONLY | O_CREAT | O_TRUNC ,0666);
    printf("fdb:%d\n",fdb);
    int fdc = open("logc.txt",O_WRONLY | O_CREAT | O_TRUNC ,0666);
    printf("fdc:%d\n",fdc);
    int fdd = open("logd.txt",O_WRONLY | O_CREAT | O_TRUNC ,0666);
    printf("fdd:%d\n",fdd);
    return 0;
}

发现输出的是3,4,5,6 。没有0,1,2
在这里插入图片描述
因为进程默认会打开三个输出流,类型都是FILE*。在这里插入图片描述
可以不用printf就能在显示器上打印

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

int main()
{
    const char* message = "hello linux\n";
    write(1,message,strlen(message));
    return 0;
}

消息可以打印到显示器上
在这里插入图片描述

文件描述符fd

在这里插入图片描述
这样就可以把文件的管理起来
在这里插入图片描述
系统之中会存在很多的进程,也会存在很多的文件,进程与文件的数量比肯定是1:n的
进程task_struct中有struct files_struct* files指针,指向的结构体files_struct中有da_array[N]的指针数组,指向的是struct file文件

在这里插入图片描述
要找到一个文件的,进程只需要找到其文件的下标,返回个上层,上层拿着int fd就可以访问文件
在这里插入图片描述
综上所述: fd的本质就是:内核的进程:文件映射关系的数组的下标!
open的返回值就是拿到文件数组的下标
在这里插入图片描述
一旦把文件打开了:读写关都需要fd
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
读的本质是把文件内核级的缓存拷贝到应用层
如果文件内核级的缓存中没有数据,就会把进程阻塞,从磁盘中搬数据,搬完唤醒进程,再做拷贝
在这里插入图片描述

写数据也需要先把log.txt的内容,放入到文件的缓冲区,上层拷贝进缓冲区,在缓冲区更改内容,然后再由OS进行定期刷新到磁盘中
在这里插入图片描述
所以读写都是函数的一种拷贝
open在干什么呢?
1.创建file
2.开辟文件缓冲区的空间,加载文件数据(延后)
3.查进程的文件描述符表
4.file地址填入对应的表下表中
5.返回下标
源码:
进程task_struct中的files指针
在这里插入图片描述
struct files_struct中的数组
在这里插入图片描述
这个就是在struct file中打开的文件
在这里插入图片描述
内核级缓冲区
在这里插入图片描述

fd==012与硬件的关系

fd==0,1,2是默认打开的
那硬件如何和软件产生关系的?
在这里插入图片描述
要往每种的外设中读写数据,所以每种的外设有自己的读写方法
在这里插入图片描述
工程师肯定要写每种设备的驱动
每一个被打开的设备,OS肯定会为设备创建struct file,struct file中会包含函数指针
要访问一个struct file,直接从中调用read,就可以直接调用键盘的方法
在这里插入图片描述
从OS往上看,不用关心底层的差异。
在上层看到的所有的设备叫做一切皆文件
拿着同一种struct file可以访问各种设备:叫做多态
在这里插入图片描述
源码:这是struct file中的一个指针,他指向的是一个操作表
在这里插入图片描述
操作底层方法的指针表
在这里插入图片描述
这一层又叫做vfs全称叫做virtual file system(虚拟操作系统)
在这里插入图片描述
过一遍在文件中写入的过程:
open打开log.txt,在file_struct拿到log.txt的文件描述符3传到上层
write拿到fd,buf,长度的参数后写入到缓冲区中
后面由操作系统定期刷新到磁盘中
在这里插入图片描述
综上所述:操作系统只认文件描述符fd
如何理解C语言通过FILF*访问文件呢?

#include <stdio.h>

int main()
{
    FILE* fp = fopen("log.txt","a");
    if(NULL == fp)
    {
        perror("fopen");
        return 1;
    }
    fprintf(fp,"helloworld,%d,%s,%lf\n",10,"hello Linux",3.14);
    fclose(fp);
    return 0;
}

首先fopen,fwrite…都是库函数
在这里插入图片描述

#include <stdio.h>

int main()
{
    FILE* fp = fopen("log.txt","w");
    if(fp == NULL) return 1;
    printf("fd:%d\n",fp->_fileno);  //fileno就是文件标识符
    fwrite("hello",5,1,fp);
    return 0;
}

在这里插入图片描述
也可以把stdout等的文件描述符打出来

#include <stdio.h>

int main()
{
    printf("stdin:%d\n",stdin->_fileno);
    printf("stdout:%d\n",stdout->_fileno);
    printf("stderr:%d\n",stderr->_fileno);
    return 0;
}

在这里插入图片描述
所有C语言上的文件操作函数,本质底层都是对系统调用的封装
C语言为什么这么做?
在这里插入图片描述
C语言如何做到跨平台性?
底层不一样,但在上层fopen,fwrite语法都一样的。
在这里插入图片描述
如果所有语言都想要有跨平台性,就要对不同的平台系统调用进行封装–>文件接口就有了差别
一个进程:默认会打开stdin,stdout,stderr,这里验证一下:
运行以下代码的时候,打开进程文件夹进行查看

ls /proc/pid
#include <stdio.h>
#include <unistd.h>

int main()
{
    while(1)
    {
        printf("pid:%d\n",getpid());
    }
    return 0;
}

在这里插入图片描述
这里指向同一个地方是因为云服务器,并没有键盘等。
在这里插入图片描述

read && stat

在这里插入图片描述
这里介绍一个函数stat
在这里插入图片描述
文件 = 内容 + 属性
stat就是对文件属性做操作的
目前索要的就是st_size:文件有多少字节
在这里插入图片描述
返回值:如果成功返回0;错误返回-1
在这里插入图片描述

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

const char* filename = "log.txt";

int main()
{
    struct stat st;
    int n = stat(filename,&st);
    if(n < 0) return 1;
    printf("file size:%lu\n",st.st_size);  //类型为无符号整型
    int fd = open(filename,O_RDONLY);
    if(fd < 0)
    {
        perror("open");
        return 2;
    }
    printf("fd:%d\n",fd);
    char* file_buffer = (char*)malloc(st.st_size+1);  //多申请一个字节为了打印出来
    n = read(fd,file_buffer,st.st_size);
    if(n > 0)
    {
        file_buffer[n] = '\0';  //写文件的时候没有把\0写进去
        printf("%s\n",file_buffer);
    }
    free(file_buffer);
    close(fd);
    return 0;
}

在这里插入图片描述

重定向

做个实验,先把文件fd==0关掉

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

const char* filename = "log.txt";

int main()
{
    close(0);
    int fd = open(filename,O_CREAT | O_WRONLY | O_TRUNC,0666);
    if(fd < 0)
    {
        perror("open");
        return 1;
    }
    printf("fd:%d\n",fd);
    close(fd);
    return 0;
}

发现新创建的文件fd==0
在这里插入图片描述
把1关掉

close(1);

发现并没有打印
在这里插入图片描述
文件描述符的分配规则:查自己的文件描述符表,分配最小的没有被使用的fd
再谈论一个实验:
在这里插入图片描述

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

const char* filename = "log.txt";

int main()
{
    close(1);  //关闭stdout
    int fd = open(filename,O_CREAT | O_WRONLY | O_TRUNC,0666);
    if(fd < 0)
    {
        perror("open");
        return 1;
    }
    printf("printf,fd:%d\n",fd);
    fprintf(stdout,"fprintf,fd:%d\n",fd);
    fflush(stdout);  //刷新缓冲区
    close(fd);

    return 0;
}

会发现并没有打印到显示器上,而是把内容放到log.txt中
在这里插入图片描述
把flush去掉,再次运行

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

const char* filename = "log.txt";

int main()
{
    close(1);  //关闭stdout
    int fd = open(filename,O_CREAT | O_WRONLY | O_TRUNC,0666);
    if(fd < 0)
    {
        perror("open");
        return 1;
    }
    printf("printf,fd:%d\n",fd);
    fprintf(stdout,"fprintf,fd:%d\n",fd);
    close(fd);

    return 0;
}

log.txt被创建出来了,但没有内容
在这里插入图片描述
综合上面两个现象谈一下原因:
先关闭fd== 1 ,不再指向stdout。然后log.txt被打开,被分配到了fd== 1 的位置上
printf和fprintf都是往stdout打印,只认stdout == 1 ,不管下层的变换
本来应该向屏幕打印的内容却打印到了log.txt中,这叫做重定向
在这里插入图片描述
重定向的本质:是在内核中改变文件描述符表特定下表的内容,与上层无关
关于fflush(stdout)不能写入log.txt的原因:
stdout的类型是struct FILE*,struct file内部有_fileno还有语言级别的文件缓冲区,先写入stdout的文件级别的缓冲区,后再由文件级别的缓冲区写入到内核级别的缓冲区
所以fflush(stdout)是通过文件描述符把文件级别的缓冲区中的内容写入到内核级别的缓冲区当中
在这里插入图片描述
在return的时候刷新到内核级缓冲区中,但close(fd)把文件关了,所以刷新不了了
关于有人说:“\n"不也是刷新到缓冲区吗!”\n"是到内核级别的缓冲区刷新到磁盘,不是文件缓冲区干的活。这段代码连内核级别都没进入,当然写入不进去

dup2

在这里插入图片描述
这里主要介绍dup2:本质是文件描述符下表的所对应的内容的拷贝
在这里插入图片描述
如果想要显示器打印 -> log.txt,是dup2(fd,1); oldfd -> newfd
把fd == 3的内容拷贝到fd ==1的下标中。
两个指针指向一个对象的问题:struct file中有个引用计数ref_count,记载了有多少指针指向自己。若没人指向自己,就释放掉了
在这里插入图片描述
验证:

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

const char* filename = "log.txt";

int main()
{
    int fd = open(filename,O_WRONLY | O_CREAT | O_TRUNC,0666);
    dup2(fd,1);
    printf("hello world\n");
    fprintf(stdout,"hello world\n");
    return 0;
}

打印到log.txt文件中
在这里插入图片描述
把清空后写入变成追加写入就是追加重定向>>

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

const char* filename = "log.txt";

int main()
{
    int fd = open(filename,O_CREAT | O_WRONLY | O_APPEND,0666);
    dup2(fd,1);
    printf("hello world\n");
    fprintf(stdout,"hello world\n");
    return 0;
}

在这里插入图片描述

缓冲区的理解

缓冲区就是一段内存空间。
缓冲区的优点:
1.解耦:把数据交给缓冲区,底层怎么做不用管
例子:你要送给远方朋友一个东西,去楼下找个快递点,把东西给到快递点,让快递小哥去送达。你只用负责把东西放到快递点以及填写资料即可。
2.提高效率:提高使用者的效率。提高IO刷新的效率(调用系统接口是有成本的,OS很忙,尽量要少调用).
例子:发快递都是攒到一起发,不能一个个发,因为发快递也是有成本的。
用户缓冲区的刷新策略:
1.立即刷新(无缓冲):C语言级的fflush(stdout) ; 系统级的fsync(int fd) 立即从内核缓冲区刷新到外设
在这里插入图片描述
2.行刷新:显示器(给用户看的,看的舒服)
3.全缓冲:普通文件:缓冲区写满,才刷新
4.特殊情况:进程退出,系统会自动刷新。
内核策略我们不关心,只要交给了操作系统,就相当于交给了外设

经典的例子

先看现象:

#include <stdio.h>
#include <string.h>
#include <unistd.h>

int main()
{
	//C语言
    printf("hello printf\n");
    fprintf(stdout,"hello fprintf\n");
    //system call
    const char* msg = "hello write\n";
    write(1,msg,strlen(msg));
    return 0;
}

打印的也没错
在这里插入图片描述
加一个fork

#include <stdio.h>
#include <string.h>
#include <unistd.h>

int main()
{
    printf("hello printf\n");
    fprintf(stdout,"hello fprintf\n");
    const char* msg = "hello write\n";
    write(1,msg,strlen(msg));
    fork();
    return 0;
}

为什么打印到屏幕是打印了三行字符串,写入到log.txt是五行呢?
在这里插入图片描述
显示器文件->行刷新

./myfile

不经过内核缓冲区,在stdout里面待着等着被打印
向普通文件写入->全刷新(当缓冲区满了/进程退出才会刷新)

./myfile > log.txt

write是先刷新进内核里面了
printf/fprintf才刷新到stdout对应的缓冲区,并没有被写满
子进程和父进程运行完后,都要刷新各自的缓冲区,所以各自打印了两次。
看一下FILE*的源码中的缓冲区:
/usr/include/stdio.h
在这里插入图片描述

/usr/include/stdlib.h

在这里插入图片描述

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

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

相关文章

[Linux]:环境变量与进程地址空间

✨✨ 欢迎大家来到贝蒂大讲堂✨✨ &#x1f388;&#x1f388;养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; 所属专栏&#xff1a;Linux学习 贝蒂的主页&#xff1a;Betty’s blog 1. 环境变量 1.1 概念 **环境变量(environment variables)**一般是指在操作…

在Unity环境中使用UTF-8编码

为什么要讨论这个问题 为了避免乱码和更好的跨平台 我刚开始开发时是使用VS开发,Unity自身默认使用UTF-8 without BOM格式,但是在Unity中创建一个脚本,使用VS打开,VS自身默认使用GB2312(它应该是对应了你电脑的window版本默认选取了国标编码,或者是因为一些其他的原因)读取脚本…

自己部门日均1000+告警?如何减少90%无效告警?

目录标题 一、告警的类别1.技术告警1.1基础设施告警1.2基本服务告警 2.业务告警3.监控大盘告警 二、为何需要告警治理&#xff1f;三、治理迫在眉睫1.1告警治理策略1.2核心监控告警点1.3避免告警反模式1.4告警规约制定1.5自动化处理 一、告警的类别 一般的告警分为以下几点&am…

ISP面试准备2

系列文章目录 文章目录 系列文章目录前言一.如何评价图像质量&#xff1f;二.引起图像噪声的原因三. ISP3.1 ISP Pipeline主要模块3.1.1坏点校正&#xff08;Defect Pixel Correction, DPC&#xff09;3.1.2黑电平校正&#xff08;Black Level Correction, BLC&#xff09;3.1.…

面试官:synchronized的锁升级过程是怎样的?

大家好&#xff0c;我是大明哥&#xff0c;一个专注「死磕 Java」系列创作的硬核程序员。 回答 在 JDK 1.6之前&#xff0c;synchronized 是一个重量级、效率比较低下的锁&#xff0c;但是在JDK 1.6后&#xff0c;JVM 为了提高锁的获取与释放效&#xff0c;,对 synchronized 进…

基于JSP的实验室管理系统

你好呀&#xff0c;我是计算机学姐码农小野&#xff01;如果有相关需求&#xff0c;可以私信联系我。 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;JSP技术 Spring Boot框架 工具&#xff1a;IDEA/Eclipse、Navicat、Tomcat 系统展示 首页 用户个…

自然语言处理系列六十二》神经网络算法》MLP多层感知机算法

注&#xff1a;此文章内容均节选自充电了么创始人&#xff0c;CEO兼CTO陈敬雷老师的新书《自然语言处理原理与实战》&#xff08;人工智能科学与技术丛书&#xff09;【陈敬雷编著】【清华大学出版社】 文章目录 自然语言处理系列六十二神经网络算法》MLP多层感知机算法CNN卷积…

【Python篇】PyQt5 超详细教程——由入门到精通(序篇)

文章目录 PyQt5 超详细入门级教程前言序篇&#xff1a;1-3部分&#xff1a;PyQt5基础与常用控件第1部分&#xff1a;初识 PyQt5 和安装1.1 什么是 PyQt5&#xff1f;1.2 在 PyCharm 中安装 PyQt51.3 在 PyCharm 中编写第一个 PyQt5 应用程序1.4 代码详细解释1.5 在 PyCharm 中运…

电子电气架构---私有总线通信和诊断规则

电子电气架构—私有总线通信和诊断规则 我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 屏蔽力是信息过载时代一个人的特殊竞争力,任何消耗你的人和事,多看一眼都是你的不对。非必要不费力证明自…

最新版 | SpringBoot3如何自定义starter(面试常考)

文章目录 一、自定义starter介绍二、自定义Starter的好处及优势三、自定义starter应用场景四、自定义starter1、创建autoconfigure的maven工程2、创建starter的maven工程3、在autoconfigure的pom文件中引入MyBatis的所需依赖4、编写自动配置类MyBatisAutoConfiguration5、编写i…

红旗EQM换电连接器哪家生产

红旗EQM换电连接器概述 红旗EQM换电连接器是针对红旗品牌电动汽车设计的一种快速更换电池的装置。它允许用户在短时间内完成电池的更换&#xff0c;从而提高电动车的使用效率和便捷性。接下来&#xff0c;我们将详细探讨红旗EQM换电连接器的相关操作步骤、所需工具以及最新的相…

[Git使用] 实战技巧

文章目录 1. 理解分叉点2. Rebase3. FixUp4. Revert1. 理解分叉点 合并分支的时候会产生分叉点 比如: 仓库有dev和feature两个分支; 操作1:dev远程新建一个文件操作2:feature提交第一次操作3:远程执行把feture合并到dev分支在可视化界面可以看到 远程Dev分支的可视化: …

MySQL 锁分类有哪些?一文带你详解!!

MySQL 锁 全局锁全局锁的应用场景全局锁的缺点 表级锁表锁元数据&#xff08;MDL&#xff09;锁MDL 锁的问题 意向锁AUTO-INC 锁 行级锁记录锁&#xff08;Record Lock&#xff09;间隙锁&#xff08;Gap Lock&#xff09;临键锁&#xff08;Next-Key Lock&#xff09;插入意向…

安卓开发板_联发科MTK开发评估套件串口调试

串口调试 如果正在进行lk(little kernel ) 或内核开发&#xff0c;USB 串口适配器&#xff08; USB 转串口 TTL 适配器的简称&#xff09;对于检查系统启动日志非常有用&#xff0c;特别是在没有图形桌面显示的情况下。 1.选购适配器 常用的许多 USB 转串口的适配器&#xf…

宝塔部署Vue项目解决跨域问题

一、前言 使用宝塔面板部署前端后端项目相比用命令行进行部署要简单许多&#xff0c;宝塔的可视化操作对那些对Linux不熟悉的人很友好。使用宝塔部署SpringBoot后端项目和Vue前端项目的方法如下&#xff1a; 1、视频教程 2、文字教程1 3、文字教程2 以上的教程完全可以按照步骤…

以太网交换机工作原理学习笔记

在网络中传输数据时需要遵循一些标准&#xff0c;以太网协议定义了数据帧在以太网上的传输标准&#xff0c;了解以太网协议是充分理解数据链路层通信的基础。以太网交换机是实现数据链路层通信的主要设备&#xff0c;了解以太网交换机的工作原理也是十分必要的。 1、以太网协议…

SQLException: No Suitable Driver Found - 完美解决方法详解

&#x1f6a8; SQLException: No Suitable Driver Found - 完美解决方法详解 &#x1f6a8; **&#x1f6a8; SQLException: No Suitable Driver Found - 完美解决方法详解 &#x1f6a8;****摘要 &#x1f4dd;****引言 &#x1f3af;****正文 &#x1f4da;****1. 问题概述 ❗…

网络层 VII(IP多播、移动IP)【★★★★★★】

一、IP 多播 1. 多播的概念 多播是让源主机一次发送的单个分组可以抵达用一个组地址标识的若干目的主机&#xff0c;即一对多的通信。在互联网上进行的多播&#xff0c;称为 IP 多播&#xff08;multicast , 以前曾译为组播&#xff09;。 与单播相比&#xff0c;在一对多的…

【go】内存分配模型

内存是怎么分配给对象的&#xff1f; 内存分配优化的地方是&#xff1f; 讲讲golang内存分配模型&#xff1f; ans: 1.按照对象的大小分配&#xff1a;先算出对象的大小如果是tiny对象&#xff0c;就从tiny block中获取地址和偏移量&#xff0c;将对象打包到mcache;如果是16B以…

Xilinx系FPGA学习笔记(五)ROM的IP核学习

系列文章目录 文章目录 系列文章目录前言ROM IP分布式ROM生成ROM配置创建COE文件 块ROM生成如何快速生成Example Design 两种ROM对比 前言 最近在学习小梅哥的xilinx型FPGA开发板&#xff0c;一边学习一边记录&#xff0c;简化整理一下笔记 ROM IP 在 Memories &Storage …