C++linux高并发服务器项目实践 day10

news2024/11/25 23:45:38

C++linux高并发服务器项目实践 day10

  • 守护进程
    • 进程组
    • 会话
    • 进程组、会话操作函数
    • 守护进程
      • 守护进程的创建步骤
  • 线程
    • 线程和进程的区别
    • 线程之间共享和非共享资源
    • 线程操作
      • 线程创建
      • 线程退出
      • 线程参与
      • 线程分离
      • 线程取消
    • 线程属性

守护进程

在UNIX系统中,用户通过终端登录系统后得到一个shell进程,这个终端称为shell进程的控制终端(Controlling Terminal),进程中,控制终端是保存在PCB中的信息,而fork()会复制PCB中的信息,因此由shell进程启动的其他进程的控制终端也是这个终端

默认情况下(没有重定向),每个进程的标准输入、输出和标准错误输出都指向控制终端,进程从标准输入读也就是读用户的键盘输入,进程往标准输出或标准错误输出写也就是输出到显示器上

在控制终端输入一些特殊的控制键可以给前台进程发信号,例如Ctrl + C会产生SIGINT信号,Ctrl + \会产生SIGQUIT信号

echo $$
可以通过上述命令来查看当前终端的进程号

tty
可以用于显示终端机连接标准输入设备的文件名称

进程组

进程组和会话在进程之间形成了一种两级层次关系:进程组是一组相关进程的集合,会话是一组相关进程组的集合。进程组和会话是为支持shell作业控制而定义的抽象概念,用户通过shell能够交互式地在前台或后台运行命令

进程组由一个或多个共享同一进程组标识符(PGID)的进程组成。一个进程组拥有一个进程组首进程,该进程是创建该组的进程,其进程ID为该进程组的ID,新进程会继承其父进程所属的进程组ID

进程组拥有一个生命周期,其开始时间为首进程创建组的时刻,结束时间为最后一个成员进程退出组的时刻,一个进程可能会因为终止而退出进程组,也可能会因为加入了另外一个进程组而退出进程组。进程组首进程无需是最后一个离开进程组的成员

会话

会话是一组进程组的集合。会话首进程是创建该新会话的进程,其进程ID会成为会话ID。新进程会继承其父进程的会话ID

一个会话中的所有进程共享单个控制终端。控制终端会在会话首进程首次打开一个终端设备时被建立,一个终端最多可能会成为一个会话的控制终端

在任一时刻,会话中的其中一个进程组会成为终端的前台进程组,其他进程组会成为后台进程组。只有前台进程组中的进程才能从控制终端中读取输入。当用户在控制终端中输入终端字符生成信号后,该信号会被发送到前台进程组中的所有成员

当控制终端的连接建立起来之后,会话首进程会成为该终端的控制进程
在这里插入图片描述

在这里插入图片描述

进程组、会话操作函数

  • pid_t getpgrp(void); 获取当前进程的进程组
  • pid_t getpgid(pid_t pid);获取指定进程的进程组id
  • int setpgid(pid_t pid ,pid_t pgid);设置进程组的ID
  • pid_t getsid(pid_t pid);获取指定进程的会话ID
  • pid_t setsid(void);设置会话的ID

守护进程

守护进程,也就是通常说的Daemon进程(精灵进程),是Linux中的后台服务进程。它是一个生存期较长的进程,通常独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。一般采用以d结尾的名字

守护进程具备下列特征:

  • 生命周期很长,守护进程会在系统启动的时候被创建并一直运行直至系统被关闭
  • 它在后台运行并且不拥有控制终端。没有控制终端确保了内核永远不会为守护进程自动生成任何控制信号以及终端相关的信号(如SIGINT、SIGQUIT).

Linux的大多数服务器就是用守护进程实现的。比如,Internet服务器inetd,Web服务器httpd等

守护进程的创建步骤

  • 执行一个fork(),之后父进程退出,子进程继续执行
  • 子进程调用setsid()开启一个新会话
  • 清楚进程的umask以确保当守护进程创建文件和目录时拥有所需的权限
  • 修改进程的当前工作目录,通常会改为根目录(/)
  • 关闭守护进程从其父进程继承而来的所有打开着的文件描述符
  • 在关闭了文件描述符0、1、2之后,守护进程通常会打开/dev/null并使用dup2()使所有这些描述符指向这个设备
  • 核心业务逻辑
/*
    写一个守护进程,每隔2s获取一下系统时间,将这个时间写入到磁盘文件中
*/
#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/time.h>
#include <signal.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>

void work(int num){
    //捕捉到信号之后,获取系统时间,写入磁盘文件
    time_t tm = time(NULL);
    struct  tm * loc = localtime(&tm);
    //方法一
    // char buf[1024];

    // sprintf(buf,"%d-%d-%d %d:%d:%d\n",loc->tm_year,loc->tm_mon,loc->tm_mday,loc->tm_hour,loc->tm_min,loc->tm_sec);
    
    // printf("%s\n",buf);

    //方法二
    char * str = asctime(loc);
    int fd = open("time.txt",O_RDWR | O_CREAT|O_APPEND,0664);
    write(fd,str,strlen(str));
    close(fd);
}

int main(){

    //1.创建子进程,退出父进程
    pid_t pid = fork();

    if(pid > 0){
        exit(0);
    }

    //2.将子进程重新创建一个会话
    setsid();

    //3.设置掩码
    umask(022);

    //4.更改工作目录
    chdir("/home/yuuki/lesson/lesson28/");

    //5.关闭、重定向文件描述符
    // int fd = open("/dev/null",O_RDWR);
    // dup2(fd,STDIN_FILENO);
    // dup2(fd,STDOUT_FILENO);
    // dup2(fd,STDERR_FILENO);

    //6.业务逻辑

    //捕捉定时信号
    struct sigaction act;
    act.sa_flags = 0;
    act.sa_handler = work;
    sigemptyset(&act.sa_mask);
    
    sigaction(SIGALRM,&act,NULL);


    struct itimerval val;
    val.it_interval.tv_sec = 2;
    val.it_interval.tv_usec = 0;
    val.it_value.tv_sec = 2;
    val.it_value.tv_usec = 0;

    //创建定时器
    setitimer(ITIMER_REAL,&val,NULL);
    
    while(1){}

    return 0;
}

输出的结果可以在time.txt中查看,vim里可以使用下面代码来刷新文本

:e

线程

与进程(process)类似,线程(thread)是允许应用程序并发执行多个任务的一种机制。一个进程可以包含多个线程。同一个程序中的所有线程均会独立执行相同程序,且共享同一份全局内部区域,其中包括初始化数据段、未初始化数据段,以及堆内存段(传统意义上的UNIX进程只是多线程程序的一个特例,该进程只包含一个线程)

进程是CPU分配资源的最小单位,线程是操作系统调度执行的最小单位

线程是轻量级的进程(LWP:Light Weight Process),在Linux环境下线程的本质仍是进程

查看指定进程的LWP号:ps -Lf pid

线程和进程的区别

进程间的信息难以共享。由于除去只读代码段外,父子进程并未共享内存,因此必须采用一些进程间通信方式,在进程间进行信息交换

调用fork()来创建进程的代价相对较高,即便利用写时复制技术,仍需要赋值诸如内存页表和文件描述符表之类的多种进程属性,这意味着fork()调用在时间上的开销依然不菲

线程之间能够方便、快速地共享信息。只需将数据赋值到共享变量中即可

创建线程比创建进程通常要快10倍甚至更多。线程间是共享虚拟地址空间的,无需采用写时复制来复制内存,也无需复制页表

线程之间共享和非共享资源

在这里插入图片描述

线程操作

  • pthread_t pthread_self(void);
  • int pthread_equal(pthread_t t1,pthread_t t2);
  • int pthread_create(pthread_t *thread,const pthread_attr_t *attr,void *(*start_routine)(void *),void *arg);
  • void pthread_exit (void *retval);
  • int pthread_join(pthread_t thread,void **retval);
  • int pthread_detach(pthread_t thread);
  • int pthread_cancel(pthread_t thread);

在编译时最后加上-l 以及库名,如下,就能引用对应库

-lpthread

线程创建

一般情况下,main函数所在的线程我们称之为主线程(main线程),其余创建的线程称之为子线程
程序中默认只有一个进程,fork()函数调用,2进程
程序中默认只有一个线程,pthread_create()函数调用,2个线程

#include <pthread.h>

int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);

  • 功能:创建一个子线程
  • 参数:
    • thread:传出参数,线程创建成功后,子线程的线程ID被写到该变量中
    • attr:设置线程的属性,一般使用默认值,NULL
    • start_routine:函数指针,这个指针是子线程需要处理的逻辑代码
    • arg:给第三个参数使用,传参
  • 返回值:
    成功返回0,失败返回错误号,该错误号并非之前的errno
    获取错误号的信息:char *strerrno(int errnum);
#include <pthread.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

void *callback(void * arg){
    printf("child thread...\n");
    printf("arg value : %d\n",*(int*)arg);
    return NULL;
}

int main(){
    pthread_t tid;

    int num = 10;

    //创建一个子线程
    int ret = pthread_create(&tid,NULL,callback,(void*)&num);

    if(ret != 0){
        char * errstr = strerror(ret);
        printf("error : %s\n",errstr);
    }

    for(int i = 0;i < 5;i++){
        printf("%d\n",i);
    }

    sleep(1);

    return 0;
}

线程退出

#include <pthread.h>

void pthread_exit(void *retval);

  • 功能:终止一个线程,在哪个线程中调用,就表示终止哪个线程
  • 参数:
    • retval:需要传递一个指针,作为一个返回值,可以在pthread_join()中获取到

pthread_t pthread_self(void);

  • 功能:获取当前的线程的线程ID

int pthread_equal(pthread_t t1,pthread_t t2);

  • 功能:比较两个线程ID是否相等
    不同的操作系统,pthread_t类型的实现不同,有的是无符号的长整型,有的是使用结构体去实现的
#include <stdio.h>
#include <pthread.h>
#include <string.h>

void * callback(void* arg){
    printf("child thread id: %ld\n",pthread_self());
    return NULL;
}

int main(){
    pthread_t tid;

    //创建一个子线程
    int ret = pthread_create(&tid,NULL,callback,NULL);

    if(ret != 0){
        char* errstr = strerror (ret);
        printf("error : %s\n",errstr);
    }

    //主线程
    for(int i = 0;i < 5;i++){
        printf("%d\n",i);
    }

    printf("tid : %ld,main thread id: %ld\n",tid,pthread_self());

    //让主线程退出,当主线程退出时,不会影响其他正常运行的线程
    pthread_exit(NULL);

    //主线程结束后,接下来的代码不会执行
    printf("main thread\n");

    return 0;
}

线程参与

#include <pthread.h>
int pthread_join(pthread_t thread, void **retval);

  • 功能:和一个已经终止的线程进行连接
    回收子线程的资源
    这个函数是阻塞函数,调用一次只能回收一个子线程
    一般在主线程中使用
  • 参数:
    • thread:需要回收的子线程的ID
    • retval:接收子线程退出时的返回值
  • 返回值:
    0:成功
    非0:失败,返回错误号
#include <stdio.h>
#include <pthread.h>
#include <string.h>
#include <unistd.h>

int value =10;

void * callback(void* arg){
    printf("child thread id: %ld\n",pthread_self());
    //sleep(2);
    //return NULL;
    //int value =10;
    pthread_exit((void*)&value);//等同于return (void*)&value;
}

int main(){
    pthread_t tid;

    //创建一个子线程
    int ret = pthread_create(&tid,NULL,callback,NULL);

    if(ret != 0){
        char* errstr = strerror (ret);
        printf("error : %s\n",errstr);
    }

    //主线程
    for(int i = 0;i < 5;i++){
        printf("%d\n",i);
    }

    printf("tid : %ld,main thread id: %ld\n",tid,pthread_self());

    //主线程调用pthread_join()回收子线程的资源
    int * thread_retval;
    ret = pthread_join(tid,(void**)&thread_retval);
    if(ret != 0){
        char* errstr = strerror (ret);
        printf("error : %s\n",errstr);
    }

    printf("exit data : %d\n",*thread_retval);

    printf("回收子线程资源成功\n");

    //让主线程退出,当主线程退出时,不会影响其他正常运行的线程
    pthread_exit(NULL);



    return 0;
}

线程分离

#include <pthread.h>
int pthread_detach(pthread_t thread);

  • 功能:分离一个线程。被分离的线程在终止的时候,会自动释放资源返回给系统
    1. 不能多次分离,会产生不可预料的行为
    2. 不能去连接一个已经分离的线程,会报错
  • 参数:需要分离的线程的ID
  • 返回值:
    成功:0
    失败:返回错误号
#include <stdio.h>
#include <pthread.h>
#include <string.h>
#include <unistd.h>

void * callback(void *arg){
    printf("child thread id:%ld\n",pthread_self());
    return NULL;
}

int main(){

    //创建一个子线程
    pthread_t tid;

    int ret = pthread_create(&tid,NULL,callback,NULL);
    if(ret != 0){
        char * errstr = strerror(ret);
        printf("error1 : %s\n",errstr);
    }

    //输出主线程和子线程的ID
    printf("tid : %ld,main thread id : %ld\n",tid,pthread_self());

    //设置子线程分离,子线程分离后,子线程结束时对应的资源就不需要主线程释放
    ret = pthread_detach(tid);
    if(ret != 0){
        char * errstr = strerror(ret);
        printf("error2 : %s\n",errstr);
    }

    //设置分离后,对分离的子线程进行连接pthread_join()
    // ret = pthread_join(tid,NULL);
    // if(ret != 0){
    //     char * errstr = strerror(ret);
    //     printf("error3 : %s\n",errstr);
    // }
    //会报错error3 : Invalid argument


    pthread_exit(NULL);

    return 0;
}

线程取消

#include <pthread.h>
int pthread_cancel(pthread_t thread);

  • 功能:取消线程(让线程终止)
    取消某个线程,可以终止某个线程的运行,但是并不是立马终止,而是当子线程执行到一个取消点,线程才会终止
    取消点:系统规定好的一些系统调用,我们可以粗略的理解为从用户区到和内核区的切换,这个位置称之为取消点
#include <stdio.h>
#include <pthread.h>
#include <string.h>
#include <unistd.h>

void * callback(void *arg){
    printf("child thread id:%ld\n",pthread_self());
    for(int i = 0;i < 5;i++){
    printf("child %d\n",i);
    }
    return NULL;
}

int main(){

    //创建一个子线程
    pthread_t tid;

    int ret = pthread_create(&tid,NULL,callback,NULL);
    if(ret != 0){
        char * errstr = strerror(ret);
        printf("error1 : %s\n",errstr);
    }

    //取消线程
    ret = pthread_cancel(tid);
    if(ret != 0){
        char * errstr = strerror(ret);
        printf("error2 : %s\n",errstr);
    }
    
    for(int i = 0;i < 5;i++){
        printf("%d\n",i);
    }

    //输出主线程和子线程的ID
    printf("tid : %ld,main thread id : %ld\n",tid,pthread_self());


    pthread_exit(NULL);

    return 0;
}

线程属性

int pthread_attr_init (pthread_attr_t *attr);

  • 功能:初始化线程属性变量

int pthread_attr_destroy(pthread_attr_t *attr);

  • 功能:释放线程属性的资源
    int pthread_attr_getdetachstate(const pthread_attr_t *attr,int *detachstate);
  • 功能:获取线程分离的状态属性
    int pthread_attr_setdetachstate(pthread_attr_t *attr,int detachstate);
  • 功能:设置线程分离的状态属性

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

void * callback(void *arg){
    printf("child thread id:%ld\n",pthread_self());
    return NULL;
}

int main(){
    //创建一个线程属性变量
    pthread_attr_t attr;
    //初始化属性变量
    pthread_attr_init(&attr);

    //设置属性
    pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);

    //获取线程的栈的大小
    size_t  size;
    pthread_attr_getstacksize(&attr,&size);

    printf("thread stack size : %ld\n",size);

    //创建一个子线程
    pthread_t tid;

    int ret = pthread_create(&tid,&attr,callback,NULL);
    if(ret != 0){
        char * errstr = strerror(ret);
        printf("error1 : %s\n",errstr);
    }

    //输出主线程和子线程的ID
    printf("tid : %ld,main thread id : %ld\n",tid,pthread_self());

    //释放线程属性资源
    pthread_attr_destroy(&attr);
    pthread_exit(NULL);

    return 0;
}

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

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

相关文章

DCMM评估之战略维度沟通

01 数据战略规划过程 过程描述&#xff1a; 过程描述如下:a) 识别利益相关者,明确利益相关者的需求;b) 数据战略需求评估,组织对业务和信息化现状进行评估,了解业务和信息化对数据的需求;c) 数据战略制定,包含但不限于:1) 愿景陈述,其中包含数据管理原则、目的和目标;2) 规划…

SpringCloud 远程调用Feign、网关Gateway、配置中心Nacos、微服务架构小结、Nacos搭建集群

统一检查maven maven依赖出错的解决 注意代码格式化。因代码格式混乱&#xff0c;导致代码出错&#xff0c;pom.xml出现重复的parent标签 学习方法&#xff0c;听得懂为什么要这么做&#xff0c;要远远比 怎么做 重要的多 一、远程调用Feign 能够使用Feign进行远程调用能够…

【C++学习】创建二维动态数组

1.指针 创建二维动态数组_牛客题霸_牛客网 (nowcoder.com) 使用指针的指针 使用指针的指针可以很方便地创建动态的二维数组&#xff0c;其关键在于使用两层指针进行分配。 以下是一个动态创建n行m列的二维数组的示例代码&#xff1a; int **arr new int*[n]; // 创建一个…

深度学习训练营J2:ResNet50v2算法分析与实战

深度学习训练营J2:ResNet50v2算法分析与实战 原文链接环境介绍0.引言论文分析与解读1.ResNet50和ResNet50v2之间的结构对比2.不同结构之间的尝试 3.关于激活的不同尝试4.文章结果 ResNet50v2架构复现5.残差结构6.模块构建7.架构展示以及网络构建 8.网络结构打印ResNet50v2完整结…

Python——1

一、注释 &#xff08;1&#xff09;单行注释&#xff1a;#需要注释的内容&#xff08;#&#xff09; &#xff08;2&#xff09;多行注释&#xff1a;需要注释的内容&#xff08;三引号&#xff09; 二、变量及变量类型 1.变量 语法定义&#xff1a;变量名 变量值&#…

【小程序】微信云托管服务

链接 官方文档 云托管官网 特点 无需自提供服务&#xff0c;有云托管平台自动分配&#xff0c;并自动缩容/扩容支持多种语言及模板实例采用容器化管理方式实现服务部署支持小程序内网访问&#xff0c;仅公网测试&#xff0c;提供足够的安全防护&#xff0c;微信用户就近接入…

Python:BeautifulSoup库介绍

BeautifulSoup库介绍 1、BeautifulSoup是Python中的一个第三方库&#xff0c;其最主要的功能是处理HTML文档 ⑴查找HTML文档中的指定标签 ⑵获取HTML文档中指定标签的标签名、标签值、标签属性等 ⑶修改HTML文档中指定标签 2、BeautifulSoup库将HTML文档解析为一…

服务器如何做端口映射,使服务器之间通信,然后访问目标网站(baidu.com)

文章目录 服务器如何做端口映射&#xff0c;使服务器之间通信&#xff0c;然后访问目标网站&#xff08;baidu.com)问题缘由所需环境操作步骤1. 目的服务器设置2. 中间服务器设置3. 修改客户端 总结 服务器如何做端口映射&#xff0c;使服务器之间通信&#xff0c;然后访问目标…

DataX读取Hive Orc格式表丢失数据处理记录

文章目录 问题问题概述问题详细描述 原因解决方法修改源码验证 问题 问题概述 DataX读取Hive Orc存储格式表数据丢失 问题详细描述 同步Hive表将数据发送到Kafka&#xff0c;Hive表A数据总量如下 SQL&#xff1a;select count(1) from A; 数量&#xff1a;19397281使用Dat…

HTML小游戏25 —— HTML5拉杆子过关小游戏(附完整源码)

本节教程我会带大家使用 HTML 、CSS和 JS 来制作一个HTML5拉杆子过关小游戏 ✨ 前言 &#x1f579;️ 本文已收录于&#x1f396;️100个HTML小游戏专栏&#xff1a;100个H5游戏专栏https://blog.csdn.net/qq_53544522/category_12064846.html&#x1f3ae; 目前已有100小游戏…

交叉编译--build、--host、--target、--prefix

一、编译例子 ./configure --build编译平台 --host运行平台 --target目标平台 [各种编译参数]build&#xff1a;表示目前我们正在运行的平台名称是什么&#xff0c;如&#xff1a;当前我们是在电脑上编译该系统&#xff0c;那么我们的 --build 就可能是 x86&#xff0c;如果…

如何避免因为 Kubernetes 和 Kafka 而被解雇

本文由 Bing AI 生成。Bing AI 真是尽显程序员本色&#xff0c;我等它生成文章的过程中发现出现了 Markdown 语法&#xff0c;结果点复制过来的就是直接 Markdown 文档。 Kubernetes 和 Kafka 是两个非常流行的技术&#xff0c;它们分别用于容器编排和分布式消息传递。它们的优…

XSD2Code++ Crack

XSD2Code Crack XSD2Code是为那些希望在将复杂的XML和JSON模式转换为NetCore时节省时间的开发人员设计的。它使用简单且灵活&#xff0c;可以很容易地集成到任何项目中&#xff0c;并适应开发人员的需求。它通过直观、可定制的用户界面&#xff0c;真正提高了生产力。使用XSD2C…

【SpringCloud】初步认识微服务

文章目录 1.认识微服务1.1微服务由来1.2为什么需要微服务&#xff1f; 2.两种架构2.1.单体架构2.2.分布式架构 3.微服务的特点4.SpringCloud5.总结最后说一句 1.认识微服务 随着互联网行业的发展&#xff0c;对服务的要求也越来越高&#xff0c;服务架构也从单体架构逐渐演变为…

K8s基础10——数据卷、PV和PVC、StorageClass动态补给、StatefulSet控制器

文章目录 一、数据卷类型1.1 临时数据卷&#xff08;节点挂载&#xff09;1.2 节点数据卷&#xff08;节点挂载&#xff09;1.3 网络数据卷NFS1.3.1 效果测试 1.4 持久数据卷&#xff08;PVC/PV&#xff09;1.4.1 效果测试1.4.2 测试结论 二、PV、PVC生命周期2.1 各阶段工作原理…

华为机试真题 数组奇偶排序

人寄语: 准备面试华为外包德科,记录一下一些面试题; 牛客网代码提交的坑,可以看一下下面的第一道题,ide本地编译通过,牛客网死活不通过,提交代码提示:返回非0。原因分析   查询得知,结果非零的意思的代码退出的时候不是以正常的0退出的,而是非0状态,也就是代码出错…

操作系统进程线程(三)—进程状态、同步互斥、锁、死锁

Linux下同步机制 POSIX信号量&#xff1a;可用于进程同步&#xff0c;也可用于线程同步POSIX互斥锁条件变量&#xff1a;只能用于线程同步。 进程同步的四种方法 临界区 对临界资源进行访问。 同步和互斥 同步&#xff1a;多个进程因为合作产生直接制约关系&#xff0c;使…

教你如何正确使用ChatGPT

目录 前言 一、ChatGPT Sidebar 二、免费镜像 三、共享账号 总结 前言 ChatGPT 是一种基于深度学习技术的自然语言处理工具&#xff0c;能够用于文本生成、语言翻译等任务。然而&#xff0c;其使用需要一定的技术基础和相关知识&#xff0c;不少用户可能会遇到一些问题。…

从功能到自动化,4个月时间我是如何从点工进入互联网大厂的

1、知识体系化 不知不觉&#xff0c;入行软件测试也有五个年头。待过创业公司也待过上市公司。做过功能测试、自动化测试也做过性能测试。做过测试新人也做过测试组长。如果要是从这5年中说出最宝贵的经验&#xff0c;我想应该是知识体系化。那么什么是知识体系化&#xff0c;…

SViT 实验记录

目录 一、网络的搭建 1、Conv Stem 2、各阶段的模块 3、3X3卷积 二、前向传播过程 1、Stem 2、各阶段中的基本模块STT Block 1&#xff09;CPE模块 2&#xff09;STA模块 网络结构 一、网络的搭建 论文中的结构原图 基本模块 1、Conv Stem (patch_embed): PatchEmbed…