uboot命令体系 源码解读并从中添加命令

news2024/11/15 8:24:39

1、uboot命令体系基础

1.1、使用uboot命令

(1)uboot启动后进入命令行底下,在此输入命令并回车结束,uboot会接收这个命令并解析,然后执行。

1.2、uboot命令体系实现代码在哪里

(1)uboot命令体系的实现代码在uboot/common/下,其中若干个cmd_xxx.c、command.c、 main.c都是和命令有关的。

1.3、每个命令对应一个函数

(1)uboot中的每个命令都对应着一个函数。譬如说bootm内核启动命令对应着do_bootm函数,但我们执行bootm 30008000命令去启动内核时,背后实际调用的是do_bootm函数,这个函数就是这个命令的执行过程。

1.4、命令参数以argc&argv的方式传给函数

(1)有些uboot的命令还支持传递参数。也就是说命令背后对应的函数的参数列表中有argc和argv,命令体系会把我们执行命令时的命令+参数(md 30000000 10)以argc(3)和argv(argv[0]=md, argv[1]=30000000 argv[2]=10)的方式传递给执行这个命令的函数。
举例,以help命令为例:
help命令背后对应的函数名叫:do_help。在uboot/common/command.c的236行。
int do_help (cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])

2、uboot命令解析和执行过程分析

2.1、从main_loop说起

(1)uboot启动的第二阶段的最后进入了一个死循环,死循环的循环体就是main_loop,main_loop函数在死循环中不断被执行。
(2)main_loop函数执行一遍,就是一个获取命令、解析命令、执行命令的过程。
(3)run_command函数就是用来执行命令的函数。

2.2、run_command函数详解:以下是简化版

//command.h 41~55行
struct cmd_tbl_s {
    char        *name;        /* Command Name            *///命令名字
    int        maxargs;    /* maximum number of arguments    *///命令最多可以接收的参数
    int        repeatable;    /* autorepeat allowed?        *///直接回车会自动执行上一条命令
                    /* Implementation function    */
    int        (*cmd)(struct cmd_tbl_s *, int, int, char *[]);//命令执行时调用的函数
    char        *usage;        /* Usage message    (short)    *///使用说明(短)
#ifdef    CFG_LONGHELP
    char        *help;        /* Help  message    (long)    *///使用说明(长)
#endif
#ifdef CONFIG_AUTO_COMPLETE
    /* do auto completion on the arguments */
    int        (*complete)(int argc, char *argv[], char last_char, int maxv, char *cmdv[]);
#endif
};
//main.c 1284~1441行
int run_command (const char *cmd, int flag)
{
    cmd_tbl_t *cmdtp;
    char cmdbuf[CFG_CBSIZE];    /* working copy of cmd        */
    
    char *str = cmdbuf;
    char *argv[CFG_MAXARGS + 1];    /* NULL terminated    */
    int argc;
    int repeatable = 1;




    clear_ctrlc();        /* forget any previous Control C */
    //crtl+c的实现

    if (!cmd || !*cmd) {//cmd不指向空,并且cmd指向的地方有内容
        return -1;    /* empty command */
    }

    if (strlen(cmd) >= CFG_CBSIZE) {//CFG_CBSIZE = 256,这里的意思uboot的命令不可以超过256
        puts ("## Command too long!\n");
        return -1;
    }

    strcpy (cmdbuf, cmd);//命令复制一份到cmdbuf这个局部变量数组

    /* Process separators and check for invalid
     * repeatable commands
     */


    //以下这个循环是在解析命令中的转义字符
    while (*str) {

        /*
         * Find separator, or string end
         * Allow simple escape of ';' by writing "\;"
         */
        for (inquotes = 0, sep = str; *sep; sep++) {
            if ((*sep=='\'') &&
                (*(sep-1) != '\\'))
                inquotes=!inquotes;

            if (!inquotes &&
                (*sep == ';') &&    /* separator        */
                ( sep != str) &&    /* past string start    */
                (*(sep-1) != '\\'))    /* and NOT escaped    */
                break;
        }

        /*
         * Limit the token to data between separators
         */
        token = str;
        if (*sep) {
            str = sep + 1;    /* start of command for next pass */
            *sep = '\0';
        }
        else
            str = sep;    /* no more commands for next pass */
//解析转义字符结束

        

        /* Extract arguments */
        if ((argc = parse_line (finaltoken, argv)) == 0) {
            //parse_line函数把"md 30000000 10"解析成argv[0]=md, argv[1]=30000000 argv[2]=10;
            rc = -1;    /* no command at all */
            continue;
        }

        /* Look up command in command table */
        if ((cmdtp = find_cmd(argv[0])) == NULL) {
            //find_cmd(argv[0])函数去uboot的命令集当中搜索有没有argv[0]这个命令
            printf ("Unknown command '%s' - try 'help'\n", argv[0]);//没有找到命令就会打印这句
            rc = -1;    /* give up after bad command */
            continue;
        }

        /* found - check max args */
        if (argc > cmdtp->maxargs) {//超过命令所能接收的最大参数个数
            printf ("Usage:\n%s\n", cmdtp->usage);//打印出这个命令的使用说明
            rc = -1;
            continue;
        }




        /* OK - call function to do the command */
        if ((cmdtp->cmd) (cmdtp, flag, argc, argv) != 0) {//执行这个命令所对应的函数
            rc = -1;
        }

        repeatable &= cmdtp->repeatable;

    }

    return rc ? rc : repeatable;
}

2.3、关键点分析

(1)控制台命令获取。在run_command函数中作为第一个参数cmd
(2)命令解析。parse_line函数把"md 30000000 10"解析成argv[0]=md, argv[1]=30000000 argv[2]=10;
(3)命令集中查找命令。find_cmd(argv[0])函数去uboot的命令集合当中搜索有没有argv[0]这个命令,
(4)执行命令。(cmdtp->cmd) (cmdtp, flag, argc, argv);最后用函数指针的方式调用执行了对应函数。
思考:关键点在于find_cmd函数如何从uboot的命令集中找到这个命令,判断这个命令是不是uboot支持的合法命令,uboot的命令集又是怎么样构成的?这些都取决于uboot的命令体系机制。
(uboot是如何完成命令的这一套设计的,命令如何去添加、存储、索引的。)

3、uboot的命令集是如何构成的

3.1、可能的构成方式

(1)数组。结构体数组,数组中每一个结构体就是一个命令的所有信息。坏处是数组的大小需要一开始就确定,不能动态添加命令来存储。
(2)链表。链表的每个节点就对应着一个命令的所有信息,所有的命令都被放在一条链表上。这样就解决了数组方式的不灵活。坏处是需要额外的内存开销(链表指针),并且链表的各种算法(遍历、插入、删除等)的代码都具有一定的时间复杂度。
(3)uboot没有使用数组和链表,而是使用了一种新的方式来实现这个功能。

3.2、命令结构体cmd_tbl_t

不管uboot以什么方式来构建命令集,都需要一个结构体来包含这个命令的所有信息。

struct cmd_tbl_s {
char        name;        /* Command Name            */
int        maxargs;    /* maximum number of arguments    */
int        repeatable;    /* autorepeat allowed?        */
/* Implementation function    */
int        (*cmd)(struct cmd_tbl_s *, int, int, char *[]);
char        usage;        /* Usage message    (short)    */
#ifdef    CFG_LONGHELP
char        help;        /* Help  message    (long)    */
#endif
#ifdef CONFIG_AUTO_COMPLETE /* do auto completion on the arguments */
int        (*complete)(int argc, char *argv[], char last_char, int maxv, char *cmdv[]);
#endif
};
typedef struct cmd_tbl_s    cmd_tbl_t;

(1)name:命令名称,字符串格式。
(2)maxargs:命令最多可以接收的参数个数
(3)repeatable:指示这个命令是否可以重复执行。重复执行是uboot命令行的一种工作机制,就是直接按回车则会执行上一条执行的命令。
(4)cmd:函数指针,命令对应的函数的函数指针,将来执行这个命令的函数时使用这个函数指针来调用。
(5)usage:命令的短帮助信息。对命令的简单描述。
(6)help:命令的长帮助信息。对命令的细节信息。
(7)complete:函数指针,uboot中的命令的参数也可以实现自动补全的效果,背后是一个函数来实现的,这个函数指针就指向这里,但在uboot中一般没有使用。
总结:uboot的命令体系在工作时,一个命令对应一个cmd_tbl_t结构体的一个实例,然后uboot支持多少个命令,就需要多少个结构体实例。uboot的命令体系把这些结构体实例管理起来,当用户输入了一个命令时,uboot会去这些结构体实例中查找(查找方法和存储管理的方法有关)。如果找到则执行命令,如果未找到则提示命令未知。

3.3、uboot实现命令管理的思路

(1)每一个结构体实例构成一个命令

(2)使用链接脚本,给每个命令结构体实例添加特定段属性(用户自定义段),添加段属性后就标记着这个实例是属于我们自定义的某个段的,之后链接时链接器会将带有该段属性的所有内容都链接在一起排列(紧挨着,不会夹杂其他东西,也不会丢掉一个带有这种段属性的,但是顺序是乱序的)。相当于水果摊,将不同种类的水果放在一起,苹果放在一起,香蕉放在一起。。。,这个段属性就是起标记作用,我们给每个实例都附加一个段属性,就代表着他们是属于同一个段的。

(3)uboot第二阶段将整个uboot重定位到DDR中,那么这个段也会被加载到DDR中。加载到DDR中的uboot镜像中,带有特定段属性的这一段其实就是命令结构体的集合,有点像一个结构体数组。

(4)段的起始地址和结束地址 定义在链接脚本u-boot.lds中,决定了这些命令集的开始和结束地址。

4、uboot如何处理命令集

4.1、uboot命令结构体是如何实例化的

(1)通过一个宏 U_BOOT_CMD
这个宏定义在uboot/common/command.h中(89~101行)。

#define Struct_Section  __attribute__ ((unused,section (".u_boot_cmd")))
//Struct_Section将来会被__attribute__ ((unused,section (".u_boot_cmd")))替换

#ifdef  CFG_LONGHELP
#define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) \
cmd_tbl_t __u_boot_cmd_##name Struct_Section = {#name, maxargs, rep, cmd, usage, help}
/*
 *    cmd_tbl_t:变量类型
 *     __u_boot_cmd_##name:变量名,##name是gcc编译器的一种语法,这个##name将来会被宏中的name所替代
 *    Struct_Section:被__attribute__ ((unused,section (".u_boot_cmd")))替换,
 *        __attribute__这个宏是gcc编译器的一种语法,给前面的变量__u_boot_cmd_##name赋予一个段属性,
 *        这里是指定这个变量属于哪个段,属于.u_boot_cmd段
 */
#else    /* no long help info */
#define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) \
cmd_tbl_t __u_boot_cmd_##name Struct_Section = {#name, maxargs, rep, cmd, usage}
#endif    /* CFG_LONGHELP */

举例:
U_BOOT_CMD(
version, 1, 1, do_version,
“version - print monitor version\n”,
NULL
);
这个宏替换后变成:
cmd_tbl_t u_boot_cmd_version attribute ((unused,section (“.u_boot_cmd”))) = {#name, maxargs, rep, cmd, usage, help}
总结:这个U_BOOT_CMD宏的理解,关键在于结构体变量的名字和段属性。名字使用##作为连字符,使用__attribute
((unused,section (“.u_boot_cmd”)))附加用户自定义段属性,以保证链接时将这些数据结构链接在一起排布。

(2)链接脚本 (56~58行)。

__u_boot_cmd_start = .;
    .u_boot_cmd : { *(.u_boot_cmd) } #自定义了.u_boot_cmd段,这个段专门用来存放uboot的命令集
    __u_boot_cmd_end = .;

4.2、find_cmd函数详解

//command.h (57~60行)
typedef struct cmd_tbl_s    cmd_tbl_t;

extern cmd_tbl_t  __u_boot_cmd_start;
extern cmd_tbl_t  __u_boot_cmd_end;

//command.c (344~378行)
cmd_tbl_t *find_cmd (const char *cmd)
{
    cmd_tbl_t *cmdtp;
    cmd_tbl_t *cmdtp_temp = &__u_boot_cmd_start;    /*Init value */
    //__u_boot_cmd_start这个符号在uboot.lds链接脚本处定义,在command.h文件(57~60行)处使用extern声明
    //这个符号的含义是:命令集的首地址
    const char *p;
    int len;
    int n_found = 0;

    /*
     * Some commands allow length modifiers (like "cp.b");
     * compare command name only until first dot.
     */
    len = ((p = strchr(cmd, '.')) == NULL) ? strlen (cmd) : (p - cmd);
    //查找,看输入的命令中是否有. , 因为有些命令是有.的, 有些则没有, 例如md.b 30000000 10
    //这里是计算命令的长度, 以md.b命令为例, 如果没有. , 则是strlen(cmd) = 2; 如果有. ,则是(p - cmd) = 2。
    for (cmdtp = &__u_boot_cmd_start;
         cmdtp != &__u_boot_cmd_end;
         cmdtp++) {
        if (strncmp (cmd, cmdtp->name, len) == 0) {
            //将命令结构体段中每个结构体的name属性取出与当前cmd比较,即查看这个命令是否存在
            if (len == strlen (cmdtp->name))
                return cmdtp;    /* full match */

            cmdtp_temp = cmdtp;    /* abbreviated command ? */
            n_found++;
        }
    }
    if (n_found == 1) {            /* exactly one match */
        return cmdtp_temp;
    }

    return NULL;    /* not found or ambiguous command */
}

(1)find_cmd函数的任务是从uboot的命令集中查找是否有某个命令。如果找到则返回这个命令结构体的指针,如果未找到返回NULL。
(2)函数的实现思路很简单,如果不考虑命令带点的情况(md.b md.w这种)就更简单了。查找命令的思路其实就是for循环遍历数组的思路,不同的是以指针增加的方式来遍历,数组中的每个元素是结构体变量类型。

4.3、U_BOOT_CMD宏详解

(1)这个宏其实就是定义了一个命令对应的结构体变量,这个变量名和宏的第一个参数有关,因此只要宏调用时传参的第一个参数不同则定义的结构体变量不会重名。

4.4、验证U_BOOT_CMD宏

验证方法:将前面的相关内容放到一个C文件,然后对这个C文件只预处理不编译(gcc test.c -E -o test.i)
test.c内容:

struct cmd_tbl_s {
    char        *name;        /* Command Name            *///命令名字
    int        maxargs;    /* maximum number of arguments    *///命令最多可以接收的参数
    int        repeatable;    /* autorepeat allowed?        *///直接回车会自动执行上一条命令
                    /* Implementation function    */
    int        (*cmd)(struct cmd_tbl_s *, int, int, char *[]);//命令执行时调用的函数
    char        *usage;        /* Usage message    (short)    *///使用说明(短)
#ifdef    CFG_LONGHELP
    char        *help;        /* Help  message    (long)    *///使用说明(长)
#endif
#ifdef CONFIG_AUTO_COMPLETE
    /* do auto completion on the arguments */
    int        (*complete)(int argc, char *argv[], char last_char, int maxv, char *cmdv[]);
#endif
};

typedef struct cmd_tbl_s    cmd_tbl_t;

#define Struct_Section  __attribute__ ((unused,section (".u_boot_cmd")))
#define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) \
cmd_tbl_t __u_boot_cmd_##name Struct_Section = {#name, maxargs, rep, cmd, usage, help}


int
do_version (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
    char version_string[] = "test";
    printf ("\n%s\n", version_string);
    return 0;
}
//对命令的结构体的填充使用了一个宏
U_BOOT_CMD(
    version,    1,        1,    do_version,
    "version - print monitor version\n",
    NULL
);

int main()
{
    return 0;
}

生成的test.i内容:

struct cmd_tbl_s {
    char        *name;        /* Command Name            *///命令名字
    int        maxargs;    /* maximum number of arguments    *///命令最多可以接收的参数
    int        repeatable;    /* autorepeat allowed?        *///直接回车会自动执行上一条命令
                    /* Implementation function    */
    int        (*cmd)(struct cmd_tbl_s *, int, int, char *[]);//命令执行时调用的函数
    char        *usage;        /* Usage message    (short)    *///使用说明(短)
#ifdef    CFG_LONGHELP
    char        *help;        /* Help  message    (long)    *///使用说明(长)
#endif
#ifdef CONFIG_AUTO_COMPLETE
    /* do auto completion on the arguments */
    int        (*complete)(int argc, char *argv[], char last_char, int maxv, char *cmdv[]);
#endif
};

typedef struct cmd_tbl_s    cmd_tbl_t;

#define Struct_Section  __attribute__ ((unused,section (".u_boot_cmd")))
#define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) \
cmd_tbl_t __u_boot_cmd_##name Struct_Section = {#name, maxargs, rep, cmd, usage, help}


int
do_version (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
    char version_string[] = "test";
    printf ("\n%s\n", version_string);
    return 0;
}
//对命令的结构体的填充使用了一个宏
U_BOOT_CMD(
    version,    1,        1,    do_version,
    "version - print monitor version\n",
    NULL
);

int main()
{
    return 0;
}

5、在uboot中增加自定义命令

5.1、在已有的c文件中直接添加命令

在这里插入图片描述
(1)在uboot/common/command.c中添加一个命令,叫:mycmd
(2)在已有的.c文件中添加命令比较简单,直接使用U_BOOT_CMD宏即可添加命令,并给命令提供一个do_xxx的对应的函数就完成了。
(3)添加完成后要重新编译工程(make distclean; make x210_sd_config; make),然后烧录uboot进开发板去使用这个命令。
(4)还可以在函数中使用argc和argv来验证命令行传参。
以下是uboot命令行下验证我们添加自定义命令是否成功
在这里插入图片描述

5.2、自建一个c文件并添加命令

(1)在uboot/common目录下新建一个命令文件,叫cmd_chm.c(对应的命令名就叫chm,对应的函数就叫do_chm函数),然后在c文件中添加命令对应的U_BOOT_CMD宏和函数。注意头文件包含不要漏掉。
在这里插入图片描述
(2)在uboot/common/Makefile中添加上cmd_chm.o,目的是让Make在编译时能否把cmd_chm.c编译链接进去。
在这里插入图片描述
(3)重新编译烧录。重新编译步骤是:make distclean; make x210_sd_config; make

5.3、心得体会:uboot命令体系的优点

(1)uboot的命令体系本身稍微复杂,但是他写好之后就不用动了。我们后面在移植uboot时也不会去动uboot的命令体系。我们最多就是向uboot中去添加命令,向uboot中添加命令是非常简单的。

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

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

相关文章

【博学谷学习记录】超强总结,用心分享 | 架构师 MongoDB学习总结

文章目录 MongoDB基本使用Nosql简介什么是NoSQL为什么使用NoSQLRDBMS vs NoSQLNoSQLNoSQL的优缺点缺点 MongoDB基础什么是MongoDB存储结构主要特点 基本概念和传统数据库对比集合命令规范 文档key的命令规范注意事项 MongoDB的数据类型BSON支持的数据类型 MongoDB基本使用 Nos…

DT MongoDB Plug -in description

目录 DT MongoDB | Client Create MongoDB Client Connect By Url Get Collection DT MongoDB | Collection Insert One Insert Many Delete One Delete Many Replace One Update One Update Many Find Find One DT MongoDB | Document Create MongoDB Documen…

【算法与数据结构】顺序表

顺序表 数据结构 结构定义结构操作 顺序表:结构定义 一个数组,添加额外的几个属性:size, count等 size: 数组有多大 count: 数组中当前存储了多少元素 顺序表三部分: 一段连续的存储区:顺序表存储元素的地方整型…

【Linux】Linux安装Java环境(OracleJDK)

文章目录 前言第一步,到官网下载jdk1.8第二步,下载下来上传到/opt目录下,并且解压第三步,解压之后配置环境变量:第四步,刷新配置文件第五步,查看版本 前言 linux环境为CentOS7.8 版本。 上期跟…

shell教程

面试题: 1.Shell中单引号和双引号区别 1)单引号不取变量值 2)双引号取变量值 3)反引号,执行引号中命令 4)双引号内部嵌套单引号,取出变量值 5)单引号内部嵌套双引号,不取出变量值 一、shell脚本 1.shell脚本概…

ChatGLM的搭建过程

本次搭建的是清华大学开源的ChatGLM。源码地址。模型地址。 1、开启BBR加速 如何开启BBR加速可以去看我的这篇文章,Linux开启内核BBR加速。 2、拉取ChatGLM源码和ChatGLM模型 点击这里跳转到源码处。 点击这里跳转到模型下载处。 我这里在下载之前创建了一个目…

与ChatGPT的一次技术对话

文章目录 前言 - 向引领前沿技术的伟大工作者致敬提问:请给我推荐几个最先进的目标检测AI模型提问:YOLOv4是什么原理,有什么创新提问:请问你知道yolov5吗提问: 那yolov5又有什么创新呢提问:你刚刚的回答正确…

SwiftUI 4.0 新 LabeledContent 视图帮您解决所有对齐烦恼

概览 在用 SwiftUI Form 设计 App 界面时,最头疼的就是内部视图对齐的问题了。好不容易适配了 iOS 中的布局,到了 iPadOS 或 MacOS 上却变得一团糟。 有没有一劳永逸,简单方便的办法呢? 如上图所示:我们利用 SwiftUI…

3. SQL底层执行原理详解

一条SQL在MySQL中是如何执行的 1. MySQL的内部组件结构1.1 Server层1.2 Store层 2. 连接器3. 分析器3.1 词法分析器原理 4. 优化器5. 执行器6. bin-log归档 本文是按照自己的理解进行笔记总结,如有不正确的地方,还望大佬多多指点纠正,勿喷。 …

基于ASP.NET的Web应用系统架构探讨

摘要:提出了一种基于ASP.NET开发方式的四层架构的Web应用系统构造思想。其基本内容是:将面向对象的UML建模与Web应用系统开发相结合,将整个系统分成适合ASP.NET开发方式的应用表示层、业务逻辑层、数据访问层和数据存储层四层结构…

[CF复盘] Codeforces Round 871 (Div. 4)20230506

[CF复盘] Codeforces Round 871 Div. 4 20230506 总结A. Love Story1. 题目描述2. 思路分析3. 代码实现 B. Blank Space1. 题目描述2. 思路分析3. 代码实现 C. Mr. Perfectly Fine1. 题目描述2. 思路分析3. 代码实现 D. Gold Rush1. 题目描述2. 思路分析3. 代码实现 E. The La…

UNeXt:基于 MLP 的快速医学图像分割网络

UNeXt是约翰霍普金斯大学在2022年发布的论文。它在早期阶段使用卷积,在潜在空间阶段使用 MLP。通过一个标记化的 MLP 块来标记和投影卷积特征,并使用 MLP 对表示进行建模。对输入通道进行移位,可以专注于学习局部依赖性。 UNeXt 基本架构 U…

考研数学高数1-1综合测试-函数及其性质

今天听完强化课之后去做学习包的题,发现没带平板,如果写到纸上,塞到书里又不知道去哪了,所以索性就拿Latex写了,虽然有一点麻烦,但是好在数量不多,就这么写吧。 都是我自己写的过程,…

第三十八章 Unity GUI系统(下)

上一章节我们将了UI的锚点,关于锚点我们只讲了一半,因为锚点并不只是一个点,它还可以是一个矩形。 我们可以将锚点拆开(鼠标选中后拖动),也就是将“四瓣雪花”拆成“四瓣”。那么此时锚点就成为一个矩形。我…

00-docker篇: linux系统安装docker操作 (最实用的操作)

目录 1. docker 简介 -> 简易理解: -> docker是否有自己仓库呢 -> docker 是靠什么运行呢 -> 简单说点docker优点 2. linux安装docker ps: 如果是新服务器 请直接看2. 3 -> 2.1: 查看内核版本: -> 2.2 如果有残留docker, 删除指令 -> 2.3 yu…

2023年全国职业院校技能大赛网络建设与运维-网络运维部分

全国职业院校技能大赛 网络建设与运维 五、网络运维 某单位网络拓扑架构如下,交换机连接两台服务器,其中Server1服务器是数字取证服务器,Server2服务器是应急响应服务器,通过交换设备相连,通过路由设备连接到安全设…

Portraiture4最新版滤镜P图一键磨皮插件

今天coco玛奇朵给大家带来了一款ps磨皮插件,超级简单好用。Portraiture 滤镜是一款 Photoshop,Lightroom 和 Aperture 插件,DobeLighttroom 的 Portraiture 消除了选择性掩蔽和逐像素处理的繁琐的手工劳动,以帮助您在肖像修整方面…

如何使用 ChatGPT 来快速编写产品需求文档(PRD)

PRD 生成 ChatGPT 即了解具体的编程知识,也了解编程之前的需求设计过程。因此产品经理也可以使用 ChatGPT 来快速编写PRD(产品需求文档, production requirement documentation)。 根据需求编写 PRD 首先,我们可以尝试把需求交给 ChatGPT,…

模型如何压缩?使用轻量化的模型压缩技术剪枝(pruning)

深度学习模型参数太多,本地服务器部署没有问题,但是如果部署到移动端、边缘端,像手机、树莓派等,它们的性能不能满足,所以我们要压缩模型大小,让他们可以部署到边缘端 模型压缩:使用轻量化的模型…

Redis 布隆过滤器总结

Redis 布隆过滤器总结 适用场景 大数据判断是否存在来实现去重:这就可以实现出上述的去重功能,如果你的服务器内存足够大的话,那么使用 HashMap 可能是一个不错的解决方案,理论上时间复杂度可以达到 O(1) 的级别,但是…