【进程】进程概念及相关函数实现

news2024/11/13 23:51:57

目录

0. 进程概述

1. 创建进程

1.1 进程的创建:fork函数

1.2 进程的等待:wait()、waitpid()

1.3 特殊进程:僵尸进程、孤儿进程、守护进程

1.4 进程的终止:exit和_exit函数

1.5 进程退出清理:atexit函数

1.6 进程的创建 :vfork函数

2. 进程的替换:exec函数族

3. system函数:

总结:


0. 进程概述

进程的定义

程序和进程的区别:

  •         程序:是静态的,存放在磁盘上的可执行文件。
  •         进程:是动态的,是运行在内存中的程序的执行实例。
  •         程序是一些指令的有序集合,而进程是程序执行的过程,进程是程序的一次执行过程。
  •         进程的状态是变化的,其包括进程的创建、调度和消亡。
  •         程序只要运行,此时就是进程,程序没运行一次,就会创建一个进程。

在Linux系统中,进程是管理事务的基本单元。

进程拥有自己独立的处理环境和系统资源(处理器、存储器、I/O设备、数据、程序)。

进程的状态及转换:

进程整个生命周期可以简单的划分为三种状态:

就绪态:

        进程已经具备执行的一切条件,正在等待分配CPU的处理时间。

执行态:

        该进程正在占用CPU运行。

等待态:

        进程因不具备某些执行条件而暂时无法继续执行的状态。

进程的调度机制:

        时间片轮转,上下文切换。

        多进程不是说一个进程执行完再执行另一个进程,而是交替执行的,一个进程执行一段时间,然后下一个进程再执行一段时间,依次类推,所有进程执行完之后再回到第一个进程继续执行,依次类推。

进程三个状态的转换关系:

 进程控制块:

        进程控制块就是用于保存一个进程信息的结构体,又称之为PCB。

        OS是根据PCB来对并发执行的进程进行控制和管理的。系统在创建一个进程的时候会开辟一段内存空间存放与此进程相关的PCB数据结构。

        PCB是操作系统中最重要的记录型数据结构。PCB中记录了用于描述进程进展情况及控制进程运行所需的全部信息。

        PCB是进程存在的唯一标志,在Linux中PCB存放在task_struct结构体中。

        /usr/src/linux-heads....generic/include/linux/sched.h

PCB结构体中的部分数据:

调度数据:

        进程的状态、标志、优先级、调度策略等。

时间数据:

        创建该进程的时间、在用户态的运行时间、在内核态的运行时间等。

文件系统数据:

        umask掩码、文件描述符表等。

内存数据、进程上下文、进程标识(进程号)

。。。

进程控制:

进程号:

        每一个进程都是由一个进程号来标识,其类型为pid_t,进程号的范围:0-32767。

        进程号总是唯一的,但是进程号可以重用。当一个进程终止后,其进程号就可以再次使用了。

在ubuntu中查看当前系统中所有的开启的进程:

ps ajx

PPID:当前进程的父进程的进程号。

PID:当前进程的进程号。

PGID:当前进程所在的组的进程组ID。

COMMAND:当前进程的名字

特殊的进程号:

  •         在Linux系统中进程号由0开始。
  •         进程号为0及1的进程由内核创建。
  •         进程号为0的进程通常是调度进程,常被称为交换进程(swapper)。
  •         进程号为1的进程通常是init进程,init进程是所有进程的祖先。
  •         除调度进程外,在Linux下面所有的进程都由init进程直接或间接创建。

进程号(PID)

        标识进程的一个非负整型数。

父进程号(PPID)

        任何进程(除Init进程)都是由另一个进程创建,该进程称为被创建进程的父进程,对应的进程号称为父进程号(PPID)。

进程组号(PGID)

        进程组是一个或多个进程的集合。他们之间相互关联,进程组可以接收同一终端的各种信号,关联的进程有一个进程组号(PGID)。

Linux操作系统中提供了三个获得进程号的函数getpid()、getpgid()、getppid()。

#include<sys/types.h>

#include<unistd.h>

pid_t getpid(void);

功能:获取当前进程的进程号。

pid_t getppid(void);

功能:获取当前进程的父进程的进程号。

pid_t getpgid(pid_t pid);

功能:获取当前进程所在进程组的进程号。

代码示例:

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

int main()
{
    printf("pid = %d\n", getpid());
    printf("ppid = %d\n", getppid());
    printf("pgid = %d\n", getpgid(getpid()));
    while (1);
    return 0;
}

执行截图:

1. 创建进程

1.1 进程的创建:fork函数

fork函数:

#include<unistd.h>

pid_t fork(void);

功能:在已有的进程基础上创建一个子进程。

参数:

        无

返回值:

        成功:

                >0:子进程的进程号,标识父进程的代码区。

              ==0:子进程的代码区。

        失败:

                -1:返回给父进程,子进程不会创建。

        使用fork函数得到的子进程是父进程的一个复制品,它从父进程处继承了整个进程的地址空间。

地址空间:

        包括进程上下文、进程堆栈、打开的文件描述符、信号控制设定、进程优先级、进程组号等。子进程所独有的只有它的进程号,计时器等。因此,使用fork函数的代价是很大的。 

fork函数执行完毕后父子进程的空间示意图:

创建子进程

不区分父子进程(不推荐)

代码示例:

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

int main()
{
    //通过fork函数创建一个子进程
    //注意:只要执行了一次fork,就会在原有的进程基础上创建一个新的子进程
    //而且如果fork之后不区分父子进程的代码区,则后面的所有代码都会执行
    fork();
    printf("hadj{\n");
    while (1);
    return 0;
}

执行截图:

区分父子进程(推荐)

代码示例:

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


int main()
{
    //父子进程是来回交替执行的,谁先谁后不一定。
    pid_t pid;
    pid = fork();
    if (pid < 0)    //创建子进程失败
    {
        perror("fail to fork");
        exit(1);
    }
    else if (pid > 0)   //父进程代码区
    {
        while (1)
        {
            printf("in parent process\n");
            sleep(1);
        }
    }
    else   //子进程代码区(pid == 0)
    {
        while (1)
        {
            printf("in son process\n");
            sleep(1);
        }
    }
    return 0;
}

执行截图:

父进程拥有独立的地址空间、

代码示例:

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

int a = 555;

int main()
{
    pid_t pid;
    pid = fork();
    static int  b = 777;
    int c = 888;
    //子进程会复制父进程fork之前的所有内容
    if (pid < 0)
    {
        perror("fail to fork");
        exit(1);
    }
    else if (pid > 0)
    {
        printf("in partent process\n");
        a++;
        b++;
        c++;
        printf("a = %d, b = %d, c = %d\n", a, b, c);
    }
    else
    {
        sleep(1);
        printf("in son process\n");
        printf("a = %d, b = %d, c = %d\n", a, b, c);
    }
    while (1);

    return 0;
}

执行截图:

 代码示例:

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


int main()
{

    int fd;
    if ((fd = open("file.txt", O_RDONLY)) == -1)
    {
        perror("fail to open");
        exit(1);
    }
    //子进程会继承父进程的一些共有的区域,例如磁盘空间,内核空间。
    //文件描述符的偏移量保存在内核空间中,所以父进程改变偏移量,子进程获取的偏移量是改变之后的。
    pid_t pid;
    pid = fork();

    if (pid < 0)
    {
        perror("fail to fork");
        exit(1);
    }
    else if (pid > 0)
    {
        printf("in parent process\n");
        char  buf[32] = "";
        if (read(fd, buf, 30) == -1)
        {
            perror("fail to read");
            exit(1);
        }
        printf("buf = [%s]\n", buf);
    }
    else
    {
        sleep(1);
        printf("in son process\n");

        char buf[32] = "";
        if (read(fd, buf, 30) == -1)
        {
            perror("fail to read");
            exit(1);
        }
        printf("buf = [%s]\n", buf);

    }
    while (1);
    return 0;
}

执行截图:

子进程继承父进程的空间

进程的挂起:

进程在一定的时间内没有任何动作,称为进程的挂起。

#include<unistd.h>

unsigned int sleep(unsigned in sec);

功能:

        进程挂起指定的秒数,知道指定的时间用完或受到信号才解除挂起。

返回值:

        若进程挂起到sec指定的时间则返回0,中断则返回剩余秒数。

注意:

        进程挂起指定的秒数后程序并不会立即执行,系统只是将此进程切换到就绪态。

代码示例:

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

int main()
{
        while(1)
        {
                printf("hello world\n");
                sleep(2);
        }
        return 0;
}

执行截图:

 

1.2 进程的等待:wait()、waitpid()

父子进程有时需要简单的进程间同步,如父进程等待子进程的结束。

Linux下提供了两个等待函数wait()、waitpid()。

wait函数

#include<sys/types.h>

#include<sys/wait.h>

pid_t wait(int *status);

功能:

        等待子进程终止,如果子进程终止了,此函数会回收子进程的资源。

        调用wait函数的进程会挂起,直到它的一个子进程退出或收到一个不能被忽视的信号时才被启用

        若调用进程没有子进程或它的子进程已经结束,该函数立即返回。

参数:

        status:函数返回时,参数status中包含子进程退出时的状态信息。

                        子进程的退出信息在一个int中包含了多个字段,

                        用宏定义可以取出其中的每个字段。

                        子进程可以通过exit或_exit函数发送退出状态。

返回值:

        成功:子进程的进程号。

        失败:-1

代码示例:

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


int main()
{
    pid_t pid;
    pid = fork();

    if (pid < 0)
    {
        perror("fail to fork");
        exit(1);
    }
    if (pid > 0)
    {
        //不接收子进程的退出状态
        wait(NULL);
        //接收子进程退出状态,子进程中必须用exit或_exit函数退出进程时发送退出状态
        int status = 0;
        wait(&status);
        if (WIFEXITED(status) != 0)
        {
            printf("son process return status:%d\n", WEXITSTATUS(status));
        }
        printf("in pratent proces\n");

    }
    else
    {
        int i = 0;
        for (i = 0; i < 5; i++)
        {
            printf("in son process\n");
            sleep(1);
        }
        exit(2);
    }
    return 0;
}

执行截图:

waitpid函数

#include<sys/types.h>

#include<sys/wait.h>

pid_t waitpid(pid_t pid, int *status, int options);

功能:

        等待子进程终止,如果子进程终止了,此函数会回收子进程的资源。

参数:

        pid:指定的进程或者进程组。

                pid>0:等待进程ID等于PID的子进程

                pid+0:等待同一个进程组中的任何子进程,如果子进程已经加入了别的进程组,waitpid不会等待它。

                pid=-1:等待任一子进程,此时waitpid和wait作用一样。

                pid<-1:等待指定进程组中的任何子进程,这个子进程组的ID等于pid的绝对值

        status:保存子进程退出时的状态信息。

        options:选项

                0:同wait,阻塞父进程,等待子进程退出。

                WNOHANG:没有任何已经结束的子进程,则立即返回。

                WUNTRACED:如果子进程暂停了则此函数马上返回,并且不予以理会子进程的结束状态。(跟踪调试,很少使用)

返回值:

        成功:返回状态改变了的子进程的进程号;如果设置了选项WNOHANG并且pid指定的进程存在,返回0。

        失败:返回-1。当pid指示的子进程不存在,或此进程存在,但不是调用进程的子进程,waitpid 就会出错返回,这时 errno 被设置为 ECHILD。

wait(status)  <==> waitpid(-1, status, 0)

代码示例:

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


int main()
{

    pid_t pid;
    pid = fork();
    if (pid < 0)
    {
        perror("fail to fork");
        exit(1);
    }
    else if (pid > 0)
    {
        waitpid(pid, NULL, 0);
        printf("in father process\n");
    }
    else
    {
        int i = 0;
        for (i = 0; i < 5; i++)
        {
            printf("in son  process\n");
            sleep(1);
        }
    }
    return 0;
}

 执行截图:

1.3 特殊进程:僵尸进程、孤儿进程、守护进程

  • 僵尸进程(Zombie Process) 进程已运行结束,但进程的占用的资源未被回收,这样的进程称为僵尸进程。 子进程已运行结束,父进程未调用 wait 或 waitpid 函数回收子进程的资源是子进 程变为僵尸进程的原因。
  • 孤儿进程(Orphan Process) 父进程运行结束,但子进程未运行结束的子进程。
  • 守护进程(精灵进程)(Daemon process) 守护进程是个特殊的孤儿进程,这种进程脱离终端,在后台运行。

1.4 进程的终止:exit和_exit函数

exit函数:

#include<stdlib.h>

void exit(int value)

参数:

        status:返回给父进程的参数(低 8 位有效)。

                        一般失败设置为非0,成功设置为0

_exit函数:

#include void _exit(int value)

参数:

        status:返回给父进程的参数(低 8 位有效)。

                一般失败设置为非0,成功设置为0

exit和_exit函数的区别:

  •         exit为库函数,而_exit为系统调用。
  •         exit会刷新缓冲区,但是_exit不会刷新缓冲区
  •         一般使用exit

 代码示例:

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

void fun()
{
    printf("nihao hangzhou");
    //测试return是带换行\n
   // printf("nihao hangzhou\n");
    
    //主函数中退出进程,子函数中退出当前函数。
    //      return  ;
    //退出一个进程,刷新缓冲区
    //exit(0);  
    //退出一个进程,不刷新缓冲区    
    _exit(0);
    printf("welcoom to binjiang\n");
}
int main()
{
    printf("hello world\n");
    fun();
    printf("hello kitty\n");
    return 0;
}

执行截图:

return截图:

exit截图:

 

_exit截图:

 

1.5 进程退出清理:atexit函数

进程在退出前可以使用atexit函数注册退出函数

#include<stdlib.h>

int atexit(void (*function)(void));

功能:

        注册进程正常结束前调用的函数,进程退出执行注册函数。

参数:

        function:进程结束前,调用函数的入口地址。

        一个进程中可以多次调用atexit函数注册清理函数,正常结束前调用函数的顺序和注册时的顺序相反。 

代码示例:

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


void clear_fun1(void)
{
    printf("perform clear fun1 \n");
}
void clear_fun2(void)
{
    printf("perform clear fun2 \n");
}
void clear_fun3(void)
{
    printf("perform clear fun3 \n");
}
int main()
{
    //atexit在进程结束时才会执行对应的回调函数
    //注意调用顺序是反的
    atexit(clear_fun1);
    atexit(clear_fun2);
    atexit(clear_fun3);
    printf("process exit 3 sec later!!!\n");
    sleep(3);
    return 0;
}

执行截图:

1.6 进程的创建 :vfork函数

vfork函数

#include<sys/types.h>

#include<unistd.h>

pid_t vfork(void)

功能:

        vfork函数和fork函数一样都是在已有的进程中创建一个新的进程,但它们创建的子进程是有区别的。

返回值:

        创建子进程成功,则在子进程中返回0,父进程中返回子进程ID。

        出错返回-1.

fork和vfork函数的区别:

        vfork保证子进程先运行,在它调用exec或exit之后,父进程才可能被调度运行。

        vfork和fork一样都创建一个子进程,但它并不将父进程的地址空间完全复制到子进程中,因为子进程会立即调用exec(或exit),于是也就不访问该地址空间。

        相反,在子进程中调用exec或exit之前,它在父进程的地址空间中运行,在exec之后子进程会有自己的进程空间。

子进程在父进程之前运行

代码示例:

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

int main()
{
    //使用vfork,子进程会先执行,直到子进程执行exit或exec后,父进程才会执行。
    pid_t pid;
    pid = vfork();
    if (pid < 0)
    {
        perror("fail to  vfork");
        exit(1);
    }
    else if (pid > 0)
    {
        while (1)
        {
            printf("in father process\n");
            sleep(1);
        }
    }
    else
    {
        int i = 0;
        for (i = 0; i < 5; i++)
        {
            printf("in son process\n");
            sleep(1);
        }
        exit(0);
    }
    return 0;
}

执行截图:

 子进程与父进程共享一个空间

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

int a = 10;
int main()
{
//父子进程共享一个空间
    pid_t pid;
    int b = 9;
    pid = vfork();
    if (pid < 0)
    {
        perror("fail to vfork");
        exit(1);
    }
    else if (pid > 0)
    {
        printf("in father process a = %d b = %d\n", a, b);
    }
    else
    {
        a++;
        b++;
        printf("in son process a = %d b = %d\n", a, b);
        exit(0);
    }
    return 0;
}

 执行截图:

2. 进程的替换:exec函数族

exec函数族,是由六个exec函数组成的

  1. exec函数族提供了六种在进程中启动另一个进程的方法
  2. exec函数族可以根据指定的文件名或目录名找到可执行文件
  3. 调用exec函数的进程并不创建新的进程,故调用exec前后,进程的进程号并不会改变,其执行的程序完全由新的程序替换,而新的程序则从其main函数开始执行。

exec函数族取代调用进程的数据段、代码段和堆栈段。

 #include<unistd.h>

int execl(const char *path, const char *arg, .../*(char *) NULL */);

int execlp(const char *file, const char *arg, .../* (char *) NULL */);

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

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

int execle(const char *path, const char *arg, .../*, (char *) NULL*/, char *const envp[]);

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

功能:

        在一个进程里面执行另一个程序,主要用于执行命令。

参数:

        path:命令或者程序的路径。

        l:如果是带l的函数,对应的命令或者程序是通过每一个参数进行传递的,最后一个为NULL表示结束。

                例如:"ls","-l",NULL

        v:如果是带v的函数,对应的命令或者程序是通过一个指针数组来传递的,指针数组的最后一个元素为NULL标志结束。

                char *str[] = {"ls", "-l", NULL};

        p:如果是不带p的函数,第一个参数必须传当前命令或者程序的绝对路径,

              如果是带p的函数,第一个参数既可以是绝对路径,也可以是相对路径。

返回值:

        失败:-1

代码示例:

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


int main()
{
    pid_t pid;
    pid = fork();
    if (pid < 0)
    {
        perror("fail to fork");
        exit(1);
    }
    else if (pid > 0)
    {
        printf("in father process\n");
        wait(NULL);
        printf("the soon process has quied\n");
    }
    else
    {
        printf("in son process\n");
#if 0
        if (execl("/bin/ls", "ls", "-l", NULL) == -1)
        {
            perror("fail to execl");
            exit(1);
        }
#endif
#if 0
        if (execlp("ls", "ls", "-l", NULL) == -1)
        {
            perror("fail to execlp");
            exit(1);
        }
#endif
#if 0
        char* str[] = { "ls", "-l",NULL };
        if (execv("/bin/ls", str) == -1)
        {
            perror("fail to execv");
            exit(1);
        }
#endif
#if 0
        if (execlp("./myshell.sh", "./myshell.sh", NULL) == -1)
        {
            perror("fail to execlp");
            exit(1);
        }
#endif
#if 1
        if (execlp("./myshell.sh", "./myshell.sh", NULL) == -1)
        {
            perror("fail to execlp");
            exit(1);
        }
#endif
#if 0
        if (execlp("./hello", "./hello", NULL) == -1)
        {
            perror("fail to execl");
            exit(1);
        }
#endif
#if 0
        if (execl("./hello", "./hello", NULL) == -1)
        {
            perror("fail to execl");
            exit(1);
        }
#endif

        printf("hello world\n");
    }
    return 0;
}
80, 1          Bot

一个进程调用exec后,除了进程ID,进程还保留了下列特征不变:

  • 父进程号
  • 进程组号
  • 控制终端
  • 根目录
  • 当前工作目录
  • 进程信号屏蔽集
  • 未处理信号
  • .....

3. system函数:

#include<stdlib.h>

int system(const char *command);

功能:

        system会调用fork函数产生子进程,子进程调用exec启动/bin/sh -c string来执行参数string字符串所代表的命令,此命令执行完后返回调用进程。

参数:

        要执行的命令的字符串。

返回值:

        如果command为NULL,则system()函数返回非0,一般为1.

        如果system()在调用/bin/sh时失败则返回127,其他失败原因返回-1。

注意:

        system调用成功后会返回执行shell命令后的返回值。其返回值可能为1、127也可能为-1,故最好应再检查errno来确认执行成功。

代码示例:

#include<stdio.h>
#include<stlib.h>

int main()
{
    system("clear");
    system("ls -l");
    system("./hello");
    system(. / myshell.sh");

        return 0;
}

总结:

        本章介绍了进程的概念、创建和使用。通过了解进程管理的核心原理,我们可以更好地利用和控制系统资源,实现并发操作,并构建高效的应用程序。深入了解进程的概念和技术,将使我们能够设计出更稳定、可扩展的系统架构,并解决多进程之间的通信和同步问题。

        希望这篇博客能帮助您在应用程序开发过程中更好地理解和应用进程管理的重要性。谢谢阅读!        

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

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

相关文章

【我的创作纪念日】关于某站的音频爬虫+GUI

文章目录 一、前言&机遇二、爬虫代码三、爬虫GUI四、文件打包五、结果展示未来可期 一、前言&机遇 许久没看私信内容&#xff0c;一上线就看到了官方的私信&#xff0c;我已经来到CSDN1024天啦&#xff01; 想到注册这个号的初衷是学习记录爬虫&#xff0c;后面渐渐变…

抖音产业带服务商哪些类目在招募?开通需要什么条件?

5月&#xff0c;刚刚结束的抖音电商生态大会上&#xff0c;抖音电商总裁魏雯雯披露&#xff0c;近一年平台GMV增幅超80%。其中&#xff0c;商城GMV同比增长277%&#xff0c;电商搜索GMV同比增长159%&#xff0c;货架场景GMV在平台GMV占比超30%。过去一年&#xff0c;抖音电商直…

数据结构与算法——树与二叉树

&#x1f60a;数据结构与算法——树与二叉树 &#x1f680;前言&#x1f680;树&#x1f6a2;树的定义&#x1f6a2;树的基本术语&#x1f6a2;有序树和无序树&#x1f6a2;森林 &#x1f680;二叉树&#x1f6a2;二叉树的定义&#x1f6a2;二叉树的性质&#x1f6a2;满二叉树&…

【VUE】Element UI 表单校验出现async-validator: [‘discipline is required‘]报错

问题:async-validator: [discipline is required] 选择器已经获取到数值&#xff0c;却显示获取到 解决办法如下

线性规划算法

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家&#xff1a;点击跳转 本文部分内容来自网友博客 一&#xff0c;线性规划 例如&#xff0c;一个企业“生产计划”的线性规划模型如下&#xff1a; 是subjec…

​如何优雅的卸载Edge浏览器

如何优雅的卸载Edge浏览器 由于Edge浏览器越来越复杂&#xff0c;功能越来越繁琐我是真的一刻也用不下去了。虽然我主力是火狐浏览器&#xff0c;Edge用来访问一些只能使用Chromium内核的网页作为备用。 但是我现在一打开Edge浏览器我就窝火&#xff0c;也懒得再去调整优化&a…

【表格树状】jqgrid表格树状折叠效果实现(附代码,留邮箱发demo)

【写在前面】有段时间没好好的整理一篇前端文章了&#xff0c;之前的6月城市活动也结束了&#xff0c;期待下周的榜单公布&#xff0c;其实这个月还有一个东西也让我牵肠挂肚的&#xff0c;就是软考的成绩也会在这个月的中旬公布&#xff0c;也是感觉很悬。既成定局&#xff0c…

【中间件-Openjob】高性能任务调度框架Openjob简介及快速搭建

介绍基础基础信息任务调度框架对比 特性高可靠高性能定时调度分布式计算延迟任务工作流程权限管理告警监控跨语言 安装访问docker-compose安装在线访问 总结 介绍 一款分布式高性能任务调度框架&#xff0c;支持多种定时任务、延时任务、工作流设计、轻量级分布式计算、无限水平…

el-form动态嵌套表单验证

v-for 遍历的表单校验 根据官网的介绍&#xff0c;是在 el-form-item 中使用 :rules 属性&#xff0c;同时 prop 属性直接定位到具体循环元素。这个用法的前提是在循环外面包裹一个 el-form 元素&#xff0c;v-for 位于 el-form-item 中。 <template><el-form:model…

深入浅出对话系统——对话管理与对话生成

引言 对话管理 我们知道对话管理主要包括状态追踪(DST)和策略优化(DPO)。 对话管理模块包含两个子任务&#xff1a; 对话状态追踪(Dialogue State Tracking) 根据用户输入和对话历史识别对话状态&#xff1b;策略学习(Policy Learning) 根据识别到的对话状态选择合适的下一步…

BC SU21 对象 ZJHD_LGO 已交付;只能进行有限更改

ECC 升级S4 库位从ECC的唯一库位 → S4 工厂 库位才能唯一。 那原先的 依据库存地 控制库位的zjhd打印机的权限需要新增工厂字段。 但是su21的修改的时候 提示 &#xff1a;对象 ZJHD_LGO 已交付&#xff1b;只能进行有限更改 查了一下&#xff0c;SAP官方说只能 把该权限…

HCIP第二次作业

要求&#xff1a;R1-R2-R3-R4-R5 RIP 100运行版本2 R6-R7 RIP 200 运行版本1 1.使用合理IP地址规划网络&#xff0c;各自创建环回接口 2.R1创建环回 172.16.1.1/24 172.16.2.1/24 172.16.3.1/24 3.要求R3使用R2访问R1环回 4.减少路由条目数量&#xff0c;R1-R2之间增加路由传递…

部署springboot项目读取外部配置文件

我们在部署springboot项目的时候&#xff0c;经常会遇到这样的情况&#xff1a;测试环境与生产环境的配置不一样&#xff0c;这就导致每次部署的时候都要修改配置文件再打包&#xff0c;即使用了nacos进行配置管理&#xff0c;但测试环境与生产环境的nacos部署的地方肯定不一样…

MyBatis简单入门

文章目录 快速入Mapper代理开发具体步骤使用mapper代理中的包扫描 查询字段名称不一致问题方法一&#xff1a;对SQL语句起别名方法二&#xff1a; 采用resultMap映射 条件查询单条件查询SQL中特殊字符的处理 多条件查询方式一&#xff1a;散装参数方式二&#xff1a;对象参数方…

ABAP:ABAP解析xml文件的方法

目前我在ECC的系统找到两种实现XML解析的办法&#xff0c;第一种是通过strans创建转化例程&#xff0c;然后在程序中调用转化例程来转化xml&#xff0c;第二种是调用方法按照node解析xml。 要转化的xml文件demo如下 <?xml version"1.0" encoding"Windows-…

1.5 编写自定位ShellCode弹窗

在笔者上一篇文章中简单的介绍了如何运用汇编语言编写一段弹窗代码&#xff0c;虽然简易ShellCode可以被正常执行&#xff0c;但却存在很多问题&#xff0c;由于采用了硬编址的方式来调用相应API函数的&#xff0c;那么就会存在一个很大的缺陷&#xff0c;如果操作系统的版本不…

提取图像中的文本信息(Tesseract OCR 和 pytesseract)

环境准备 安装Tesseract&#xff1a;点这里参考本人博客 下载第三方库 pip install Pytesseract这个库只自带了一个英语的语言包&#xff0c;这个时候如果我们图片中有对中文或者其他语言的识别需求&#xff0c;就需要去下载其他语言包 下载其他语言包 进入官网以后进入Tra…

MyBatisPlus基础知识

一、MyBatisPlus 1.MyBatisPlus入门案例与简介 这一节我们来学习下MyBatisPlus的入门案例与简介&#xff0c;这个和其他课程都不太一样&#xff0c;其他的课程都是先介绍概念&#xff0c;然后再写入门案例。而对于MyBatisPlus的学习&#xff0c;我们将顺序做了调整&#xff0…

STM32——MCU简单介绍

文章目录 一、单片机基础简介1.MCU简介&#xff08;1&#xff09;MCU的组成&#xff08;2&#xff09;常见的MCU 2.STM32简介&#xff08;1&#xff09;STM32&STM8产品型号--各个字母的含义 3.如何查手册&#xff08;1&#xff09;数据手册芯片信息总线框图时钟树内存映射 …

Java面试题及答案整理( 金九银十最新版,持续更新)

最近可能有点闲的慌&#xff0c;没事就去找面试面经&#xff0c;整理了一波面试题。我大概是分成了 Java 基础、中级、高级&#xff0c;分布式&#xff0c;Spring 架构&#xff0c;多线程&#xff0c;网络&#xff0c;MySQL&#xff0c;Redis 缓存&#xff0c;JVM 相关&#xf…