进程控制-操作系统

1. 进程概述

进程和程序的区别:程序和进程是两个不同的概念,他们的状态,占用的系统资源都是不同的

  • 程序:就是磁盘上的可执行文件文件, 并且只占用磁盘上的空间,是一个静态的概念。
  • 进程:被执行之后的程序叫做进程不占用磁盘空间,需要消耗系统的内存,CPU资源,每个运行的进程的都对应一个属于自己的虚拟地址空间,这是一个动态的概念。
1.1 并行和并发
  • CPU时间片

CPU时间片是多任务操作系统中,每个进程被分配的执行时间段。通过调度算法,操作系统确保每个进程有机会执行,防止某个进程长时间占用CPU。时间片的大小影响系统响应和效率,是一种权衡考虑。

  • 并发和并行
  1. 并发: 多个任务在同一时间段内交替执行,它们可能不是同时进行的,而是通过快速切换实现感觉上的同时进行。并发主要强调任务之间的相互独立和独立进展。
  2. 并行: 多个任务在同一时刻同时执行,真正的同时性。这通常涉及到物理上的多个处理单元(例如多核处理器)或多台计算机。

简而言之,并发是指多个任务交替执行,而并行是指多个任务同时执行。并发通常用于处理大量的任务,使系统更加响应,而并行则是通过同时执行多个任务来提高整体的处理速度。

1.2 PCB

PCB - 进程控制块(Processing Control Block),Linux内核的进程控制块本质上是一个叫做 task_struct的结构体。在这个结构体中记录了进程运行相关的一些信息

PCB 是进程存在的唯一标识,这意味着一个进程的存在,必然会有一个 PCB,如果进程消失了,那么 PCB 也会随之消失。

包含的信息: 进程id(pid), 进程的状态, 进程优先级, 进程对应的虚拟地址空间的信息等, PCB 的信息在进程切换时被保存和恢复,以确保操作系统能够正确地管理和调度多个进程。

1.3 进程状态

进程一共有五种状态分别为:**创建态,就绪态,运行态,阻塞态(挂起态),退出态(终止态)**其中创建态和退出态维持的时间是非常短的,稍纵即逝。

  1. 就绪状态(Ready): 进程已经准备好执行,等待分配CPU时间。通常,进程在等待操作系统的调度时处于就绪状态。
  2. 运行状态(Running): 进程正在CPU上执行指令。在任何给定时刻,只能有一个进程处于运行状态。
  3. 阻塞状态(Blocked): 进程被阻塞,无法执行。这可能是因为等待某个事件的发生,例如等待输入/输出完成、等待资源的释放等。在阻塞状态中的进程会等待操作系统的通知,以便重新进入就绪状态。

在这里插入图片描述

1.4 进程命令
  1. ps: 显示当前进程的快照。

    ps aux     # 显示所有用户的所有进程
    ps -ef     # 显示所有进程的详细信息
    
  2. top: 实时显示系统中正在运行的进程的信息,以及系统的整体性能。

    top
    
  3. kill: 终止一个进程。

    kill PID    # 使用进程ID(PID)终止进程
    

在这里插入图片描述

9号信号(SIGKILL)的行为是无条件杀死进程,想要杀死哪个进程就可以把这个信号发送给这个进程,操作如下:

# 无条件杀死进程, 进程ID通过 ps aux 可以查看
$ kill -9 进程ID
$ kill -SIGKILL 进程ID

2. 进程创建

2.1 函数

Linux中进程ID为 pid_t 类型,其本质是一个正整数

  • 获取当前进程的进程ID(PID)
#include <sys/types.h>
#include <unistd.h>
pid_t getpid(void);
  • 获取当前父进程的进程ID(PPID)
#include <sys/types.h>
#include <unistd.h>
pid_t getppid(void);
  • 创建一个新的进程
#include <unistd.h>
pid_t fork(void);
2.2 fork()函数

fork 函数是在Unix/Linux操作系统中用于创建新进程的系统调用之一。调用 fork 会创建一个与调用进程几乎完全相同的新进程,这两个进程将在不同的内存空间中运行。

基本语法:

#include <unistd.h>

pid_t fork(void);
  • 返回值:
    • 在父进程中,fork 返回新创建子进程的进程ID(PID)。
    • 在子进程中,fork 返回0。
    • 如果出现错误,返回-1。

工作原理:

  • fork 被调用时,它会复制调用进程的内存和资源(文件描述符、环境变量等)。
  • 在父进程和子进程中,fork 返回不同的值,这样程序可以根据返回值来确定自己是父进程还是子进程。
  • 子进程是父进程的副本,它从 fork 返回的位置开始执行。

示例:

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

int main() {
    pid_t pid;

    pid = fork();

    if (pid == -1) {
        // 错误处理
        perror("fork");
        return 1;
    } else if (pid > 0) {
        // 父进程
        printf("Parent process, child PID = %d\n", pid);
    } else {
        // 子进程
        printf("Child process\n");
    }

    // 父子进程都会执行到这里
    printf("This is common code for both parent and child\n");

    return 0;
}

这段代码调用 fork 创建一个新的进程。父进程得到子进程的PID,而子进程得到0。接下来,父子进程都会执行相同的代码,但可以通过 if (pid > 0) 判断来执行不同的逻辑。

需要注意的是,fork 创建的新进程是父进程的副本,但并不共享父进程和子进程之间的变量。如果在父子进程中都修改同一个变量,它们互不影响。

3. 父子进程

3.1 子进程执行位置

父进程肯定是从main()函数开始运行的,子进程是在父进程中调用fork()函数之后被创建, 子进程就从fork()之后开始向下执行代码。

在这里插入图片描述

3.2 循环创建子进程

我们可以只让父进程创建子进程,如果是子进程不让其继续创建子进程,因此只需要在程序中添加关于父子进程的判断即可。

// 需要在上边的程序中控制不让子进程, 再创建子进程即可
// process_loop.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

int main()
{
    pid_t pid;
    // 在循环中创建子进程
    for(int i=0; i<3; ++i)
    {
        pid = fork();
        if(pid == 0)
        {
            // 不让子进程执行循环, 直接跳出
            break;
        }
    }
    printf("当前进程pid: %d\n", getpid());

    return 0;
}
3.3 终端显示问题

执行上述代码后会出现以下显示问题, 回车后即可恢复

在这里插入图片描述

  1. a.out 进程启动之后,共创建了3个子进程,其实 a.out 也是有父进程的就是当前的终端
  2. 终端只能检测到 a.out 进程的状态,a.out执行期间终端切换到后台,a.out执行完毕之后终端切换回前台
  3. 当终端切换到前之后,a.out的子进程还没有执行完毕,当子进程输出的信息就显示到终端命令提示符的后边了,导致终端显示有问题,但是此时终端是可以接收键盘输入的,只是看起来不美观而已。
  4. 想要解决这个问题,需要让所有子进程退出之后再退出父进程,比如:在父进程代码中调用 sleep()

修改后的代码:

在这里插入图片描述

执行结果:

在这里插入图片描述

3.4 进程数数

当父进程创建一个子进程,那么父子进程之间可以通过全局变量互动,实现交替数数的功能吗?不过不确定可以写一段测试代码:

// number.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

// 定义全局变量
int number = 10;

int main()
{
    printf("创建子进程之前 number = %d\n", number);

    pid_t pid = fork();
    // 父子进程都会执行这一行
    printf("当前进程fork()的返回值: %d\n", pid);

    //如果是父进程
    if(pid > 0)
    {
        printf("我是父进程, pid = %d, number = %d\n", getpid(), ++number);
        printf("父进程的父进程(终端进程), pid = %d\n", getppid());
        sleep(1);
    }
    else if(pid == 0)
    {
        // 子进程
        number += 100;
        printf("我是子进程, pid = %d, number = %d\n", getpid(), number);
        printf("子进程的父进程, pid = %d\n", getppid());
    }

    return 0;
}

结果:

在这里插入图片描述

两个进程中是不能通过全局变量实现数据交互的,因为每个进程都有自己的地址空间,两个同名全局变量存储在不同的虚拟地址空间中,二者没有任何关联性。如果要进行进程间通信需要使用:管道,共享内存,本地套接字,内存映射区,消息队列等方式。

4. exexl 和 execlp函数

在项目开发过程中,有时候有这种需求,需要通过现在运行的进程启动磁盘上的另一个可执行程序,也就是通过一个进程启动另一个进程,这种情况下我们可以使用 exec族函数

也就是说 exec族函数并没有创建新进程的能力,只是有大无畏的牺牲精神,让起启动的新进程寄生到自己虚拟地址空间之内,并挖空了自己的地址空间用户区,把新启动的进程数据填充进去。

4.1 execl()
#include <unistd.h>
// 变参函数
int execl(const char *path, const char *arg, ...);

参数:

  • path: 要启动的可执行程序的路径, 推荐使用绝对路径
  • arg: ps aux 查看进程的时候, 启动的进程的名字, 可以随意指定, 一般和要启动的可执行程序名相同
  • … : 要执行的命令需要的参数,可以写多个,最后以 NULL 结尾,表示参数指定完了。

返回值:如果这个函数执行成功, 没有返回值,如果执行失败, 返回 -1

4.2 execlp()

该函数常用于执行已经设置了环境变量的可执行程序, 因此使用这个函数执行可执行程序不需要指定路径,只需要指定出名字即可。

// p == path
int execlp(const char *file, const char *arg, ...);

参数:

  • file: 可执行程序的名字
    • 在环境变量PATH中,可执行程序可以不加路径
    • 没有在环境变量PATH中, 可执行程序需要指定绝对路径
  • arg: ps aux 查看进程的时候, 启动的进程的名字, 可以随意指定, 一般和要启动的可执行程序名相同
  • … : 要执行的命令需要的参数,可以写多个,最后以 NULL 结尾,表示参数指定完了。

返回值:如果这个函数执行成功, 没有返回值,如果执行失败, 返回 -1

4.3 函数的使用

一般不会在进程中直接调用这个函数, 因为这样的话这个进程的代码区代码会被替换, 我们一般在调用这些函数的时候都会先创建一个子进程,在子进程中调用 exec 族函数,子进程的用户区数据被替换掉开始执行新的程序中的代码逻辑,但是父进程不受任何影响仍然可以继续正常工作。

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

int main()
{
    // 创建子进程
    pid_t pid = fork();
    // 在子进程中执行磁盘上的可执行程序
    if(pid == 0)
    {
        // 磁盘上的可执行程序 /bin/ps
#if 1
        execl("/bin/ps", "title", "aux", NULL);
        // 也可以这么写
        // execl("/bin/ps", "title", "a", "u", "x", NULL);
#else
        execlp("ps", "title", "aux", NULL);
        // 也可以这么写
        // execl("ps", "title", "a", "u", "x", NULL);
#endif
        // 如果成功当前子进程的代码区别 ps中的代码区代码替换
        // 下面的所有代码都不会执行
        // 如果函数调用失败了,才会继续执行下面的代码
        perror("execl");
        printf("++++++++++++++++++++++++\n");
        printf("++++++++++++++++++++++++\n");
        printf("++++++++++++++++++++++++\n");
        printf("++++++++++++++++++++++++\n");
        printf("++++++++++++++++++++++++\n");
        printf("++++++++++++++++++++++++\n");
    }
    else if(pid > 0)
    {
        printf("我是父进程.....\n");
    }

    return 0;
}

在这里插入图片描述

5. 进程控制

进程控制主要是指进程的退出, 进程的回收和进程的特殊状态 孤儿进程和僵尸进程。

5.1 结束进程

如果想要直接退出某个进程可以在程序的任何位置调用exit()或者_exit()函数。函数的参数相当于退出码

exit 函数是用于终止程序的标准库函数,它可在C和C++中使用。当程序执行到 exit 函数时,它会正常退出,并返回一个状态码给操作系统

基本语法:

#include <stdlib.h>

void exit(int status);
  • status:指定要返回给操作系统的状态码。通常,0 表示正常退出,非零值表示异常退出或错误状态。

示例:

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

int main() {
    printf("Before exit function\n");

    // 使用 exit 函数退出程序,返回状态码 0
    exit(0);

    // 以下代码不会执行
    printf("After exit function\n");

    return 0;
}

在这个例子中,当程序执行到 exit(0) 时,程序将立即终止,不会执行后面的代码。此时,操作系统将收到状态码 0,表示程序正常退出。

exit 函数的主要作用是确保程序在退出时执行一些清理工作,例如关闭文件、释放动态分配的内存等。如果不使用 exit 而直接让 main 函数返回,那么可能会导致一些清理工作无法完成。

5.2 孤儿进程

在操作系统中,**孤儿进程是指其父进程先于它自己退出,导致它成为孤立的进程。**当一个进程创建子进程,而子进程的父进程在子进程退出之前就已经终止,这个子进程就会变成孤儿进程。

孤儿进程通常会被 init 进程(在现代系统中可能是 systemd 或其他类似的进程)接管。init 进程(PID= 1)会定期检查系统中是否存在孤儿进程,并负责清理这些进程的资源,防止它们变成僵尸进程。

以下是孤儿进程的主要特征和处理方式:

  1. 没有父进程: 孤儿进程的父进程已经退出,因此它没有父进程。
  2. 接管: 通常由 init 进程或类似的进程接管。init 进程会通过 wait 或类似的机制来收集孤儿进程的退出状态,确保它们正常终止。
  3. 不影响系统正常运行: 孤儿进程并不会影响系统的正常运行,但它们可能会占用系统资源,因此需要及时被清理。

下面是一个产生孤儿进程的简单示例,使用 fork 创建子进程,并在子进程中让它先于父进程退出:

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

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

    if (pid == -1) {
        perror("fork");
        return 1;
    } else if (pid > 0) {
        // 父进程
        printf("Parent process (PID=%d) is sleeping...\n", getpid());
        sleep(2);
        printf("Parent process exits.\n");
    } else {
        // 子进程
        printf("Child process (PID=%d) exits.\n", getpid());
        exit(0);
    }

    return 0;
}

在这个例子中,子进程在创建后立即退出,而父进程会休眠一段时间后退出。因此,子进程成为了一个孤儿进程。

5.3 僵尸进程

在一个启动的进程中创建子进程,这时候就有了父子两个进程,父进程正常运行, 子进程先与父进程结束, 子进程无法释放自己的PCB资源, 需要父进程来做这个件事儿, 但是如果父进程也不管, 这时候子进程就变成了僵尸进程。

僵尸进程不能将它看成是一个正常的进程,这个进程已经死亡了,用户区资源已经被释放了,只是还占用着一些内核资源(PCB)

要处理僵尸进程,父进程通常需要调用 waitwaitpid 等系统调用,以获取子进程的终止状态。一旦父进程处理了子进程的终止状态,该子进程的资源就会被释放,它不再是僵尸进程。

以下是一个简单的示例,演示了一个父进程创建子进程,然后父进程休眠一段时间,导致子进程成为僵尸进程:

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

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

    if (pid == -1) {
        perror("fork");
        return 1;
    } else if (pid > 0) {
        // 父进程
        printf("Parent process (PID=%d) is sleeping...\n", getpid());
        sleep(5); // 父进程休眠一段时间,让子进程成为僵尸进程
        printf("Parent process exits.\n");
    } else {
        // 子进程
        printf("Child process (PID=%d) exits.\n", getpid());
        exit(0);
    }

    return 0;
}

消灭僵尸进程的方法是,杀死这个僵尸进程的父进程,这样僵尸进程的资源就被系统回收了。通过kill -9 僵尸进程PID的方式是不能消灭僵尸进程的,这个命令只对活着的进程有效,僵尸进程已经死了

5.4 进程回收

在父进程中进行子进程的资源回收,回收方式有两种,一种是阻塞方式wait(),一种是非阻塞方式waitpid()。

5.4.1 wait

这是个阻塞函数,**如果没有子进程退出, 函数会一直阻塞等待, 当检测到子进程退出了, 该函数阻塞解除回收子进程资源。font>**这个函数被调用一次, 只能回收一个子进程的资源,如果有多个子进程需要资源回收, 函数需要被调用多次。

基本语法:

#include <sys/types.h>
#include <sys/wait.h>

pid_t wait(int *status);

status:用于存储子进程终止状态的指针。可以为NULL,表示不关心子进程的终止状态。

在这里插入图片描述

返回值:如果成功,返回被等待子进程的进程ID(PID)。如果调用出错,返回-1,并设置 errno 表示错误类型。

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

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

    if (pid == -1) {
        perror("fork");
        return 1;
    } else if (pid > 0) {
        // 父进程
        printf("Parent process (PID=%d) is waiting for the child process...\n", getpid());

        int status;
        pid_t terminated_child = wait(&status);

        if (terminated_child == -1) {
            perror("wait");
            return 1;
        }

        if (WIFEXITED(status)) {
            printf("Child process (PID=%d) exited with status %d.\n", terminated_child, WEXITSTATUS(status));
        } else if (WIFSIGNALED(status)) {
            printf("Child process (PID=%d) terminated by signal %d.\n", terminated_child, WTERMSIG(status));
        }

        printf("Parent process exits.\n");
    } else {
        // 子进程
        printf("Child process (PID=%d) exits.\n", getpid()); 
        exit(0); // 子进程退出后, 上面的wait函数解除阻塞
    }

    return 0;
}

在这里插入图片描述

5.4.2 waitpid

waitpid 函数是用于等待指定子进程终止并获取其终止状态的系统调用。与 wait 不同,waitpid 允许指定等待的子进程,从而可以避免等待所有子进程的终止

#include <sys/types.h>
#include <sys/wait.h>

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

参数:

  • pid:

    • -1:回收所有的子进程资源, 和wait()是一样的, 无差别回收,并不是一次性就可以回收多个, 也是需要循环回收的
    • 大于0:指定回收某一个进程的资源 ,pid是要回收的子进程的进程ID
    • 0:回收当前进程组的所有子进程ID
    • 小于 -1:pid 的绝对值代表进程组ID,表示要回收这个进程组的所有子进程资源
  • status: NULL, 和wait的参数是一样的

  • options: 控制函数是阻塞还是非阻塞

    • 0: 函数是行为是阻塞的 ==> 和wait一样
    • WNOHANG: 函数是行为是非阻塞的

返回值:

  • 如果函数是非阻塞的, 并且子进程还在运行, 返回0
  • 成功: 得到子进程的进程ID
  • 失败: -1
    • 没有子进程资源可以回收了, 函数如果是阻塞的, 阻塞会解除, 直接返回-1
    • 回收子进程资源的时候出现了异常

阻塞回收的例子和wait基本一样, 举一个非阻塞回收的例子:

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

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

    if (pid == -1) {
        perror("fork");
        return 1;
    } else if (pid > 0) {
        // 父进程
        printf("Parent process (PID=%d) is waiting for the child process...\n", getpid());

        int status;
        int options = WNOHANG;  // 使用 WNOHANG 选项,非阻塞等待
        pid_t terminated_child;

        do {
            terminated_child = waitpid(pid, &status, options);
            
            if (terminated_child == -1) {
                perror("waitpid");
                return 1;
            }

            if (terminated_child == 0) {
                // 子进程还没有终止
                printf("No child process has terminated yet. Sleeping for a while...\n");
                sleep(1);
            }

        } while (terminated_child == 0);

        if (WIFEXITED(status)) {
            printf("Child process (PID=%d) exited with status %d.\n", terminated_child, WEXITSTATUS(status));
        } else if (WIFSIGNALED(status)) {
            printf("Child process (PID=%d) terminated by signal %d.\n", terminated_child, WTERMSIG(status));
        }

        printf("Parent process exits.\n");
    } else {
        // 子进程
        printf("Child process (PID=%d) exits.\n", getpid());
        exit(0);
    }

    return 0;
}

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

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

相关文章

202402读书笔记|《当你老了》——灰蒙曙光比爱情温柔,清晨露珠比希望更可爱

202402读书笔记|《当你老了》——灰蒙曙光比爱情温柔&#xff0c;清晨露珠比希望更可爱 《当你老了》作者叶芝&#xff0c;断断续续碎片时间读完的一本书&#xff0c;不是很惊艳&#xff0c;但值得一读。就因为很喜欢当你老了&#xff0c;所以拾起的这本书。读完知道了原来叶芝…

web网站,可当期末作业,随机一言,天气,时钟,音乐等综合网站

文章目录 主页面时间胶囊底部条文心一言音乐播放器天气时钟禁用右键其他每次刷新页面会随机更换壁纸新春版 主页面 时间胶囊 底部条 文心一言 点击可随机变化文心一言 音乐播放器 天气时钟 禁用右键 其他 每次刷新页面会随机更换壁纸 新春版

linux sh 脚本文件换行错误

windows 写好的脚本到服务运行不起来&#xff0c;显示换行问题 因为 windwos 的换行和 linux 的换行风格不同 解决办法&#xff1a;在使用的文本编辑器中&#xff0c;修改格式为 unix 格式 以 notepad 为例&#xff0c;在编辑 -> 文档格式转换中设置格式为 Unix

使用.Net nanoFramework获取ESP32板载按键的点击事件

本文以 ESP32-S3-Zero 板载的按键为例&#xff0c;介绍了GPIO的使用方法&#xff0c;以及如何获取按键的点击事件。板载按钮作为自带的天然用户按钮&#xff0c;除了其本身的功能外&#xff0c;也可以作为某些应用场景下的简单的交互方式。 1. 引言 对于一般的产品来说&#x…

阿里通义千问「全民舞王」,一张照片就能跳《科目三》,刷爆朋友圈

这两天看朋友圈、网上都在发这种跳舞的视频。只要上传一张全身照&#xff0c;就可以生成各种跳舞的视频。 比如前段时间火爆海底捞的《科目三》&#xff0c;还有《DJ慢摇》、《鬼步舞》、《兔子舞》、甚至还有咱《秧歌舞》。 先来一睹为快&#xff01; 阿里通义千问「全民舞王…

Numpy和Pandas知识点总结

1.python常见的开源库介绍 1.1numpy 一个运行速度非常快的数学库&#xff0c;主要用于数组计算 1.2pandas 一个强大的“分析结构化数据”的工具集&#xff0c;底层依赖numpy 用于数据挖掘和数据分析&#xff0c;同时也提供数据清洗功能 pandas主要有两种数据结构&#xf…

VMware--安装CentOS系统

在虚拟机安装CentOS系统 1 下载CentOS镜像 方式一&#xff1a;可以到官网下载&#xff0c;下载速度较慢。 https://vault.centos.org/7.6.1810/isos/x86_64/ &#xff08;最后的 / 不要漏掉&#xff09; 方式二&#xff1a;可以到国内的镜像网站下载。 阿里开源镜像站&…

『年度总结』逐梦编程之始:我的2023学习回顾与展望

目录 前言 我与Python 我与C语言 第一篇正式博客&#xff1a; 第二篇正式博客&#xff08;扫雷&#xff09;&#xff1a; 指针学习笔记: C语言学习笔记&#xff1a; 我与数据结构&#xff1a; yuan 这篇博客&#xff0c;我将回顾2023年编程之旅的起点&#xff0c;同时展…

Unity | NGO网络框架

目录 一、相关属性及变量 1.ServerRpc属性 2.ClientRpc属性 3.NetworkVariable变量 二、相关组件 1.NetworkManager 2.Unity Transport 3.Network Object 4.NetworkBehaviour&#xff1a; 5.NetworkTransform Syncing(Synchronizing) Thresholds Interpolation 三…

原生微信小程序AR(扫描指定图片显示glb模型)

效果 ar案例视频 准备&#xff1a;需要准备要扫描的图片地址和扫描成功后显示的模型 1.在components创建组件 index.js文件代码 Component({properties: {title: {type: String,value: ,},intro: {type: String,value: ,},hint: {type: String,value: ,},code: {type: String…

引导过程和服务

宏内核(monolithic kernel)&#xff1a;又称单内核和强内核&#xff0c;Unix&#xff0c;Linux把所有系统服务都放到内核里&#xff0c;所有功能集成于同一个程序&#xff0c;分层实现不同功能&#xff0c;系统庞大复杂&#xff0c;Linux其实在单内核内核实现了模块化&#xff…

汽车电子学习总结

国内的主要有比亚迪、联合汽车电子&#xff08;联电&#xff09;、麦格米特、上海电驱动&#xff1b;国外的主要有欧美系的博世、麦格纳、大陆、博格华纳&#xff1b;日系的电装、电产等公司。

【Android Studio】在单独的窗口中启动模拟器

参考&#xff1a;https://developer.android.com/studio/run/emulator-launch-separate-window?hlzh-cn 默认情况下&#xff0c;Android 模拟器会在 Android Studio 中运行。这样&#xff0c;您就可以高效地使用屏幕空间&#xff0c;使用热键在模拟器和编辑器窗口之间快速导航…

华为交换机基于mac地址划分VLAN

SW1配置 vlan 10mac-vlan mac-address 5489-98c3-5611 #pc1 mac地址 mac-vlan mac-address 5489-98c3-5622 #pc2 mac地址 interface GigabitEthernet0/0/1undo port hybrid vlan 1 #禁用交换机默认的vlan 1&#xff0c;避免产生干扰port hybrid untagged vlan 10mac-vlan enab…

【Java】RuoYi-Vue-Plus 多数据源整合TDengine时序数据库——服务端自动建库建表

目录 环境准备整合TDengine 数据源1. 添加驱动依赖2. 添加数据源配置3. 添加Mapper4. 添加建表sql脚本5. Controller 测试效果 环境准备 RuoYi-Vue-Plus v5.1.2JDK17Maven 3.6.3Redis 5.XMySQL 5.7TDengine 2.6.0.34 客户端 整合TDengine 数据源 1. 添加驱动依赖 注意&…

【AWS系列】巧用 G5g 畅游Android流媒体游戏

序言 Amazon EC2 G5g 实例由 AWS Graviton2 处理器提供支持&#xff0c;并配备 NVIDIA T4G Tensor Core GPU&#xff0c;可为 Android 游戏流媒体等图形工作负载提供 Amazon EC2 中最佳的性价比。它们是第一个具有 GPU 加速功能的基于 Arm 的实例。 借助 G5g 实例&#xff0c;游…

亚信安慧AntDB数据库:企业核心业务系统数据库升级改造的可靠之选

在近期召开的“2023年国有企业应用场景发布会”上&#xff0c;亚信安慧公司的核心数据库产品AntDB闪耀登场&#xff0c;技术总监北陌先生针对企业核心业务系统数据库升级改造的关键议题发表了深度分享。他从研发、工程实施和运维管理三个维度细致剖析了当前企业在进行数据库升级…

OpenCASCADE MFC例子

OpenCASCADE MFC例子 说明 一直对OpenCASCADE一直都比较感兴趣&#xff0c;这个例子是我参考这位大神C幼儿园中班小朋友的专栏做出来的OpenCASCADE_C幼儿园中班小朋友的博客-CSDN博客 不过我用的是vcpkg的方式安装OpenCASCADE&#xff0c;这个需要注意一下&#xff0c;可能需…

智慧机房建设浪潮:2024年动环监控系统厂家排名出炉

近几年来&#xff0c;伴随着信息化技术的快速发展&#xff0c;老旧的传统机房在设备性能、网络安全、数据统计等各方面都已经不再能完全满足使用需求&#xff0c;国内多个机房兴起了轰轰烈烈的智慧机房建设浪潮。不同于传统机房只能依赖人工24小时值守&#xff0c;智慧机房凭借…

数据库之存储引擎

1. 存储引擎的概念 存储引擎是MYSQL数据库的组件&#xff0c;负责执行时间的数据I/O操作&#xff08;数据的存储和提取&#xff09;&#xff0c;工作在文件系统之上&#xff0c;数据库的数据会先传到存储引擎&#xff0c;再按照存储引擎的存储格式保存到文件系统。 &#xff…