多进程控制

进程退出

在 Unix-like 系统中,exit()_exit() 都是用于终止进程的函数,但它们在细节上有一些重要的区别。

exit()

exit() 函数由 C 标准库提供,用于结束程序执行并返回一个状态码给操作系统。在调用 exit() 时,会执行以下步骤:

  1. 调用所有注册的退出函数:这些是通过 atexit()on_exit() 注册的函数。
  2. 刷新所有的标准 I/O 流:这包括标准输出和标准错误输出,确保所有缓冲的输出都被写入相应的文件或终端。
  3. 关闭所有的标准 I/O 流:这包括打开的文件和其他资源的清理。
  4. 调用 _exit() 函数:最终通过调用 _exit() 系统调用来终止进程。

_exit()

_exit() 函数是一个低级别的系统调用,由 POSIX 标准定义。它直接终止调用它的进程,并快速返回一个状态码给操作系统,但它不执行 exit() 中的清理步骤:

  1. 不刷新 I/O 缓冲区:任何未写入的缓冲数据都将丢失。
  2. 不执行注册的退出函数:通过 atexit() 注册的函数不会被调用。
  3. 立即终止进程:直接通知操作系统终止当前进程,不进行任何库或系统级的清理。
使用场景
  • exit():在大多数情况下使用 exit() 是更安全的选择,因为它确保了所有的输出都被适当处理并且所有的清理工作都得到了执行。这特别重要如果程序中涉及到文件操作或其他需要清理的资源。
  • _exit():在子进程调用 exec() 失败时使用 _exit() 是一种常见的用法,因为子进程可能不希望执行父进程注册的退出处理函数或者可能会破坏父进程的输出缓冲。另一个使用场景是在某些需要快速退出而不关心资源清理的紧急情况。
示例代码

这是一个简单的示例,展示了如何在父子进程中使用 exit()_exit()

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

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

    if (pid == -1) {
        perror("fork failed");
        exit(1);
    } else if (pid == 0) {
        // 子进程: 尝试执行某个命令
        printf("Child Process: Attempting to execute command\n");
        execlp("ls", "ls", NULL);
        // 如果 execlp 失败,使用 _exit 退出
        perror("execlp failed");
        _exit(1);
    } else {
        // 父进程: 等待子进程结束
        int status;
        wait(&status);
        if (WIFEXITED(status)) {
            printf("Child exited with status %d\n", WEXITSTATUS(status));
        }
        exit(0);
    }
}

在这个例子中,子进程在 exec() 失败时使用 _exit() 以确保不执行标准的清理操作,这可能会影响父进程。

/*
    #include <stdlib.h>
    void exit(int status);

    #include <unistd.h>
    void _exit(int status);

    status参数:是进程退出时的一个状态信息。父进程回收子进程资源的时候可以获取到。
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main() {

    printf("hello\n");
    printf("world");

    exit(0);
   // _exit(0); //一般很少用,不能刷新IO缓存区
    
    return 0;
}

孤儿进程

孤儿进程是在Unix-like操作系统中,其父进程已经结束或终止,而子进程仍在运行的进程。这类进程的直接父进程不存在,所以被操作系统的特定进程(在大多数Unix系统中是init进程,PID为1)收养。这个过程确保了孤儿进程不会无控制地运行,而是继续被合适地管理。

产生孤儿进程的情况

孤儿进程通常在以下情况下产生:

  1. 父进程结束执行:父进程的执行周期完成后结束,但它的一个或多个子进程仍在运行。
  2. 父进程异常终止:由于错误或其他原因,父进程异常终止,子进程仍在继续运行。
孤儿进程的处理

在Unix-like系统中,所有孤儿进程最终都会被init进程(或其他类型的系统级进程,如在一些现代系统中可能是systemd)收养。init进程周期性地执行等待操作来收集其子进程的退出状态,从而确保这些进程不会变成僵尸进程(未清理的终止进程)。

孤儿进程与僵尸进程的区别

孤儿进程和僵尸进程是两种常见的进程状态,但它们之间有明显的区别:

  • 孤儿进程:还在运行的进程,其父进程已经结束。这些进程已被系统的init进程收养。
  • 僵尸进程:已经结束但其退出状态尚未被父进程回收(通过wait()waitpid()调用)的进程。僵尸进程在系统资源表中占据位置,但不占用CPU资源。

实际应用

在实际应用中,程序员需要确保他们的程序能够正确管理子进程,避免留下大量的孤儿或僵尸进程,这可能会导致资源泄露或管理混乱。合适地使用进程监控和回收机制(如wait())是良好程序设计的重要部分。

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

int main() {

    // 创建子进程
    pid_t pid = fork();

    // 判断是父进程还是子进程
    if(pid > 0) {

        printf("i am parent process, pid : %d, ppid : %d\n", getpid(), getppid());

    } else if(pid == 0) {
        sleep(1);
        // 当前是子进程
        printf("i am child process, pid : %d, ppid : %d\n", getpid(),getppid());
       
    }

    // for循环
    for(int i = 0; i < 3; i++) {
        printf("i : %d , pid : %d\n", i , getpid());
    }

    return 0;
}

输出

worlddaic@daic:~/Linux/linuxwebserver/part02multiProgress/lesson21$ g++ orphan.c 
daic@daic:~/Linux/linuxwebserver/part02multiProgress/lesson21$ ./a.out 
i am parent process, pid : 3270, ppid : 2943
i : 0 , pid : 3270
i : 1 , pid : 3270
i : 2 , pid : 3270
daic@daic:~/Linux/linuxwebserver/part02multiProgress/lesson21$ i am child process, pid : 3271, ppid : 1
i : 0 , pid : 3271
i : 1 , pid : 3271
i : 2 , pid : 3271

僵尸进程

僵尸进程是在 Unix-like 操作系统中,已经完成执行(终止)但仍在进程表中占据一个条目的进程。这种状态的进程等待着父进程读取其终止状态,直到父进程通过 wait()waitpid() 系统调用来进行回收。在此之前,僵尸进程会保持在系统的进程表中,从而导致资源(如进程号)的占用,尽管它们已经不再占用内存、CPU时间或其他物理资源。

僵尸进程不能被kill -9杀死

僵尸进程产生的原因

僵尸进程的产生通常与父进程的行为有关:

  1. 父进程没有调用 wait():当一个子进程终止后,其状态必须由父进程查询。如果父进程没有调用 wait()waitpid() 来获取子进程的终止状态,那么子进程的进程描述符仍然保留在系统中。
  2. 父进程先于子进程终止:在父进程终止之后,子进程由 init 进程接管。如果 init 进程没有及时处理这些子进程的终止状态,这些子进程也会变成僵尸进程,直到被处理。
僵尸进程的影响

僵尸进程虽然不消耗除进程表外的其他资源,但大量的僵尸进程会占用系统有限的进程号,这可能导致系统性能下降,甚至在极端情况下阻止新的进程被创建。

如何处理僵尸进程

处理僵尸进程主要涉及两个方面:

  1. 父进程主动处理:在编写涉及子进程的程序时,父进程应该使用 wait()waitpid() 来回收子进程的状态,确保子进程不会变成僵尸进程。
  2. 自动处理:如果父进程已经终止,操作系统通常会让 init 进程自动接管并回收这些孤立的子进程,从而避免它们成为僵尸进程。
示例代码

这是一个可能导致僵尸进程的简单 C 程序示例:

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

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

    if (pid == 0) {
        // 子进程
        printf("This is the child process.\n");
        exit(0);  // 子进程退出
    } else {
        // 父进程
        printf("This is the parent process.\n");
        sleep(20);  // 父进程睡眠,不立即处理子进程
        wait(NULL);  // 处理子进程状态,避免僵尸进程
    }

    return 0;
}

在这个例子中,如果父进程在子进程退出后没有调用 wait(),那么子进程将成为僵尸进程,直到父进程处理它的终止状态。这种情况在父进程执行其他任务较长时间后尤其常见。正确的做法是使用 wait() 来确保子进程不会留下未处理的状态。

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

int main() {

    // 创建子进程
    pid_t pid = fork();

    // 判断是父进程还是子进程
    if(pid > 0) {
        while(1) {
            printf("i am parent process, pid : %d, ppid : %d\n", getpid(), getppid());
            sleep(1);
        }

    } else if(pid == 0) {
        // 当前是子进程
        printf("i am child process, pid : %d, ppid : %d\n", getpid(),getppid());
       
    }

    // for循环
    for(int i = 0; i < 3; i++) {
        printf("i : %d , pid : %d\n", i , getpid());
    }

    return 0;
}
daic@daic:~/Linux/linuxwebserver/part02multiProgress/lesson21$ g++ zombie.c 
daic@daic:~/Linux/linuxwebserver/part02multiProgress/lesson21$ ./a.out 
i am parent process, pid : 3450, ppid : 2943
i am child process, pid : 3451, ppid : 3450
i : 0 , pid : 3451
i : 1 , pid : 3451
i : 2 , pid : 3451
i am parent process, pid : 3450, ppid : 2943
i am parent process, pid : 3450, ppid : 2943
i am parent process, pid : 3450, ppid : 2943
i am parent process, pid : 3450, ppid : 2943
daic       3450  0.0  0.0   4516   824 pts/1    S+   11:24   0:00 ./a.out
daic       3451  0.0  0.0      0     0 pts/1    Z+   11:24   0:00 [a.out] <defunct>

进程回收

进程回收是操作系统中一项重要的任务,主要涉及回收已终止进程的资源,并清理系统中的进程表条目。这个过程对于防止资源泄漏和维持系统性能至关重要。主要的资源包括内存、文件描述符、环境变量、以及其他可能由进程分配的系统资源。

在 Unix-like 系统中,进程回收通常通过父进程调用 wait()waitpid() 系统调用来实现。这两个函数允许父进程获知子进程的终止状态,并从系统中清除该子进程的记录,从而防止其变成僵尸进程。

wait() 函数
  • wait() 函数使调用进程(通常是父进程)暂停执行,直到其任何一个子进程退出或接收到一个未被阻塞的信号。当子进程终止时,wait() 会收集子进程的终止状态,并将其从系统进程表中删除。

  • wait() 的原型为:

    pid_t wait(int *status);
    

    其中 status 是一个指针,用于存储子进程的终止状态信息。

    /*
        #include <sys/types.h>
        #include <sys/wait.h>
        pid_t wait(int *wstatus);
            功能:等待任意一个子进程结束,如果任意一个子进程结束了,此函数会回收子进程的资源。
            参数:int *wstatus
                进程退出时的状态信息,传入的是一个int类型的地址,传出参数。
            返回值:
                - 成功:返回被回收的子进程的id
                - 失败:-1 (所有的子进程都结束,调用函数失败)
    
        调用wait函数的进程会被挂起(阻塞),直到它的一个子进程退出或者收到一个不能被忽略的信号时才被唤醒(相当于继续往下执行)
        如果没有子进程了,函数立刻返回,返回-1;如果子进程都已经结束了,也会立即返回,返回-1.
    
    */
    #include <sys/types.h>
    #include <sys/wait.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    
    
    int main() {
    
        // 有一个父进程,创建5个子进程(兄弟)
        pid_t pid;
    
        // 创建5个子进程
        for(int i = 0; i < 5; i++) {
            pid = fork();
            //如果是子进程,不用继续产生孙子进程
            if(pid == 0) {
                break;
            }
        }
    
        if(pid > 0) {
            // 父进程
            while(1) {
                printf("parent, pid = %d\n", getpid());
    
                // int ret = wait(NULL);
                int st;
                int ret = wait(&st);//调用wait进行资源的回收
    
                if(ret == -1) {
                    break;
                }
    
                if(WIFEXITED(st)) {
                    // 是不是正常退出
                    printf("退出的状态码:%d\n", WEXITSTATUS(st));
                }
                if(WIFSIGNALED(st)) {
                    // 是不是异常终止
                    printf("被哪个信号干掉了:%d\n", WTERMSIG(st));
                }
    
                printf("child die, pid = %d\n", ret);
    
                sleep(1);
            }
    
        } else if (pid == 0){
            // 子进程
             while(1) {
                printf("child, pid = %d\n",getpid());    
                sleep(1);       
             }
    
            exit(0);
        }
    
        return 0; // exit(0)
    }
    
waitpid() 函数
  • waitpid() 函数与 wait() 类似,但它允许父进程等待特定的子进程,而不仅仅是任何子进程。这使得父进程能更精确地控制哪些子进程的状态被回收。

  • waitpid() 的原型为:

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

    其中 pid 指定要等待的子进程的进程 ID(或一组进程),status 用于存储终止状态,options 可以指定额外的行为,如非阻塞返回等。

    /*
        #include <sys/types.h>
        #include <sys/wait.h>
        pid_t waitpid(pid_t pid, int *wstatus, int options);
            功能:回收指定进程号的子进程,可以设置是否阻塞。
            参数:
                - pid:
                    pid > 0 : 某个子进程的pid
                    pid = 0 : 回收当前进程组的所有子进程    
                    pid = -1 : 回收所有的子进程,相当于 wait()  (最常用)
                    pid < -1 : 某个进程组的组id的绝对值,回收指定进程组中的子进程
                - options:设置阻塞或者非阻塞
                    0 : 阻塞
                    WNOHANG : 非阻塞
                - 返回值:
                    > 0 : 返回子进程的id
                    = 0 : options=WNOHANG, 表示还有子进程或者
                    = -1 :错误,或者没有子进程了
    */
    #include <sys/types.h>
    #include <sys/wait.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    
    int main() {
    
        // 有一个父进程,创建5个子进程(兄弟)
        pid_t pid;
    
        // 创建5个子进程
        for(int i = 0; i < 5; i++) {
            pid = fork();
            if(pid == 0) {
                break;
            }
        }
    
        if(pid > 0) {
            // 父进程
            while(1) {
                printf("parent, pid = %d\n", getpid());
                sleep(1);
    
                int st;
                // int ret = waitpid(-1, &st, 0);
                int ret = waitpid(-1, &st, WNOHANG);
    
                if(ret == -1) {
                    break;
                } else if(ret == 0) {
                    // 说明还有子进程存在
                    continue;
                } else if(ret > 0) {
    
                    if(WIFEXITED(st)) {
                        // 是不是正常退出
                        printf("退出的状态码:%d\n", WEXITSTATUS(st));
                    }
                    if(WIFSIGNALED(st)) {
                        // 是不是异常终止
                        printf("被哪个信号干掉了:%d\n", WTERMSIG(st));
                    }
    
                    printf("child die, pid = %d\n", ret);
                }
               
            }
    
        } else if (pid == 0){
            // 子进程
             while(1) {
                printf("child, pid = %d\n",getpid());    
                sleep(1);       
             }
            exit(0);
        }
    
        return 0; 
    }
    
为何进程回收是必要的

未经回收的终止进程(即僵尸进程)不会继续执行,但它们在进程表中占据位置,并保留与其相关的进程标识符(PID)。这些资源是有限的,如果大量产生僵尸进程,可能会导致系统无法生成新的进程。此外,僵尸进程的存在可能导致系统管理和监控工具显示误导信息。

实际应用中的注意事项

在实际编程中,确保进程正确回收是非常重要的,尤其是在涉及到大量并发子进程的应用程序中。例如,在网络服务器或多进程应用程序中,父进程需要有效管理和回收大量的子进程,以维持系统的稳定和性能。

进程回收不仅仅是资源管理的技术问题,更是系统健康的重要保障。正确的进程回收策略可以显著提高系统的稳定性和效率。

注意:一次wait或waitpid调用只能清理一个子进程,清理多个子进程应使用循环。

/*
    #include <sys/types.h>
    #include <sys/wait.h>
    pid_t wait(int *wstatus);
        功能:等待任意一个子进程结束,如果任意一个子进程结束了,此函数会回收子进程的资源。
        参数:int *wstatus
            进程退出时的状态信息,传入的是一个int类型的地址,传出参数。
        返回值:
            - 成功:返回被回收的子进程的id
            - 失败:-1 (所有的子进程都结束,调用函数失败)

    调用wait函数的进程会被挂起(阻塞),直到它的一个子进程退出或者收到一个不能被忽略的信号时才被唤醒(相当于继续往下执行)
    如果没有子进程了,函数立刻返回,返回-1;如果子进程都已经结束了,也会立即返回,返回-1.

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


int main() {

    // 有一个父进程,创建5个子进程(兄弟)
    pid_t pid;

    // 创建5个子进程
    for(int i = 0; i < 5; i++) {
        pid = fork();
        //如果是子进程,不用继续产生孙子进程
        if(pid == 0) {
            break;
        }
    }

    if(pid > 0) {
        // 父进程
        while(1) {
            printf("parent, pid = %d\n", getpid());

            // int ret = wait(NULL);
            int st;
            int ret = wait(&st);//调用wait进行资源的回收

            if(ret == -1) {
                break;
            }

            if(WIFEXITED(st)) {
                // 是不是正常退出
                printf("退出的状态码:%d\n", WEXITSTATUS(st));
            }
            if(WIFSIGNALED(st)) {
                // 是不是异常终止
                printf("被哪个信号干掉了:%d\n", WTERMSIG(st));
            }

            printf("child die, pid = %d\n", ret);

            sleep(1);
        }

    } else if (pid == 0){
        // 子进程
         while(1) {
            printf("child, pid = %d\n",getpid());    
            sleep(1);       
         }

        exit(0);
    }

    return 0; // exit(0)
}

退出信息相关宏函数

在 Unix-like 系统中,wait()waitpid() 函数在子进程终止时提供一种方式让父进程获取子进程的退出信息。当子进程终止后,这些函数可以帮助父进程获取有关子进程退出的详细信息。这些信息是通过状态码的形式提供的,父进程需要使用特定的宏来解析这个状态码。

以下是几个用于解析子进程退出状态的重要宏:

1. WIFEXITED(status)
  • 用途:检测子进程是否正常退出。
  • 返回值:如果子进程正常退出(通过 exit() 调用或从主程序返回),则返回非零值。
2. WEXITSTATUS(status)
  • 用途:如果 WIFEXITED 非零,使用这个宏可以获取子进程传递给 exit()return 的退出状态。
  • 返回值:子进程的退出代码(exit() 的参数)。
3. WIFSIGNALED(status)
  • 用途:检测子进程是否因为未捕获的信号而终止。
  • 返回值:如果子进程因为信号而终止,则返回非零值。
4. WTERMSIG(status)
  • 用途:如果 WIFSIGNALED 非零,使用这个宏可以获取导致子进程终止的信号编号。
  • 返回值:导致子进程终止的信号编号。
5. WIFSTOPPED(status)
  • 用途:检测子进程是否被信号暂停(仅适用于作业控制)。
  • 返回值:如果子进程因为信号而暂停,则返回非零值。
6. WSTOPSIG(status)
  • 用途:如果 WIFSTOPPED 非零,使用这个宏可以获取导致子进程暂停的信号编号。
  • 返回值:导致子进程暂停的信号编号。
7. WIFCONTINUED(status)
  • 用途:检测子进程是否已经由于 SIGCONT 信号恢复执行(仅在某些系统中支持)。
  • 返回值:如果子进程因 SIGCONT 信号恢复执行,则返回非零值。
示例代码

以下是一个使用这些宏的简单示例:

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

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

    if (pid == 0) {
        // 子进程
        printf("Child process\n");
        exit(5);  // 子进程退出代码为 5
    } else {
        // 父进程
        int status;
        waitpid(pid, &status, 0);  // 等待子进程结束

        if (WIFEXITED(status)) {
            printf("Child exited with status: %d\n", WEXITSTATUS(status));
        }
        if (WIFSIGNALED(status)) {
            printf("Child killed by signal: %d\n", WTERMSIG(status));
        }
    }

    return 0;
}

在这个示例中,父进程使用 waitpid() 来等待子进程终止,并使用宏来检查子进程是如何终止的。如果子进程正常退出,它将打印子进程的退出状态。这些宏是处理子进程终止情况的标准方式,能够帮助开发者编写更健壮的多进程应用程序。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

OpenHarmony语言基础类库【@ohos.util.Stack (线性容器Stack)】

ohos.util.Stack (线性容器Stack) Stack基于数组的数据结构实现&#xff0c;特点是先进后出&#xff0c;只能在一端进行数据的插入和删除。 Stack和[Queue]相比&#xff0c;Queue基于循环队列实现&#xff0c;只能在一端删除&#xff0c;另一端插入&#xff0c;而Stack都在一…

[Qt的学习日常]--信号和槽

前言 作者&#xff1a;小蜗牛向前冲 名言&#xff1a;我可以接受失败&#xff0c;但我不能接受放弃 如果觉的博主的文章还不错的话&#xff0c;还请点赞&#xff0c;收藏&#xff0c;关注&#x1f440;支持博主。如果发现有问题的地方欢迎❀大家在评论区指正 本期学习&#xff…

PyQt6 优化操作:建立侧边栏,要求可拖拽改变宽度,可用按钮控制侧边栏的展开和收起

1. 官方文档 QSplitter — PyQt Documentation v6.6.0 2. 效果展示 可拖拽改变宽度比例 点击按钮快速收起或展开侧边栏 点击按钮&#xff0c;侧边栏收起&#xff0c;同时按钮图标变为向左箭头 (对应展开功能)&#xff0c;再次点击按钮&#xff0c;侧边栏展开&#xff0c;同…

Pycharm新建工程时使用Python自带解释器的方法

Pycharm新建工程时使用Python自带解释器的方法 新建Project时最好不要新建Python解释器&#xff0c;实践证明&#xff0c;自己新建的Python解释器容易出现各种意想不到的问题。 那么怎样使用Python安装时自带的解释器呢&#xff1f; 看下面的三张截图大家就清楚了。 我的Pyth…

英智数字孪生机器人解决方案,赋能仓库物流模式全面升级

工业机械臂、仓储机器人、物流机器人等模式的机器人系统在现代产业中扮演着愈发重要的角色&#xff0c;他们的发展推动了自动化和智能化水平的提高&#xff0c;有助于为制造业、物流业、医疗保健业和服务业等行业创造新效率并提升人们的生活质量。 行业面临的挑战 机器人开发、…

Windows安装Elasticsearch 7.9.2

1 下载 https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-7.9.2-windows-x86_64.zip 2 配置 进入config目录&#xff0c;打开elasticsearch.yml文件&#xff0c;给集群和节点配置名称。 cluster.name: my-es node.name: node-1 3 启动 打开bin目录&am…

Docker之常见FAQ记录清单

一、前言 本文记录Docker使用过程中遇见的问题&#xff0c;供后续回顾参考。 关联资源&#xff1a;网络Docker博客、官方FAQ、文档、Docker 从入门到实践、中文社区、riptutorial 二、问题及处理记录 2.1、docker容器内没有vi,nano等编辑器 1&#xff09;如果宿主机本地有&a…

vs2019 - warning LNK4098 : 默认库“msvcrt.lib”与其他库的使用冲突

文章目录 vs2019 - warning LNK4098 : 默认库“msvcrt.lib”与其他库的使用冲突概述笔记实验 - 编译静态库实验 - 编译主工程&#xff0c;包含静态库实验主工程和静态库编译设置不同时的编译报错和警告备注备注 - 判断/Mdd, /MdEND vs2019 - warning LNK4098 : 默认库“msvcrt.…

[SWPUCTF-2022-新生赛]ez_sql

title:[SWPUCTF 2022 新生赛]ez_sql 审题 根据提示&#xff0c;POST传参 得到假的flag 判断类型 字符型注入 判断列数 发现空格和’or’被过滤 重新构造 nss-1/**/oorrder/**/by/**/4#发现为3个字段 采用联合注入union 爆库 发现union被过滤&#xff0c;双写union绕过 发…

以生命健康为中心的物联网旅居养老运营平台

随着科技的飞速发展和人口老龄化的日益加剧&#xff0c;养老问题逐渐成为社会关注的焦点。传统的养老模式已经难以满足现代老年人的多元化需求&#xff0c;因此&#xff0c;构建一个以生命健康为中心的物联网旅居养老运营平台显得尤为重要。 以生命健康为中心的物联网旅居养老运…

两大成果发布!“大规模量子云算力集群”和高性能芯片展示中国科技潜力

在当前的科技领域&#xff0c;量子计算的进步正日益引起全球的关注。中国在这一领域的进展尤为显著&#xff0c;今天&#xff0c;北京量子信息科学研究院&#xff08;以下简称北京量子院&#xff09;和中国科学院量子信息与量子科技创新研究院&#xff08;以下简称量子创新院&a…

【c++】深入剖析与动手实践:C++中Stack与Queue的艺术

&#x1f525;个人主页&#xff1a;Quitecoder &#x1f525;专栏&#xff1a;c笔记仓 朋友们大家好&#xff0c;本篇文章我们来到STL新的内容&#xff0c;stack和queue 目录 1. stack的介绍与使用函数介绍例题一&#xff1a;最小栈例题二&#xff1a;栈的压入、弹出队列栈的模…

Docker 的数据管理 与 Docker 镜像的创建

目录 一、Docker 的数据管理 1.1.数据卷 1.2.数据卷容器 1.3.容器互联&#xff08;使用centos镜像&#xff09; 二、Docker 镜像的创建 2.1.基于现有镜像创建 2.2.基于本地模板创建 2.3.基于Dockerfile创建 2.3.1联合文件系统&#xff08;UnionFs&#xff09; 2.3.2…

GDPU 竞赛技能实践 天码行空9

1. 埃式筛法 求区间[2, n]内所有的素数对 &#x1f496; Main.java import java.util.Scanner;public class Main {static int N (int) 1e8, cnt 0;static int[] p new int[N];static boolean[] st new boolean[N];public static void main(String[] args){Scanner sc …

使用grasshopper修改梁的起始点方向

一般北方向朝上的情况&#xff0c;梁的方向从南向北&#xff0c;从西向东。 现在使用grasshopper来判断起始点坐标&#xff0c;分辨是否错误。 交换起始点这个&#xff0c;我实在不会用电池操作&#xff0c;只好敲python代码实现了。代码如下&#xff1a; 如果会敲代码的同学…

66.网络游戏逆向分析与漏洞攻防-利用数据包构建角色信息-重新规划游戏分析信息的输出

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 如果看不懂、不知道现在做的什么&#xff0c;那就跟着做完看效果&#xff0c;代码看不懂是正常的&#xff0c;只要会抄就行&#xff0c;抄着抄着就能懂了 内容…

Apache Seata的可观测实践

title: Seata的可观测实践 keywords: [Seata、分布式事务、数据一致性、微服务、可观测] description: 本文介绍Seata在可观测领域的探索和实践 author: 刘戎-Seata 本文来自 Apache Seata官方文档&#xff0c;欢迎访问官网&#xff0c;查看更多深度文章。 Seata简介 Seata的…

STM32单片机通过ST-Link 烧录和调试

系列文章目录 STM32单片机系列专栏 C语言术语和结构总结专栏 1. ST-LINK V2 ST LINK v2下载器用于STM32单片机&#xff0c;可以下载程序、调试程序、读取芯片数据&#xff0c;解除芯片读写保护等等&#xff0c;辅助软件用的是STM32 ST-LINK Utility。 STM32 ST-LINK Utility…

电脑上的任务管理器不见了?如何把它打开?

前言 今天小白在睡觉的时候突然梦见回到了学校的电脑教室…… 相信大家都会有体验&#xff1a;每次上电脑课的时候&#xff0c;老师都会通过某些软件监控和控制学生的电脑。 想退出被控端的软件&#xff1f;没机会&#xff01;毕竟任务管理器也被禁用了&#xff0c;想整活都…

算法学习之单调栈

发射站 题目描述 某地有 N N N 个能量发射站排成一行&#xff0c;每个发射站 i i i 都有不相同的高度 H i H_i Hi​&#xff0c;并能向两边&#xff08;两端的发射站只能向一边&#xff09;同时发射能量值为 V i V_i Vi​ 的能量&#xff0c;发出的能量只被两边最近的且比…
最新文章