开发者
资源
鲲鹏开发板3个openEuler实验
鲲鹏开发板3个openEuler实验
发表于2025/05/27
1990

鲲鹏开发板3个openEuler实验

实验编号实验名称难度值预估时长(分钟)内容简介
1获取系统信息45获取CPU、操作系统等系统信息
2父子进程关系65了解父子进程的关系和资源管理
3设置CPU亲和性710查看、设置CPU亲和性

注:难度的取值范围是1-10分,分数越高越难。

I 实验设备

本文3个和openEuler操作系统相关的3个实验将基于“鲲鹏开发板 + VS Code”进行。其中VS Code为系统镜像所自带,而鲲鹏开发板的规格如下所示:

kdb.png

  • 产品名称:OrangePi Kunpeng Pro
  • 操作系统:openEuler 22.03
  • 内存大小:8GB/16GB
  • CPU:4核64位ARM处理器,1.6GHz

II 如何登录到开发板

login2kdb.png

如上图所示,登录到鲲鹏开发板的方式有二:

  • 使用PC登录开发板

    • PC通过以太网和开发板相连,则:
      • 在PC的Console命令行终端中通过ssh命令登录系统;
      • 通过VNC(Virtual Network Computing)登录系统✅。
    • PC通过串口(Serial)和开发板相连,则:
      • 注意设置与鲲鹏开发板串口相同的波特(Baud,symbol/s),这里为115200bps;
      • 通过xterm_logo.png“MobaXterm”软件选择使用串口(Serial)登录系统。
  • 开发板+显示器+键盘+鼠标相连

    • 可以直接登录开发板✅。

由于本次实验我们需要登录到开发板上操作系统的桌面以使用VS Code,所以我们选择通过VNC或“开发板+显示器+键盘+鼠标”直连的方式登录。

III 实验准备

登录系统

🔒🗝 安全

为系统安全起见,我们一般以非root用户登录,在这里我们以openEuler用户登录系统,其密码也是“openEuler”。

在运行需要root权限的命令时,要使用sudo命令,例如:

sudo rm -r /root/tmp
[sudo] openEuler 的密码:

系统会提示输入密码,而输入密码时不会有任何回显。

我们开机并登录到开发板的桌面后如下图所示:

openEuler-desktop.png

打开 VS Code IDE

现在我们可以以鼠标左键单击“活动”按钮并在弹出来的菜单中单击VS Code(Visual Studio Code)图标以启动这个IDE:

launch-vscode.png

然后我们可以按下“Ctrl+`”(后者和“~”键在一个物理位置上)以打开VS Code里的终端窗口(再次按下这两个键会关闭该窗口):

vscode-terminal.png

我们可以在终端窗口里运行以下命令以检查我们的身份:

whoami # i.e. openEuler

随后进入到我们的工作目录(这里假设是“~/kdb”,如果没有这个目录可以用mkdir命令创建它):

cd ~/kdb # or `mkdir kdb && cd $_`

当要创建一个新的C程序文件的时候可以运行以下命令:

code code.c

这个命令会创建一个名为“code.c”(这个名称只是一个示例)的文件,并将键盘输入焦点指向它,如果要保存该文件,可以按“Ctrl+s”(如果不保存则不会真正创建该文件)。

💡 扩展知识
在VS Code的编辑窗口中,按下“Ctrl+w”以关闭该编辑窗口,按下“Ctrl+o”用以打开一个文件。

IV 三个实验

现在我们就可以做实验了,本次实验有三,您可以任选一个或几个进行实验。

实验一 获取系统信息

所有的软件都在一定的系统环境上运行,本次实验我们一窥运行在鲲鹏处理器之上的openEuler操作系统的系统环境。

💡 扩展知识

  • 鲲鹏处理器是华为基于ARMv8架构开发的通用处理器。作为一款现代处理器,在芯片内部架构中,鲲鹏处理器涉及到了体系结构中的SoC、Chip、DIE、Cluster以及Core等概念,从而组成了一个复杂的综合体。
  • openEuler既是一个操作系统,又是一个开源、免费的Linux发行平台,其致力于打造中国原生开源、可自主演进操作系统根社区。作为操作系统,当前openEuler内核源于Linux,支持鲲鹏及其他多种处理器。

我们可以通过Linux shell命令查看系统信息。

快速获取系统基本信息

uname是Linux系统中用于获取系统信息的命令,其名称来源于"Unix Name"。

🎵 代码示例

uname -m # aarch64
uname -r # i.e. 5.10.0+
uname -a # ...... aarch64 GNU/Linux

以上一系列uname命令分别查看了系统的CPU架构、操作系统内核版本及其他信息。

获取CPU详细信息

lscpu是Linux系统中用于显示CPU架构和详细信息的命令。它从“/proc/cpuinfo”文件中提取信息,并以易于阅读的格式展示CPU的相关配置和特性。

🎵 代码示例

lscpu

其可能的输出是:

Architecture:           aarch64
  CPU op-mode(s):       64-bit
  Byte Order:           Little Endian
CPU(s):                 4
  On-line CPU(s) list:  0-3
...

查看操作系统版本

🎵 代码示例

cat /etc/os-release

可能的显示结果是:

NAME="openEuler"
VERSION="22.03 (LTS-SP4)"
ID="openEuler"
VERSION_ID="22.03"
PRETTY_NAME="openEuler 22.03 (LTS-SP4)"
ANSI_COLOR="0;31"

从上面的版本信息“22.03 LTS SP4”可以看出,这是openEuler的一个长期支持版(Long-Term Support),于2022年3月发布,目前已经到了第4个服务包(Service Pack 4)。

进行实验

该实验是编写一个C程序以获取openEuler操作系统上CPU、操作系统的基本信息。

🎵 代码示例

/*
 * sysinfo.c
 */

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

// 函数声明
void get_cpu_info();
void get_os_info();

int main() {
    printf("=== CPU 信息 ===\n");
    get_cpu_info();
    printf("\n=== 操作系统信息 ===\n");
    get_os_info();
    return 0;
}

// 获取CPU信息
void get_cpu_info() {
    FILE *fp;
    char buffer[128];

    // 打开/proc/cpuinfo文件
    fp = fopen("/proc/cpuinfo", "r");
    if (fp == NULL) {
        perror("无法打开 /proc/cpuinfo");
        exit(EXIT_FAILURE);
    }

    // 读取CPU信息
    while (fgets(buffer, sizeof(buffer), fp)) {
        if (strstr(buffer, "CPU architecture")) {
            printf("处理器架构: ARMv%s", strchr(buffer, ':') + 2);
            break;
        }
    }

    fclose(fp);
}

// 获取操作系统信息
void get_os_info() {
    FILE *fp;
    char buffer[128];

    // 打开/etc/os-release文件
    fp = fopen("/etc/os-release", "r");
    if (fp == NULL) {
        perror("无法打开 /etc/os-release");
        exit(EXIT_FAILURE);
    }

    // 读取操作系统名称
    while (fgets(buffer, sizeof(buffer), fp)) {
        if (strstr(buffer, "PRETTY_NAME")) {
            printf("操作系统名称: %s", strchr(buffer, '=') + 2);
            break;
        }
    }

    fclose(fp);

    // 打开/proc/version文件
    fp = fopen("/proc/version", "r");
    if (fp == NULL) {
        perror("无法打开 /proc/version");
        exit(EXIT_FAILURE);
    }

    // 读取内核版本号
    if (fgets(buffer, sizeof(buffer), fp)) {
        printf("内核版本号: %s", buffer);
    }

    fclose(fp);
}

下图显示了运行该程序的一个实例:

sysinfo.png

🤚 动手操作

请按以下步骤进行实验:

  • 进入VS Code终端,在终端中输入以下命令进入“kdb”目录:cd ~/kdb
    • 如果不存在此文件夹请输入以下命令创建该文件夹并进入之:
      • mkdir kdb &amp;&amp; cd $_
  • 在VS Code中终端创建一个名为“sysinfo.c”的文件:code sysinfo.c
    • 现在键盘的输入焦点转到编辑窗口,拷贝上述示例代码并保存该文件
  • 在VS Code的终端窗口中编译该文件:gcc sysinfo.c -o sysinfo
  • 在VS Code的终端窗口中执行该程式:./sysinfo
  • 观察该程式的输出结果。

🔔 清理

记得最后在VS Code终端窗口删除本次实验所有新建文件:rm -f ~/kdb/*

实验二 父子进程关系

fork()是Linux系统编程中非常重要的一个系统调用,用于创建新的进程。

基本概念

功能:fork()用于创建一个与当前进程(父进程)几乎完全相同的子进程。
返回值

  • 成功时:
    • 在父进程中返回子进程的PID(进程ID)
    • 在子进程中返回0(用于标识自身为子进程)
  • 失败时:
    • 返回-1,并设置errno

关键特性

  • fork() 创建的子进程是父进程的副本,包括:
    • 代码段
    • 数据段
    • 堆栈
    • 打开的文件描述符
    • 信号处理程序
  • 子进程从 fork() 返回处开始执行。
  • 父进程可以通过 wait() 或 waitpid() 等待子进程结束。
#### 简单示例

🎵 代码示例

/*
 * process.c
 */

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

int main() {
    int mileage = 0;
    pid_t pid = fork();  // 创建子进程

    if (pid < 0) {
        // fork 失败
        perror("Fork failed");
        return 1;
    } else if (pid == 0) {
        // 子进程
        mileage = 120;
        printf("\nChild process: PID = %d, Parent PID = %d. \
            \n\tAnd the son's journey reached %d kilometers.\n", \
            getpid(), getppid(), mileage);
    } else {
        // 父进程
        mileage = 100;

	    wait(NULL); // 等待子进程结束
        printf("\nParent process: PID = %d, Child PID = %d. \
            \n\tAnd the father's journey reached %d kilometers.\n", \
            getpid(), pid, mileage);
    }

    return 0;
}

运行上述程序,可能的输出是:

Child process: PID = 5026, Parent PID = 5025.
        And the son's journey reached 120 kilometers.

Parent process: PID = 5025, Child PID = 5026.
        And the father's journey reached 100 kilometers.

解释:

  • 通常每次运行时分配到的进程号都不会相同,所以每次运行的打印结果都会有变化。
  • 父进程和子进程的输出顺序不确定(取决于系统调度),但是我们使用了wait()函数让子进程先输出。
  • 父子进程各自拥有变量mileage的值,说明子进程从父进程拷贝了副本,两者不共享数据。

在VS Code IDE中运行的情况如下图所示:

process.png

进行实验

设想一对父子从家中出发到一处风景胜地,父亲立即近路(100公里路程)动身,儿子稍作准备从远路(120公里路程)前往。通常父亲会先到达目的地,但是他会等待儿子到达排在他前面用餐。请用C语言创建父子进程模拟上述情景。

🤚 动手操作

请按以下步骤进行实验:

  • 进入VS Code终端,在终端中输入以下命令进入“kdb”目录:cd ~/kdb
    • 如果不存在此文件夹请输入以下命令创建该文件夹并进入之:
      • mkdir kdb &amp;&amp; cd $_
  • 在VS Code中终端创建一个名为“process.c”的文件:code process.c
    • 现在键盘的输入焦点转到编辑窗口,拷贝上述示例代码并保存该文件
  • 在VS Code的终端窗口中编译该文件:gcc process.c -o process
  • 在VS Code的终端窗口中执行该程式:./process
  • 观察该程式的输出结果。

🔔 清理

记得最后在VS Code终端窗口删除本次实验所有新建文件:rm -f ~/kdb/*

实验三 设置CPU亲和性

CPU亲和性(CPU Affinity)是指将进程或线程绑定到特定的CPU核上运行,以优化性能或满足特定需求。通过设置CPU亲和性,可以减少上下文切换和缓存失效,从而提高系统效率。

查看核心数

我们首先需要查看逻辑CPU个数(逻辑CPU个数 = 物理CPU个数 * core个数)。我们可以用以下命令查看:

🎵 代码示例

cat /proc/cpuinfo | grep "processor" | wc -l

在本例中,输出为4,表明该硬件系统中共有4个逻辑CPU(从0到3编号)。在本实验中,将通过进程/线程的处理器亲和性将进程/线程绑定到指定的逻辑CPU上。

💡 扩展知识

可通过命令cat /proc/cpuinfo查看更多的处理器信息。

用C函数设置CPU亲和性

在C程式中,我们可以通过sched_setaffinity()函数设置进程的CPU亲和性。

语法:

int sched_setaffinity(pid_t pid, size_t cpusetsize, const cpu_set_t *mask);
  • pid:进程号(Process ID),也就是要进行绑定CPU的进程ID,如果其值为0,则表示指定的是当前进程。
  • cpusetsize:mask参数所指向的CPU集合的大小。
  • mask:与进程进行绑定的CPU集合(一个进程可以绑定到多个CPU上运行):
    • 参数mask的类型为cpu_set_t,它是一个位图,其中的每个位表示一个CPU核。
    • 示例:
      • cpu_set_t的第0位设置为1,即0001,表示将进程绑定到CPU0上运行,而不会调度到其他CPU。
      • cpu_set_t的第2位设置为1,即0100,表示将进程绑定到CPU2上运行,而不会调度到其他CPU。
      • cpu_set_t的第0位和第2位设置为1,即0101,表示将进程绑定到CPU0和CPU2上运行,即进程可能在CPU0或CPU2上运行,而不会在其他CPU上运行。
🎵 *代码示例*
/*
 * cpuset.c
 */

#define _GNU_SOURCE
#include <sched.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>

int main(int argc, char **argv) {
    cpu_set_t mask;
    CPU_ZERO(&mask);    // 初始化CPU集合,将mask置为空
    CPU_SET(1, &mask);  // 将本进程绑定到CPU1上

    // 设置进程的CPU亲和性
    if (sched_setaffinity(0, sizeof(mask), &mask) == -1) {
        printf("Set CPU affinity failed, error: %s\n", strerror(errno));
        exit(EXIT_FAILURE); 
    }

    while (1) {;}  // 无限循环,以便实验观察

    return 0;
}

注意这个程序会进入一个无限循环(可以按“Ctrl+c”键终止该循环)。

用Linux命令查看/设置CPU亲和性

具体怎么来查看其运行结果呢?除了利用编程接口来设置CPU亲和性外,在Linux中,我们也可以使用命令行工具taskset来查看和设置。

语法:

taskset -p [mask] [pid]
  • pid:进程号,也就是要进行绑定CPU的进程ID。
  • mask:与进程进行绑定的CPU集合(一个进程可以绑定到多个CPU上运行)。

🎵 代码示例

taskset -p 1234  # 查看进程的CPU亲和性
taskset -pc 1234  # 查看进程的CPU亲和性
taskset -p 0x3 1234  # 将PID为1234的进程绑定到CPU0和CPU1
taskset 0x1 ./my_program  # 启动新进程并将其绑定到CPU0

获取进程号

那么,如何在命令行中得到一个进程的进程号呢——可以使用ps命令。

语法:

ps [OPTIONS]
  • ps(process status,进程状态)命令是Linux系统中用于显示当前进程信息的一个非常有用的工具。

🎵 代码示例

ps -a | grep cpuset # 查看程式“cpuset”运行时所具备的进程号

一个实例

下面三个步骤展示了上述诸般操作的一个实例。

🎶步骤 1:首先编辑、编译并执行cpuset.c程序:

cpuset-1.png

🎶步骤 2:然后按下“Ctrl+Shift+`”键开启另外一个终端:

cpuset-2.png

🎶步骤 3:在新的终端里用taskset命令查看/设置CPU亲和性:

cpuset-3.png

🎶步骤 4:最后需要回到上图中右下角子窗口所示的“./cpuset”终端窗口,按下“Ctrl+c”键结束cpuset进程。

进行实验

🤚 动手操作

请按以下步骤进行实验:

  • 进入VS Code终端,在终端中输入以下命令进入“kdb”目录:cd ~/kdb
    • 如果不存在此文件夹请输入以下命令创建该文件夹并进入之:
      • mkdir kdb &amp;&amp; cd $_
  • 在VS Code中终端创建一个名为“cpuset.c”的文件:code cpuset.c
    • 现在键盘的输入焦点转到编辑窗口,拷贝上述示例代码并保存该文件
  • 在VS Code的终端窗口中编译该文件:gcc cpuset.c -o cpuset
  • 在VS Code的终端窗口中执行该程式:./cpuset
  • 新启动一个终端;
  • 在新的终端中输入以下命令以观察/设置“cpuset”进程的CPU亲和性:
    • ps -a | grep cpuset # 查看cpuset进程的进程号(比如说是7593)
    • taskset -pc 7593 # 查看该进程的CPU亲和性(进程号7593是一个示例)
    • taskset -p ❓ 7593 # 请填入正确的掩码,以将该进程绑定在CPU2上
    • taskset -pc 7593 # 查看是否绑定成功
  • 在运行“cpuset”程式的窗口中按“Ctrl+c”键结束该进程。

🔔 清理

记得最后在VS Code终端窗口删除本次实验所有新建文件:rm -f ~/kdb/*

术语表

本实验中所用术语列表如下:

🏷术语表

  • PC
    • Personal Computer,个人电脑
  • SSH
    • Secure Shell,安全外壳。一种网络安全协议,通过加密和认证机制实现安全的访问和文件传输等业务。
  • IDE
    • Integrated Development Environment,集成开发环境
  • TCP/IP
    • Transmission Control Protocol / Internet Protocol,传输控制协议/网际协议。其不仅仅指TCP和IP两个协议,而是一个由ARP(Address Resolution Protocol)、IP、UDP(User Datagram Protocol)、TCP、HTTP(Hypertext Transfer Protocol)、FTP(File Transfer Protocol)、SMTP(Simple Mail Transfer Protocol)、Telnet等协议构成的协议簇,尤其以TCP和IP最具代表性。

附录A 参考文章

🔗登录到鲲鹏开发板的两种方式

🔗如何通过VNC访问鲲鹏开发板

🔗鲲鹏实验平台简介

收藏举报
Level 1
0
帖子
0
粉丝
0
获赞