【Linux】进程间通信:详解 VSCode使用 | 匿名管道

news2024/9/25 11:20:52

目录

0. 引入:vscode 的使用

下载

推荐插件

连接云服务器

1. 进程间通信

1.1 是什么

1.2 为什么

1.3 怎么办

介绍 :

2. 匿名管道

2.1 引入

2.2 原理

3. 建立管道的系统调用pipe

3.1 介绍

形参

返回值

3.2 代码

3.3 站在内核的角度

编码实现


0. 引入:vscode 的使用

下载

官⽹下载地址:https://code.visualstudio.com/Download

下载太慢,推荐下载链接:

https://vscode.cdn.azure.cn/stable/30d9c6cd9483b2cc586687151bcbcd635f373630/VSCodeUser

然后一直 next 安装即可

推荐插件

安装完成后在如下地方,继续安装插件即可

  1. Remote - SSH - 远程登录Linux
  2. C/C++ - 必装
  3. C/C++ Extension Pack - C/C++扩展包,下载直接安装,它包含了 vscode 编写 C/C++ ⼯程需要的插件(C/C++、C/C++ Themes、CMake、CMake Tools和Better C++ Syntax等),和以前⽐不需要⼀个个找了。
  4. C/C++ Themes - 主题设置,插件⾥⾯可以点击设置
  5. Chinese (Simplified) (简体中⽂)
  6. vscode-icons - 改变编辑器⾥⾯的⽂件图标
  7. filesize - 左下⻆显⽰源⽂件⼤⼩的插件
  8. Include AutoComplete - ⾃动头⽂件包含
  9. GBKtoUTF8 - ⾃动将 GBK 转换为 UTF8

连接云服务器

安装如下软件后,对照 README 操作,输入主机号即可

可以在我的电脑的如下路径下查看 config ,检查连接

记事本查看

连接完成后,打开需要的文件夹即可

使用小tips:

  • ctrl ~ :打开终端
  • ctrl s : 保存文件
  • ctrl/ :可以实现注释

1. 进程间通信

1.1 是什么

  • 数据传输:一个进程需要将它的数据发送给另一个进程。
  • 资源共享:多个进程之间共享同样的资源。
  • 通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程)。
  • 进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变。

1.2 为什么

为了实现两个或者多个进程实现数据层面的交互,因为进程独立性的存在,导致进程通信的成本比较

通信可以用于:1. 基本数据 2. 发送命令 3. 某种协调 4. 通知 ...很多场景下需要多个进程协同工作来完成要求。例如

两个进程cat和grep协同工作,将log.txt文件中带有hello的文字显示出来。

1.3 怎么办

当前主要是通过三种策略来实现进程间通信的:

管道:通过文件系统通信

  • 匿名管道
  • 命名管道

System Ⅴ:聚焦在本地通信

  • 共享内存
  • 消息队列
  • 信号量

POSIX:让通信可以跨主机

  • 共享内存
  • 消息队列
  • 信号量
  • 互斥量
  • 条件变量
  • 读写锁

每一种策略下都有很多种通信方式,在这篇文章中将详细讲解管道策略的通信方式。

介绍 :

a. 进程间通信的本质:必须让不同的进程看到同一份“资源

b. 资源?特定形式的内存空间

c. 这个资源谁提供?一般是操作系统

为什么不是我们两个进程中的一个呢?假设一个进程提供,这个资源属于谁?

这个进程独有,破坏进程独立性,所以要借用第三方空间

d. 我们进程访问这个空间,进行通信,本质就是访问操作系统!

进程代表的就是用户,资源从创建,使用(一般),释放--系统调用接口

从底层设计,从接口设计,都要由操作系统独立设计,一般操作系统,会有一个独立的通信模块--隶属于文件系统--IPC 通信模块

定制标准 -- 进程间通信是有标准的 -- system V && posix

e. 基于文件级别的通信方式 --管道

2. 匿名管道

2.1 引入

父子间通信仍然正常进行,并且效率还非常的高,而且还没有影响进程的独立性。而这种不进行IO(不刷到磁盘),就是内存级文件

这种由文件系统提供公共资源的进程间通信,就叫做管道

2.2 原理

匿名管道:没有名字的文件(struct file)。

匿名管道用于父子间通信,或者由一个父创建的兄弟进程(必须有“血缘“)之间进行通信

现在我们知道了匿名管道就是没有名字的文件,通过管道进行通信时,只需要通信双方打开同一个文件就可以。

我们通过系统调用open打开文件的时候,会指定打开方式,是读还是写。

  • 当父进程以写方式打开一个文件的时候,创建的子进程会继承父进程的一切
  • 此时子进程以写的方式打开的这个文件。

既然是通信,势必有一方在写,一方在读,而现在父子双方都是以写的方式打开,它们怎么进行通信呢?

父进程以读和写的方式打开同一份文件两次。可以理解为管道就是文件,有缓冲区,由父子进程引入管道文件

父进程拷贝创建出子进程,PCB 是肯定要的,files_struct 指向同一文件

父子进程公用文件实现了,那么对于读写的控制是怎么实现的呢?

  • 为了防止父进程对管道进行误读,以及子进程对管道进行误写,破坏通信规则。
  • 将父进程的读端关闭,将子进程的写端关闭,使用系统调用close(fd)。

只想让父子进程进行单向通信!所以称为管道通信。把文件这套拿过来,用就是为了简单

我们想让子进程进行写入,父进程进行读取

细节图

如果我要进行双向通信呢?多个管道

如果文件没有任何关系,可以用我们刚刚讲的原理进行通信呢?

不能,必须是父子关系/兄弟关系/爷孙关系...进程之间需要有血缘关系,常用于父子,才能继承一套 file_struct。制作如下动图

sum: 至此,通信了吗??没有。建立通信信道--为什么这么费劲?--因为进程具有独立性,要操作系统忙活才能建立出信道,通信是有成本的!

3. 建立管道的系统调用pipe

3.1 介绍

上面都是理论上的,具体到代码中是如何建立管道的呢?既然是操作系统中的文件系统提供的公共资源,当然是用系统调用来建立管道了。

man 2 pipe

形参
  • int pipefd[2] 是一个输出型参数,表示一个数组,该数组有两个元素,下标分别为0和1。
    • 下标为0的元素表示管道读端的文件描述符 fd。(0像张嘴读书)
    • 下标为1的元素表示管道写端的文件描述符 fd。(1像拿笔写字)

通过系统调用 pipe,可以直接获得这两个文件描述符,并将其添加到父进程的文件描述符表中,无需两次打开内存级别的文件。

返回值
  • 返回值为 int 类型的整数,用来反馈管道创建的情况。
    • 返回0表示管道创建成功。
    • 返回-1表示管道创建失败,并且会将错误码自动写入 errno 中。

那么,父进程创建管道后,得到的两个文件描述符是多少呢?是3和4吗?我们可以通过代码来验证这一点。

输出型参数:文件的描述符数字带出来,让用户使用-->3,4,因为0,1,2分别被stdin,stdout,stderr占用。

3.2 代码

下面代码示例展示了如何通过管道(pipe)在父子进程之间进行通信。为了更好地理解代码,我们将其拆分开来并逐段解释每部分的含义。

#include <unistd.h>    // 包含pipe, fork, waitpid, write, read等函数的头文件
#include <sys/types.h> // 包含pid_t类型的头文件
#include <string>      // 包含std::string类的头文件
#include <iostream>    // 包含std::cout, std::endl等输入输出流的头文件
#include <cstdlib>     // 包含exit函数和一些其他通用函数的头文件

这些头文件提供了程序中使用的函数和类型的声明。

const int NUM = 100; // 定义缓冲区大小
const int N = 2;     // 定义管道数组的大小

这两行代码定义了一些常量,NUM 是缓冲区的大小,N 是管道数组的大小。

void Writer(int wfd) {
    std::string s = "hello, I am child";
    pid_t self = getpid();
    int number = 0;

    char buffer[NUM]; // 定义一个缓冲区

    while (true) {
        sleep(1);

        // 构建发送字符串
        snprintf(buffer, sizeof(buffer), "%s-%d-%d", s.c_str(), self, number++);
        // 发送/写入给父进程, system call
        write(wfd, buffer, strlen(buffer) + 1); // 注意这里需要加1来包括'\0'

        std::cout << number << std::endl;

        if (number >= 5) break;
    }
}

这个函数Writer用于子进程向父进程写入数据。wfd是写端文件描述符,s是发送的字符串,self是子进程ID,number是一个计数器。每次循环中,构建一个包含字符串、子进程ID和计数器的消息,然后写入管道。每写一次,计数器加一,循环5次后退出。

void Reader(int rfd) {
    char buffer[NUM];

    while (true) {
        buffer[0] = 0;
        ssize_t n = read(rfd, buffer, sizeof(buffer));
        if (n > 0) {
            buffer[n] = 0; // 终止字符串
            std::cout << "father get a message[" << getpid() << "]# " << buffer << std::endl;
        } else if (n == 0) {
            std::cout << "father read file done!" << std::endl;
            break;
        } else {
            std::perror("Error reading from pipe"); // 打印错误信息
            break;
        }
    }
}

这个函数Reader用于父进程从管道读取数据。rfd是读端文件描述符。每次循环中,尝试从管道读取数据,如果成功读取到数据,将其打印出来。如果读取到文件结尾或发生错误,则退出循环。

主函数中:

  1. 创建一个管道,pipefd数组包含两个文件描述符,一个用于读,一个用于写。
  2. 调用fork()创建子进程。
  3. 如果fork()失败,打印错误信息并退出。
  4. 如果是子进程,关闭读端,调用Writer函数写数据,写完后关闭写端并退出。
  5. 如果是父进程,关闭写端,调用Reader函数读数据,等待子进程结束,关闭读端。
  6. 最后,打印程序结束的信息并返回。
int main() {
    int pipefd[N] = {0}; // 管道文件描述符
    int n = pipe(pipefd);
    if (n < 0) {
        std::perror("Error creating pipe");
        return 1;
    }

    pid_t id = fork(); // 创建子进程
    if (id < 0) {
        std::perror("Error forking");
        return 2;
    }
    if (id == 0) { // 子进程
        close(pipefd[0]); // 关闭读端

        Writer(pipefd[1]); // 写入数据

        close(pipefd[1]); // 关闭写端
        exit(0);
    } else { // 父进程
        close(pipefd[1]); // 关闭写端

        Reader(pipefd[0]); // 读取数据

        pid_t rid = waitpid(id, nullptr, 0);//回收子进程
        if (rid < 0) {
            std::perror("Error waiting for child process");
            return 3;
        }

        close(pipefd[0]); // 关闭读端
    }

    std::cout << "Program finished." << std::endl;
    return 0;
}

回忆点:

  1. return 2 的设置 : 如果 fork() 失败,程序将返回 2。这里的数字 2 是一个约定的返回值,用于表示 fork() 调用失败。通常情况下,不同的错误情况会使用不同的返回值来区分。
  2. 子进程关闭管道的写端 (pipefd[1]) 并随后调用 exit(0) 是为了:确保子进程只执行写操作,一旦写操作完成,子进程就退出
  3. pid_t rid = waitpid(id, nullptr, 0) :使用 waitpid 函数来等待子进程的结束,结束后父进程一定要实现资源回收
  • waitpid 是一个系统调用,用于等待指定子进程的终止状态。
  • 参数 id 是子进程的 PID(进程标识符)。
  • 参数 nullptr 表示不关心子进程的状态信息。
  • 参数 0 表示默认的行为,即等待任何子进程的终止。

运行:

批注:

  1. buffer[0] = 0; - 清空字符串,标志数组 buffer 作为字符串使用。
  2. buffer[n] = 0; --终止字符串,进行打印
  3. snprintf(buffer, sizeof(buffer), "%s-%d-%d", s.c_str(), self, number++); - 使用安全的格式化函数,防止缓冲区溢出。

snprintf 参数设计简洁说明:

  • buffer:目标字符数组,用于存储格式化后的字符串。
  • sizeof(buffer):指定缓冲区大小,防止溢出。
  • "%s-%d-%d":定义字符串格式,包含字符串、两个整数和一个分隔符。
  • s.c_str():提供要插入的字符串。
  • self:插入的第一个整数。
  • number++:使用并递增的第二个整数。

完整代码:

#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string>
#include <iostream>
#include <cstdlib>
#include <cstdio> // 包含snprintf和perror的头文件

const int NUM = 100; // 缓冲区大小
const int N = 2;     // 管道数组的大小
using namespace std;

void Write(int wfd) {
    string s = "hello, I am child";
    int number = 0;
    char buffer[NUM]; // 定义一个缓冲区
    while (true) {
        sleep(1);
        snprintf(buffer, sizeof(buffer), "%s-%d-%d", s.c_str(), getpid(), number++);
        // 构建字符串
        write(wfd, buffer, sizeof(buffer)); // 实现传参写入
        cout << number << endl; // 打印计数
        if (number >= 5) break;
    }
}

void Reader(int rfd) {
    char buffer[NUM];
    while (true) {
        buffer[0] = 0;
        ssize_t n = read(rfd, buffer, sizeof(buffer)); // 读取缓冲区
        if (n > 0) {
            buffer[n] = 0; // 终止字符串,打印
            cout << "father get a message[" << getpid() << "]# " << buffer << endl;
        } else if (n == 0) {
            cout << "father read file done!" << endl;
            break;
        } else {
            perror("Error reading from pipe"); // 打印错误信息
            break;
        }
    }
}

int main() {
    int pipefd[N] = {0}; // 管道数组描述符
    int n = pipe(pipefd);
    if (n < 0) {
        perror("Error pipe");
        return 1;
    }

    pid_t id = fork(); // 创建子进程
    if (id < 0) {
        perror("Error forking");
        return 2; // 回忆点1
    }
    if (id == 0) {
        // 子进程
        close(pipefd[0]);
        Write(pipefd[1]); // 传入写端
        close(pipefd[1]);
        exit(0); // 回忆点2
    } else {
        // 父进程
        close(pipefd[1]);
        Reader(pipefd[0]);

        // 回忆点3
        // 等待回收
        pid_t rid = waitpid(id, NULL, 0);
        if (rid < 0) {
            perror("Error waiting for child process");
            return 3;
        }

        close(pipefd[0]); // 关闭读端
    }

    cout << "Program finished." << endl;
    return 0;
}

3.3 站在内核的角度

必须要调用 write read,系统调用,防止群众当中有坏人,操作系统起到了一个媒介的作用

note:

  • 混编C/C++需掌握字符串处理转换;
  • 管道通信需制定字节流数据协议。

编码实现

管道的 5 大特征

1. 具有血缘关系的进程,进行进程间通信

2. 管道只能单向通信

  • 为什么子进程休眠,父进程也会休眠?是的
  • 看到同一份资源实现了,多执行流共享,难免出现访问冲突的问题,存在临界资源竞争的问题

3. 管道是基于文件的,而文件的生命周期是随进程的例如:不需要我们去关 0 1 2

下面特性之后会详细解释:

4. 父子进程是会进程协同的,同步与互斥的---保护管道文件的数据安全

那父进程休眠,子进程呢?

不读的时候,一瞬间就把管道写满了,可以发现:管道是有固定大小的

5. 管道是面向字节流

可能写端写了十次一百次,读端都不管,看着就是一个一个字节的,上面提到管道有固定大小,那么是多少?

验证管道的固定大小: 使用 ulimit -a 命令可以查看当前用户进程的资源限制,其中包括管道的大小。管道大小在输出中通常标记为 pipe size

ulimit -a | grep "pipe size"
  1. 解释管道大小: 管道的默认大小在不同内核版本中可能有所不同。在许多 Linux 系统上,默认的管道大小是 64 KB。

查看 Linux 发行版和版本: 使用 cat /etc/redhat-release 命令可以查看 Red Hat 或 CentOS 系统的发行版和版本信息。

cat /etc/redhat-release

测试管道大小: 要测试管道大小是否为 64 KB,可以使用 dd 命令尝试写入一个大于 64 KB 的文件到管道,并观察是否能够成功写入。

dd if=/dev/zero of=/dev/null bs=65536 count=1 2>&1 | grep records

如果管道大小确实是 64 KB,那么上面的命令应该能成功写入 64 KB 数据。

查找 pipe_buf 大小pipe_buf 的大小定义在内核头文件中,可以通过查看内核源码或使用 grep 命令在内核头文件中搜索。制

grep -R "define PIPE_BUF" /usr/include/linux

或者,如果你有内核源码树,可以在源码中搜索:

grep -R "define PIPE_BUF" /path/to/linux-source-code/include/linux

进行原子性读取测试: 为了确保在不间断的情况下完整读取 “hello world” 字符串,并保证原子性,可以创建一个管道,并在一个进程中写入字符串,在另一个进程中读取。

管道的四种情况

  1. 读写段正常,管道如果为,读端就要阻塞
  2. 读写端正常,管道如果被写满,写端就要阻塞
  3. 读端正常读,写端关闭读端就会读到 0,表面读到了文件(pipe)结尾,不会被阻塞

eg. 父进程等待子进程(Z)退出,测试 n=0;

      4. 写端是正常写入,读端关闭了的情况呢?敬请期待下篇分解~

一些小思考:

1. 一个进程中可以开两个 pipe:

  • 一个进程可以创建多个管道,每个管道都有独立的读写端

2. read 读到文件尾,write 一写就会更新到文件头:

  • 管道是循环缓冲区,写入数据会覆盖最早的数据,read 指针会自动跳到新的数据开始处

3. 覆盖写,但为什么不会读取垃圾信息?read 指针也在移动:

  • 管道确保了写入和读取的原子性,每次读取都是连续的数据块,不会读取到被覆盖的旧数据。

拓展:

管道(pipe)是一种在进程间进行单向通信的机制。

为什么管道文件无法实现“写了就马上读到”:

  1. 缓冲区限制:管道有一个固定大小的缓冲区。当这个缓冲区满了之后,进一步的写操作将会被阻塞,直到有空间可用。这意味着,如果写入的数据超过了缓冲区的大小,那么这些数据不会立即被读取,因为它们还在等待空间变得可用
  2. 读写顺序:管道是按照顺序操作的。写入端(writer)必须先写入数据,然后读取端(reader)才能从管道中读取这些数据。如果读取端没有进行读取操作,写入端的数据可能会留在缓冲区中。
  3. 原子性:管道的读写操作具有原子性。这意味着每次读取操作要么读取一个完整的数据块,要么读取0个字节(如果缓冲区为空)。这防止了读取到不完整或“垃圾”数据。
  4. 阻塞与非阻塞:默认情况下,读取端如果尝试从一个空的管道中读取数据,将会被阻塞,直到有数据可读。
  5. 进程调度:操作系统的进程调度可能影响管道的读写操作。即使缓冲区中有数据,如果读取进程没有获得CPU时间,那么读取操作也不会发生。

总结来说,管道不是立即写入即读取的同步机制,而是一个提供了有序、缓冲的通信方式,其设计考虑了数据的完整性和进程间的同步。如果需要立即写入后读取,可能需要考虑其他通信机制,如消息队列或信号量。

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

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

相关文章

通俗易懂的告诉你大模型如何微调!

如今&#xff0c;大模型&#xff08;Large Language Models&#xff09;在人工智能领域可是炙手可热的话题。它们拥有庞大的参数和广泛的知识&#xff0c;能够处理各种复杂的任务。然而&#xff0c;就像一把锋利的刀需要经过磨砺才能更贴合手型一样&#xff0c;大模型也需要经过…

鸿道Intewell操作系统X86生态之:Intel J1900

在当今数字化转型的浪潮中&#xff0c;工业自动化和智能制造的需求日益增长&#xff0c;对实时操作系统的性能和可靠性提出了更高的要求。鸿道Intewell操作系统正是聚焦于如何将高性能的处理器与先进的操作系统相结合&#xff0c;以构建一个强大且稳定的工业控制系统。 鸿道I…

【算法系列】双指针

双指针算法 1. 双指针算法概述2 经典双指针算法题目分享1. **复写零**2. 快乐数&#xff08;medium&#xff09;3. 11. 盛最多水的容器4. 有效三⻆形的个数&#xff08;medium&#xff09;5. 四数之和 1. 双指针算法概述 常⻅的双指针有两种形式&#xff0c;⼀种是对撞指针&…

MMC和eMMC的区别

MMC 和 eMMC 的区别 1. MMC MMC&#xff08;MultiMediaCard&#xff09;是一种接口协议&#xff0c;定义了符合这一接口的内存器&#xff0c;称为 MMC 储存体或 MMC 卡。它是一种非易失性存储器件&#xff0c;广泛应用于消费类电子产品中。 1.1 外观及引脚定义 MMC卡共有七个…

文件解析漏洞合集

IIS 解析漏洞 IIS6 目录解析 打开windows——server2003&#xff0c;在 wwwroot 目录下创建 1.asp &#xff0c;在其中创建的所有文件都会在访问时以 asp 解析出来 畸形文件解析 在wwwroot目录下创建 2.asp;.jpg &#xff0c;此文件上传时是 .jpg 后缀,但解析时由于 iis6 文…

transformer死亡9问

transformer死亡20问 1. Transformer为何使用多头注意力机制&#xff1f;2. Transformer为什么Q和K使用不同的权重矩阵生成&#xff0c;为何不能使用同一个值进行自身的点乘3. Transformer计算attention的时候为何选择点乘而不是加法&#xff1f;两者计算复杂度和效果上有什么区…

C#桌面开发(那些年你总走进误区的技术):异步多线程、异步事务与递归技术

1. 异步多线程 (Asynchronous Multithreading) 在C#桌面开发中&#xff0c;异步多线程是提高应用程序响应速度和性能的关键技术之一。以下是几个深入的技术点和示例代码。 1.1 使用async和await实现异步操作 C#的async和await关键字使得编写异步代码变得更加简单。以下是一个…

老司机也会翻车?通过自动建模技术轻松实现工程机械翻滚保护分析

什么是ROPS分析&#xff1f; ROPS分析&#xff0c;指的是"Roll-Over Protective Structure"&#xff08;翻滚保护结构&#xff09;的简称&#xff0c;这是一种用于评估和设计特殊设备&#xff08;如前装载机、各种挖掘机、履带式推土机&#xff09;的被动安全标准&am…

slam过程中每一帧的gt位姿如何计算

一般得到的每一帧数据类似如下&#xff1a; 4*4的变化矩阵&#xff0c;都属于相机到世界坐标系下的变化矩阵&#xff0c;如果是x,y,z和四元数也可以转换为这种4*4的矩阵。 第一帧为世界坐标系的原点&#xff0c;后续的位姿都基于这个原点进行变化。 def load_poses(path, n_im…

HCIA概述

一、OSI七层模型 1.物理层&#xff08;七层&#xff09; 定义物理设备的标准&#xff0c;主要对物理连接方式&#xff0c;电气特性&#xff0c;机械特性等制定统一标准&#xff0c;传输比特流&#xff0c;因此最小的传输单位——位&#xff08;比特流&#xff09;。 2.数据链…

差分专题的练习

神经&#xff0c;树状数组做多了一开始还想着用树状数组来查询差分数组&#xff0c;但是我们要进行所有元素的查询&#xff0c;直接过一遍就好啦 class Solution { public:int numberOfPoints(vector<vector<int>>& nums) {vector<int> c(105, 0);for (i…

Hadoop的安装和使用-2024年08月01日

Hadoop的安装和使用-2024年08月01日 1.创建Hadoop用户2.SSH登陆权限设置3.java的安装4.Hadoop单机安装配置5.Hadoop伪分布式安装配置 1.创建Hadoop用户 如果安装Ubuntu的时候不是用的“hadoop”用户&#xff0c;那么需要增加一个名为 hadoop的用户首先按ctrlaltt打开终端窗口&…

源代码加密防泄漏如何做?

源代码开发环境复杂&#xff0c;涉及的开发软件、文件类型庞杂多变&#xff0c;究竟有什么源代码加密防泄漏软件能够适应众多开发软件而不影响原有的工作效率&#xff1f; 相信这是很多IT管理员或者老板们都想要了解的问题&#xff0c;今天和行业内专业人士讨论&#xff0c;将…

【docker】虚拟化与docker基础

一、虚拟化 1.虚拟化概述 什么是虚拟化&#xff1f; 虚拟化&#xff1a;将应用程序和系统内核资源进行解耦&#xff0c;以操作系统级别进行隔离&#xff0c;目的是提高资源利用率 2、虚拟化的功能 将虚拟化的性能优化趋近于物理资源的性能&#xff0c;主要用于提高资源利用…

浏览器被360劫持了的解决办法

所有浏览器一打开就是360界面&#xff0c;查询资料解决 以谷歌浏览器为例&#xff1a;打开其exe的位置&#xff0c;将exe文件重命名&#xff0c;再次创建快捷方式即可

Kickstart自动安装系统

目录 一、Kickstart的介绍 1.1 为什么要使用Kickstart? 1.2 如何解决以上问题&#xff1f; 1.3 Kickstart的作用 二、实验环境 2.1 实验所需环境 2.2 测试所给的环境是否可用 三、安装Kickstart配置ks文件 3.1 安Kickstart 3.2 配置ks文件 3.2.1 使用图像配置工具配置…

日志采集格式

本实验需要两个虚拟机&#xff0c;一个用于配置&#xff0c;一个用于查看测试结果 node1主机上的配置 vim /etc/rsyslog.conf #添加配置&#xff0c;写入指定的日志格式 13 module(load"builtin:omfile" Template"HAHA") 14 $template HAHA,"%FROM…

Ceres Cuda加速

文章目录 一、简介二、准备工作三、实现代码四、实现效果参考资料一、简介 字Ceres2.2.1版本之后,作者针对于稠密矩阵的分解计算等操作进行了Cuda加速,因此这里就基于此项改动测试一下效果。 二、准备工作 1、首先是需要安装Cuda这个英伟达第三方库,https://developer.nvidi…

eclipse免安装版64位 2018版本(java开发软件)

前言 eclipse是一个开放源代码的、基于Java的可扩展开发平台。就其本身而言&#xff0c;它只是一个框架和一组服务&#xff0c;用于通过插件组件构建开发环境。 一、下载地址 下载地址&#xff1a;分享文件&#xff1a;eclipse v2018.zip下载 二、安装步骤 1、下载解压后将…

日撸Java三百行(day13:链表)

目录 一、链表的基础知识 二、链表的代码实现 1.链表创建 2.链表遍历 3.链表定位查找 4.链表插入 5.链表删除 6.数据测试 7.完整的程序代码 总结 一、链表的基础知识 在之前顺序表的学习中&#xff0c;我们其实提到过链表。链表它是线性表在不同的物理存储方式下派生…