【Linux初阶】进程程序替换 | 初识、原理、函数、应用 makefile工具的多文件编译

news2025/3/1 11:03:31

🌟hello,各位读者大大们你们好呀🌟
🍭🍭系列专栏:【Linux初阶】
✒️✒️本篇内容:替换初识,替换原理,替换函数理解和使用,makefile工具的多文件编译,进程替换应用(简易命令行实现)
🚢🚢作者简介:计算机海洋的新进船长一枚,请多多指教( •̀֊•́ ) ̖́-

文章目录

  • 一、前言
  • 二、替换初识
    • 1.引入
    • 2.代码示例
  • 三、替换原理
    • 1.原理讲解
    • 2.为什么我exec函数后的代码都不执行了呢?
    • 3.exec函数调用失败
    • 4.子进程的替换会影响父进程吗?
  • 四、不同函数调用的对应使用方式
    • 1.常见的替换函数
      • (1)execl
      • (2)execlp
      • (3)execv
      • (4)execvp
    • 2.makefile工具的多文件编译
    • 3.替换成自己的可执行程序
    • 4.execle & execve
      • (1)execle and execvpe
      • (2)execve
    • 5. execvp的另一应用方式
    • 6.函数命名理解总结
  • 五、应用场景(简易命令行解释器的实现)
  • 结语


一、前言

在之前的学习中,我们学习了 fork子进程创建的知识,那么我们创建一个子进程的目的是什么呢?

  1. 想让子进程执行父进程代码的一部分,换句话说,执行父进程磁盘代码的一部分;
  2. 想让子进程执行一个全新的程序,换句话说,就是让子进程想办法加载磁盘上指定的程序,执行新程序的代码和数据;
  3. 而子进程加载磁盘上指定的程序,执行新程序的代码和数据的行为,我们称之为进程程序替换

二、替换初识

1.引入

在进程程序替换中,有许多对应的替换函数,它们大多以exec为开头。在深入学习替换的知识之前,我们先对其中一个较为简单的函数进行讲解,让大家先对进程程序替换的实现有直观感受,以便后续理解。

execl - 执行,下面这条代码的含义为将指定的程序加载到内存中,让指定进程执行。

#include <unistd.h>`
int execl(const char *path, const char *arg, ...)

我们要知道,实现进程替换操作,首先需要找到对应的程序,其次还需要提供具体的执行方法。

  • const char *path —— 找到对应的程序
  • const char *arg, … —— 提供具体的执行方法,此处的三个点为可变参数列表,我们可以暂时把它理解为省略号,代表不同的执行选项(选项最后必须以 NULL结尾);

这里我们以 ls指令,举一个简单的例子

ls -a -l

要执行上述指令,需要找到 ls的位置,还需要提供具体的执行方法,也就是 “ls”, “-a”, “-l”, NULL。

2.代码示例

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

int main()
{
    //.c->exe->load->process->运行->执行我们现在所写的代码
    printf("process is running...\n");

    // load->exe
    execl("/usr/bin/ls"/*要执行哪一个程序*/, NULL/*你想怎么执行*/); // all exec* end of NULL

    printf("process running done...\n");
}

通过运行上述代码,我们发现,它的结果相当于 ls指令(只是有颜色差异)

在这里插入图片描述
只要添加对应的选项,就可以实现功能

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

int main()
{
    //.c->exe->load->process->运行->执行我们现在所写的代码
    printf("process is running...\n");

    // load->exe
    execl("/usr/bin/ls"/*要执行哪一个程序*/, "ls", "--color=auto", "-a", "-l", NULL/*你想怎么执行*/); // all exec* end of NULL

    printf("process running done...\n");
}

运行上述代码,我们发现,它的结果相当于ls -a -l指令
在这里插入图片描述

我们通过运行自己的可执行程序,实现了Linux指令的功能,这就是程序替换体现。

细心的朋友可能发现了,我们自己写的代码为什么第二个 printf(“process running done…\n”); 不执行了呢?这里主要涉及到替换原理的知识,我们在下一节再和大家讲解。


三、替换原理

1.原理讲解

用fork创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支),子进程往往要调用一种exec函数以执行另一个程序。当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行。调用exec并不创建新进程,所以调用exec前后该进程的id并未改变

程序替换的本质,就是将指定程序的代码加载到指定的位置。这里的加载到指定的位置,通常会覆盖原有的代码和数据。
在这里插入图片描述

2.为什么我exec函数后的代码都不执行了呢?

到了这里我们就不难理解,为什么第二节中我们自己写的代码第二个 printf(“process running done…\n”); 不执行呢?答案就是,进程调用exec函数后,原来的代码和数据都被覆盖了!

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

int main()
{
    //.c->exe->load->process->运行->执行我们现在所写的代码
    printf("process is running...\n");

    // load->exe
    execl("/usr/bin/ls"/*要执行哪一个程序*/, "ls", "--color=auto", "-a", "-l", NULL/*你想怎么执行*/); // all exec* end of NULL

    // 为什么这里的printf没有在执行了???printf也是代码,是在execl之后的,
    // execl执行完毕的时候,代码已经全部被覆盖,开始执行新的程序的代码了,所以printf就无法执行了!
    printf("process running done...\n");
}

———— 我是一条知识分割线 ————

3.exec函数调用失败

通过上面的学习,我们知道,exec函数后面的代码不执行,是建立在函数调用成功的基础上的,只有调用失败,才会执行接下来的代码。

通过查询手册,我们不难得知,exec*函数只有在调用失败的时候才会有返回值,为什么它不存在调用成功的返回值呢?答案是不需要,因为成功了,原来的代码和数据就被覆盖了,就和接下来的代码无关了,判断毫无意义。

  • 这些函数如果调用成功则加载新的程序从启动代码开始执行,不再返回。
  • 如果调用出错则返回-1
  • 所以exec函数只有出错的返回值而没有成功的返回值。

代码示例,下面的 lsabcskd根本不存在,运行后会通过 perror输出对应的错误

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

int main()
{
    //.c->exe->load->process->运行->执行我们现在所写的代码
    printf("process is running...\n");

    // load->exe
    // 只要是一个函数,调用就有可能失败,如果没有替换成功,就是没有替换
    // exec*为什么没有成功返回值呢?因为成功了,就和接下来的代码无关了,判断毫无意义
    // execl 只要返回了,一定是错误了
    execl("/usr/bin/lsabcskd"/*要执行哪一个程序*/, "ls", "--color=auto", "-a", "-l", NULL/*你想怎么执行*/); // all exec* end of NULL
    
    perror("ececl"); //打印错误原因

    // 为什么这里的printf没有在执行了???printf也是代码,是在execl之后的,
    // execl执行完毕的时候,代码已经全部被覆盖,开始执行新的程序的代码了,所以printf就无法执行了!
    printf("process running done...\n");
    exit(1);
}

在这里插入图片描述

4.子进程的替换会影响父进程吗?

答案是,不会的!因为进程具有独立性,在操作系统内部,有虚拟地址空间+页表保证进程独立性,一旦有执行流想替换代码或数据,就会发生写时拷贝。

也就是说,一旦子进程想进行程序替换,那么就会发生写时拷贝,操作系统会给子进程在物理内存中开辟一块新空间,将原来的数据拷贝到新空间中,再对新空间中的数据做修改和替换。
在这里插入图片描述

代码示例

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/wait.h>

int main()
{
    printf("process is running...\n");
    pid_t id = fork();
    assert(id != -1);

    if (id == 0)
    {
        // 这里的替换,会影响父进程吗?? 进程具有独立性
        // 类比:命令行怎么写,这里就怎么传
        sleep(1);

        execl("/usr/bin/ls", "ls", "-a", "-l", "--color=auto", NULL);

        exit(1); //must failed
    }

    int status = 0;
    pid_t ret = waitpid(id, &status, 0);
    if (ret > 0) printf("wait success: exit code: %d, sig: %d\n", (status >> 8) & 0xFF, status & 0x7F);
}

在这里插入图片描述


四、不同函数调用的对应使用方式

1.常见的替换函数

其实有七种以exec开头的函数,统称exec函数

#include <unistd.h>`
int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execle(const char *path, const char *arg, ...,char *const envp[]);
int execvpe(const char *file, char *const argv[],char *const envp[]);

(1)execl

我们在之前章节的学习中已经大致了解了 execl的基础使用方法,其中 execl中的 l 我们可以把它理解为 list,标识将参数一个一个的传入 exec*中。

(2)execlp

execlp中的 p实际上代表的是 path,带 p的替换函数,不需要告诉他具体的程序路径,只需要告诉它程序是谁,就可以自动调用环境变量 PATH,进行可执行程序的查找。

代码示例

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/wait.h>

int main()
{
    printf("process is running...\n");
    pid_t id  = fork();
    assert(id != -1);

    if(id == 0)
    {
        // 这里的替换,会影响父进程吗?? 进程具有独立性
        // 类比:命令行怎么写,这里就怎么传
        sleep(1);

        //execl("/usr/bin/ls", "ls", "-a", "-l", "--color=auto", NULL);
        // 这里有两个ls, 重复吗?不重复,一个是告诉系统我要执行谁?一个是告诉系统,我想怎么执行
        execlp("ls", "ls", "-a", "-l", "--color=auto", NULL);

        exit(1); //must failed
    }

    int status = 0;
    pid_t ret = waitpid(id, &status, 0);
    if(ret>0) printf("wait success: exit code: %d, sig: %d\n", (status>>8)&0xFF, status & 0x7F);
}

在这里插入图片描述

(3)execv

int execv(const char *path, char *const argv[]);

v:vector,可以将所有的执行参数,放入数组中,统一传递,而不需要使用可变参数方案。

代码示例

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/wait.h>

int main()
{
    printf("process is running...\n");
    pid_t id = fork();
    assert(id != -1);

    if (id == 0)
    {
        sleep(1);

        char* const argv_[] = {
            "ls",
            "-a",
            "-l",
            "--color=auto",
            NULL
        };

        execv("/usr/bin/ls", argv_);

        exit(1); //must failed
    }

    int status = 0;
    pid_t ret = waitpid(id, &status, 0);
    if (ret > 0) printf("wait success: exit code: %d, sig: %d\n", (status >> 8) & 0xFF, status & 0x7F);
}

在这里插入图片描述

(4)execvp

int execvp(const char *file, char *const argv[]);

execvp和上面两个函数的 v,p的含义相同。也就是说,只要传入程序名和执行参数的数组即可运行。

代码示例

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/wait.h>

int main()
{
    printf("process is running...\n");
    pid_t id = fork();
    assert(id != -1);

    if (id == 0)
    {

        sleep(1);

        char* const argv_[] = {
            "ls",
            "-a",
            "-l",
            "--color=auto",
            NULL
        };

        execvp("ls", argv_);

        exit(1); //must failed
    }

    int status = 0;
    pid_t ret = waitpid(id, &status, 0);
    if (ret > 0) printf("wait success: exit code: %d, sig: %d\n", (status >> 8) & 0xFF, status & 0x7F);
}

在这里插入图片描述
通过上面的学习,我们已经了解如何替换操作系统的指令,那么如何将一个进程程序替换成我们自己的可执行程序呢?别急,我们会在接下来的讲解中找到答案。

2.makefile工具的多文件编译

我们知道,常规情况下,makefile只能一次性编译一个文件。

mybin : mybin.c
gcc - o $@ $^ -std = c99

.PHONY:clean
clean :
    rm - f mybin

在接下来的学习中,我们需要一次性编译多个文件,那么 makefile工具能不能支持呢?答案是可以的,我们可以定义一个为目标 all,all依赖于我们自己的代码文件,将 all跟在 .PHONY后面,最终实现多个文件的同时编译和清除。

.PHONY:all
all : mybin myexec

mybin : mybin.c
gcc - o $@ $^ -std = c99
myexec : myexec.c
gcc - o $@ $^ -std = c99

.PHONY:clean
clean :
rm - f myexec mybin

3.替换成自己的可执行程序

创建代码文件mybin

#include <stdio.h>
#include <stdlib.h>

int main()
{
    printf("我是另一个C程序\n");
    printf("我是另一个C程序\n");
    printf("我是另一个C程序\n");
    printf("我是另一个C程序\n");
    printf("我是另一个C程序\n");
    printf("我是另一个C程序\n");
    printf("我是另一个C程序\n");
    printf("我是另一个C程序\n");
    printf("我是另一个C程序\n");

    return 0;
}

创建代码文件myexec,同时在myexec中调用mybin(这里是在子进程中调用的)

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/wait.h>

int main()
{
    printf("process is running...\n");
    pid_t id = fork();
    assert(id != -1);

    if (id == 0) //子进程
    {

        sleep(1);

        char* const argv_[] = {
            "ls",
            "-a",
            "-l",
            "--color=auto",
            NULL
        };


        execl("./mybin", "mybin", NULL); 
        exit(1); //must failed
    }

    int status = 0;
    pid_t ret = waitpid(id, &status, 0);
    if (ret > 0) printf("wait success: exit code: %d, sig: %d\n", (status >> 8) & 0xFF, status & 0x7F);
}

在这里插入图片描述

总结:我们可以使用程序替换,调用任何后端语言(C/C++、python等)对应的可执行程序

4.execle & execve

函数中的 e代表的是自定义环境变量的意思

(1)execle and execvpe

int execle(const char *path, const char *arg, ...,char *const envp[]);
int execvpe(const char *file, char *const argv[],char *const envp[]);

修改代码文件 mybin

#include <stdio.h>
#include <stdlib.h>

int main()
{
    // 系统就有
    printf("PATH:%s\n", getenv("PATH"));
    printf("PWD:%s\n", getenv("PWD"));
    // 自定义
    printf("MYENV:%s\n", getenv("MYENV"));

    printf("我是另一个C程序\n");
    printf("我是另一个C程序\n");
    printf("我是另一个C程序\n");
    printf("我是另一个C程序\n");
    printf("我是另一个C程序\n");
    printf("我是另一个C程序\n");
    printf("我是另一个C程序\n");
    printf("我是另一个C程序\n");
    printf("我是另一个C程序\n");

    return 0;
}

在这里插入图片描述

代码示例(修改代码文件 myexe)

  • 只显示自定义环境变量
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/wait.h>

int main()
{
    printf("process is running...\n");
    pid_t id = fork();
    assert(id != -1);

    if (id == 0)
    {
        sleep(1);

        char* const envp_[] = {
            (char*)"MYENV=11112222233334444",
            NULL
        };

        execle("./mybin", "mybin", NULL, envp_); //自定义环境变量

        exit(1); //must failed
    }

    int status = 0;
    pid_t ret = waitpid(id, &status, 0);
    if (ret > 0) printf("wait success: exit code: %d, sig: %d\n", (status >> 8) & 0xFF, status & 0x7F);
}

在这里插入图片描述

  • 只显示系统环境变量
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/wait.h>

int main()
{
    printf("process is running...\n");
    pid_t id = fork();
    assert(id != -1);

    if (id == 0)
    {
        sleep(1);

        char* const envp_[] = {
            (char*)"MYENV=11112222233334444",
            NULL
        };
        extern char** environ;
        //execle("./mybin", "mybin", NULL, envp_); //自定义环境变量
        execle("./mybin", "mybin", NULL, environ); //实际上,默认环境变量你不传,子进程也能获取

        exit(1); //must failed
    }

    int status = 0;
    pid_t ret = waitpid(id, &status, 0);
    if (ret > 0) printf("wait success: exit code: %d, sig: %d\n", (status >> 8) & 0xFF, status & 0x7F);
}

在这里插入图片描述

  • 既显示自定义环境变量,也显示系统环境变量
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/wait.h>

int main()
{
    printf("process is running...\n");
    pid_t id = fork();
    assert(id != -1);

    if (id == 0)
    {
        sleep(1);

        char* const envp_[] = {
            (char*)"MYENV=11112222233334444",
            NULL
        };
        extern char** environ;
        //execle("./mybin", "mybin", NULL, envp_); //自定义环境变量
        putenv((char*)"MYENV=4443332211"); //将指定环境变量导入到系统中 environ指向的环境变量表
        execle("./mybin", "mybin", NULL, environ); //实际上,默认环境变量你不传,子进程也能获取

        exit(1); //must failed
    }

    int status = 0;
    pid_t ret = waitpid(id, &status, 0);
    if (ret > 0) printf("wait success: exit code: %d, sig: %d\n", (status >> 8) & 0xFF, status & 0x7F);
}

在这里插入图片描述

———— 我是一条知识分割线 ————
通过观察,我们可以发现execle对应的参数和我们的 main函数参数参数极为相似,实际上,它们的确有对应的关系,我们可以直接通过给 main传参,赋予 exec*替换函数其他的使用方法,这里的知识我们放到 5. execvp的另一应用方式 来进行演示。

int execle(const char *path, const char *arg, ...,char *const envp[]);
int main(int argc, char* argv[]char* env[])

(2)execve

execve为程序替换中唯一的系统调用接口,我们之前见到的所有 exec*函数,都是基于 execve系统调用接口做的封装,让我们具有更多选择性。

 int execve(const char *filename, char *const argv[], char *const envp[]);

5. execvp的另一应用方式

代码示例

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/wait.h>

int main(int argc, char* argv[])
{
    printf("process is running...\n");
    pid_t id = fork();
    assert(id != -1);

    if (id == 0)
    {

        sleep(1);
        // execvp传递方法如下
        // ./exec ls -a -l -> "./exec" "ls" "-a" "-l"
        execvp(argv[1], &argv[1]);

        exit(1); //must failed
    }

    int status = 0;
    pid_t ret = waitpid(id, &status, 0);
    if (ret > 0) printf("wait success: exit code: %d, sig: %d\n", (status >> 8) & 0xFF, status & 0x7F);
}

在这里插入图片描述

6.函数命名理解总结

上面讲述的函数原型看起来很容易混,但只要掌握了规律就很好记。

  1. l(list) : 表示参数采用列表
  2. v(vector) : 参数用数组
  3. p(path) : 有p自动搜索环境变量PATH
  4. e(env) : 表示自己维护环境变量

在这里插入图片描述

#include <unistd.h>
int main()
{
    char* const argv[] = { "ps", "-ef", NULL };
    char* const envp[] = { "PATH=/bin:/usr/bin", "TERM=console", NULL };
    
    execl("/bin/ps", "ps", "-ef", NULL);
    
    // 带p的,可以使用环境变量PATH,无需写全路径
    execlp("ps", "ps", "-ef", NULL);
    
    // 带e的,需要自己组装环境变量
    execle("ps", "ps", "-ef", NULL, envp);


	// 带v的,可以直接传指令选项的数组,这里是argv
    execv("/bin/ps", argv);

    // 带p的,可以使用环境变量PATH,无需写全路径
    execvp("ps", argv);
    
    // 带e的,需要自己组装环境变量
    execve("/bin/ps", argv, envp);//只有这一个是系统调用
    
    exit(0);
}

在这里插入图片描述


五、应用场景(简易命令行解释器的实现)

我们之前在 第四章节5小节execvp的另一应用方式 中,有这样一个例子,它实现了用我们自己的程序替换shell的指令,那么如果我们可以将运行的方式进一步简化,那么不就可以变成一个简易的 shell了吗?
在这里插入图片描述

下面,我将会带着大家结合相关知识,去制作一个简易的命令行解释器。

由于文章篇幅原因,我额外制作了一篇文章对简易命令行解释器的实现进行了讲解:【Linux初阶】进程替换的应用 - 简易命令行解释器的实现


结语

🌹🌹 Linux进程替换 的知识大概就讲到这里啦,博主后续会继续更新更多C++的相关知识,干货满满,如果觉得博主写的还不错的话,希望各位小伙伴不要吝啬手中的三连哦!你们的支持是博主坚持创作的动力!💪💪

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

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

相关文章

C++(4):表达式

表达式由一个或多个运算对象(operand)组成,对表达式求值将得到一个结果(result&#xff09;。字面值和变量是最简单的表达式&#xff08;expression)&#xff0c;其结果就是字面值和变量的值。把一个运算符&#xff08;operator)和一个或多个运算对象组合起来可以生成较复杂的表…

倒挂的解决方案你现在是一位计算机专家,来聊一聊:“美国的火星探测器Mars Path-finder 就是因为优先级倒挂而出现故障的故事”

目录 倒挂的解决方案 你现在是一位计算机专家&#xff0c;来聊一聊&#xff1a;“美国的火星探测器Mars Path-finder 就是因为优先级倒挂而出现故障的故事” ●使用中断禁止 具体证明请参阅Liu和Kayland于1973年发表的论文。 ● 因时序或外部中断或进程挂起而导致操作系统获…

数据结构-关键路径-理论

1.AOE-网 与AOV-网相对应的是AOE-网&#xff08;Activity On Netword&#xff09;&#xff0c;即以边表示活动的网。AOE-网是带权的有向无环图&#xff0c;其中&#xff0c;定点表示时间&#xff0c;弧表示活动&#xff0c;权表示活动持续的时间。通常AOE-网可用来估算工程的完…

Base64字符串从前台传到后台以后,“+”加号消失

记录一下问题&#xff1a; 使用 encodeURI(str) 对字符串进行加密的时候&#xff0c;后端解密会丢失 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content&…

第一章.机器学习的基础概念

第一章.机器学习的基础概念 1.1 机器学习的基础概念 1.机器学的概念&#xff1a; 机器学习就是机器从数据中总结经验。从数据中找出某种规律或者模型&#xff0c;并用他来解决某种实际问题。 2.机器学习的应用场景 1).事物本身存在某种潜在规律 2).某些问题难以使用普通编程…

two-stage目标检测算法

R-CNN 现在&#xff0c;将目光穿越回2012年&#xff0c;hinton刚刚提出alexnet的时代。 此时&#xff0c;该如何审视目标检测任务&#xff1f; 当时的目标检测采用的是滑动窗口手动特征分类器的思路。 该方法的弱点包括 速度慢 精度差 精度差的问题是由手工特征造成的&am…

【VS安装记录】Visual Studio 2022安装教程(超详细)

大家好&#xff0c;我是雷工&#xff01; 由于更换了电脑&#xff0c;很多软件需要重新安装&#xff0c;为了方便学习C#&#xff0c;今天有时间安装下Visual Studio 2022&#xff0c;顺便记录安装过程。 1、从官网下载并解压软件压缩包&#xff0c;然后打开文件夹。 2、双击…

切比雪夫不等式,大数定律及极限定理。

一.切比雪夫不等式 1.定理 若随机变量X的期望EX和方差DX存在,则对任意ε > 0,有   P{ |X - EX| > ε } < DX/ε2 或 P{ |X - EX| < ε } > 1 - DX/ε2 2.解析定理 ①该定理对 X 服从什么分布不做要求&#xff0c;仅EX DX存在即可。 ②“| |” 由于X某次…

linux kernel pwn 基础知识

基础知识 内核概述 内核架构 通常来说我们可以把内核架构分为两种&#xff1a;宏内核和微内核&#xff0c;现在还有一种内核是混合了宏内核与微内核的特性&#xff0c;称为混合内核。 宏内核&#xff08;Monolithic kernel&#xff09;&#xff0c;也译为集成式内核、单体…

网络原理——基础概念(端口号、分层、封装和复用)、各层协议(TCP/IP协议)(详细图解)

目录 一、基础概念 1、 IP地址 &#xff08;1&#xff09;点分十进制 2、端口号 3、协议 &#xff08;1&#xff09;协议的 组成部分 &#xff08;2&#xff09; 协议的 作用 4、五元组 5、协议分层 &#xff08;1&#xff09;分层的 好处 &#xff08;2&#xff0…

Overleaf中Latex问题——控制文本分两列显示(分栏布局)

文章目录 需求描述相关介绍实现代码实现效果参考和总结 需求描述 要写论文&#xff0c;需要分两列进行显示文本。但是默认都是单列展示&#xff0c;并且自动换行。 需要实现一下的效果 相关介绍 在$LaTeX 中&#xff0c;你可以使用 中&#xff0c;你可以使用 中&#xff0c…

Tokenview上线BRC-20浏览器,支持Ordinals API数据服务

5月20日&#xff0c;Tokenview团队宣布正式推出BRC-20代币浏览器&#xff0c;同时支持BTC Ordinals API数据服务。作为通用多链区块链浏览器&#xff0c;Tokenview以最快的速度推出了BRC-20浏览器&#xff0c;Ordinals API旨在为所有面向比特币的普通用户和开发者提供数据支持&…

追寻篮球梦想 点燃希望之光 2023年海彼特全国幼儿篮球联赛·总决赛圆满落幕

5月21日&#xff0c;由北京海彼特教育科技院主办的“2023年海彼特全国幼儿篮球联赛总决赛”。在河北体育馆隆重举行&#xff0c;精彩的比赛效果使体育馆顿时成为幼儿篮球界最高端、大气的舞台。 本次盛会联合举办方有&#xff1a; 河北体育馆 亚洲少儿体育协会 北京海彼特文…

组件123456789

前言&#xff1a;相信看到这篇文章的小伙伴都或多或少有一些编程基础&#xff0c;懂得一些linux的基本命令了吧&#xff0c;本篇文章将带领大家服务器如何部署一个使用django框架开发的一个网站进行云服务器端的部署。 文章使用到的的工具 Python&#xff1a;一种编程语言&…

G企孵化-千趣生活项目,10年互联网经验专业策划商业模式

G企孵化-千趣生活项目&#xff0c;10年互联网经验专业策划商业模式 背景&#xff1a;现在许许多多的项目&#xff0c;首先对外呼吁的口号就是“上市”但真正能做到上市的企业&#xff0c;确实没有几个&#xff0c;10年互联网经验的微三云胡佳东&#xff0c;给大家详细聊聊&…

【卡尔曼滤波的学习,以及一些理解】

卡尔曼滤波的一些理解 优秀的博客推荐直观理解卡尔曼滤波核心算法举个例子 最近两个多月在实习&#xff0c;做的是GPS定位相关的一些工作&#xff0c;同时也简单做了一下组合导航。卡尔曼滤波是组合不同传感器比较核心的算法&#xff0c;应用也比较广泛&#xff0c;也有很多文章…

电子数据保全及数据恢复

目录 一.创建虚拟磁盘 系统操作 1.创建虚拟磁盘文件 2.完成低级格式化——分区——高级格式化 3.虚拟磁盘创建完成 用winhex做 2. 镜像&#xff1a; 克隆&#xff1a; 计算分区的hash值&#xff1a; 二.FAT32文件系统 1.认识FAT32文件系统 三.NTFS文件系统 认识NTFS文…

数字信号处理5

好长时间没有更新了&#xff0c;一是这段时间事情比较多&#xff0c;另外一个&#xff0c;我觉得抄书其实意义不大&#xff0c;不如先看书&#xff0c;一个章节看完之后&#xff0c;再写&#xff0c;那样子的话&#xff0c;会效果更好一些&#xff0c;所以我就花了一段时间去把…

chatgpt赋能Python-python_chia

简介&#xff1a;什么是Python Chia&#xff1f; Python Chia是一种加密货币&#xff0c;它的挖矿过程使用Python编程语言。Python Chia是开源的&#xff0c;任何人都可以参与挖矿。 Chia使用绿色挖矿的方式&#xff0c;这意味着Chia的挖矿过程对环境没有任何负面影响。此外&…

typeScript开发

typeScript开发 1.TypeScript简介2.TypeScript 安装3.TypeScript 基础语法3.TypeScript 基础类型4.TypeScript 变量声明5.TypeScript 运算符6.TypeScript 条件语句7.TypeScript 循环8.TypeScript 函数9.TypeScript Number10.TypeScript String&#xff08;字符串&#xff09;11…