跳转到内容

系统操作 - 线程/进程操作

1.1. 多线程概述

进程和线程的概念是操作系统(OS)与普通微控制器之间最大的区别之一。在多任务系统中,可以划分系统资源,使不同任务独立运行。从宏观角度来看,多个应用程序看起来像是在同时运行(例如,一边浏览网页一边播放音乐)。这就是引入进程和线程的重要意义。

1.2. 快速入门

1.2.1 开发环境准备

如果您是第一次阅读本文档,请先阅读《入门指南/开发环境准备/Easy-Eai 编译环境的准备与更新》,并按照其中的步骤搭建编译环境。

在 PC 上的 Ubuntu 系统中执行 run 脚本,进入 EASY-EAI 编译环境。具体如下。

Terminal window
cd ~/develop_environment
./run.sh 2204

1.2.2 下载源代码并编译示例

在 EASY-EAI 编译环境下,创建用于管理源代码仓库的目录。

Terminal window
cd /opt
mkdir EASY-EAI-Toolkit
cd EASY-EAI-Toolkit

https://dl.dragonwake.com/download/rv1126b/embedded/EASY-EAI-Toolkit-1126B/Demos.zip

从上述链接下载 Demos.zip 压缩文件,并将其上传到虚拟机的 /opt/EASY-EAI-Toolkit 目录中,然后解压。 进入对应示例的目录并执行编译操作。具体命令如下。

Terminal window
cd EASY-EAI-Toolkit-1126B/Demos/common-system_opt/
./build.sh

💡 注意:由于依赖库放置在开发板上,因此在交叉编译过程中必须保持 /mnt 挂载。

1.2.2 下载源代码并编译示例 图27

1.2.2 下载源代码并编译示例 图27

1.2.3 运行示例

通过串口调试或 SSH 调试进入开发板后台,并移动到示例所在位置。如下所示。

Terminal window
cd /userdata/Demo/common-system_opt

1.2.3 运行示例 图28

1.2.3 运行示例 图28

示例运行命令如下。

Terminal window
./test-thread-opt

1.2.4 运行结果

运行结果如下所示。

1.2.4 运行结果 图29

1.2.4 运行结果 图29

关于 API 的详细说明以及 API 的调用方式(本示例的源代码),请参考以下说明。

1.3. 线程操作 API 说明

1.3.1 包含/引用方法

EASY EAI api 库位于本仓库的 easyeai-api 目录中。为了让客户能够在本地项目中直接调用 EASY EAI api 库,下面列出了项目中需要链接的库和头文件等内容。

说明CMake 中的写法Makefile 中的写法
api.cmake${common_root}/system_opt/api.cmake
头文件目录${SYSTEM_OPT_INCLUDE_DIRS}-I ../../easyeai-api/common/system_opt
源文件目录${SYSTEM_OPT_SOURCE_DIRS}../../easyeai-api/common/system_opt
库文件目录
库链接参数${SYSTEM_OPT_LIBS}

API 源代码路径为 EASY-EAI-Toolkit-1126B/easyeai-api/common/system_opt/。用户可以通过源代码查看接口的实现,也可以直接修改源代码。

1.3.2 线程任务原型

线程任务的原型如下所示。线程任务是程序执行的最小单位,定义如下。

Terminal window
typedef void \*(\*ThreadEntryPtrType)(void \*);

1.3.3 线程创建函数

线程创建函数的原型如下所示。

Terminal window
int32_t CreateNormalThread(ThreadEntryPtrType entry, void \*para, pthread_t \*pid);

具体说明如下。

函数名:CreateNormalThread()详细说明
头文件easyeai-api/common/system_opt/system_opt.h
输入参数entry:作为线程主体的执行函数
para:传递给线程的参数(作为共享变量使用)
pid:指向新创建的 pthread_t
类型对象的指针
返回值创建失败时返回 -1
创建成功时返回 0
注意事项pid
变量用于存储系统分配的线程 ID。传入 NULL 会导致程序异常退出。

1.3.4 执行 Shell 命令 - 调用 system()

执行 Shell 命令的函数原型如下。

Terminal window
int32_t exec_cmd_by_system(const char \*cmd);

该函数通过调用 system() 实现。具体说明如下。

函数名:exec_cmd_by_system()详细说明
头文件easyeai-api/common/system_opt/system_opt.h
输入参数cmd:Shell 命令
返回值如果 fork 失败,system() 函数返回 -1
如果 exec 成功且命令正常结束,则返回该命令通过 exit
或 return 返回的值。
注意事项1. 为了在出错时获得更有用的信息,建议在 system() 执行后监控 errno
值。
2.
异步(非阻塞)执行注意事项:为了将命令转入后台,需添加
&
并同时重定向输出。否则将以阻塞方式执行。

1.3.5 执行 Shell 命令 - 调用 popen()

执行 Shell 命令的函数原型如下。

Terminal window
int32_t exec_cmd_by_popen(const char \*cmd, char \*result);

该函数通过调用 popen() 实现,并可获取执行结果。具体说明如下。

函数名:exec_cmd_by_popen()详细说明
头文件easyeai-api/common/system_opt/system_opt.h
输入参数cmd:Shell 命令
result:Shell 命令执行后,返回结果会存储到该内存区域中
返回值调用失败时返回 -1
调用成功时返回 0(结果已正常写入 result)
注意事项如果命令执行失败,子进程会将错误信息打印到标准错误输出,因此父进程无法获取该信息。
如果需要捕获错误信息,请将子进程的错误输出重定向到标准输出(2>&1)。这样父进程就可以获取错误信息。
示例:exec_cmd_by_popen(“ls 2>&1”, result);

1.4. 线程操作 API 使用示例

线程操作 API 使用示例代码的路径如下:

EASY-EAI-Toolkit-1126B/Demos/common-system_opt/test-thread-opt.c 线程创建示例由“线程执行主体”和“线程创建操作”两部分组成。

线程执行主体如下所示。

void *testThreadBody(void *arg)
{
int *share_para = (int *)arg;
while(1)
{
printf("[tesThread] --- share_para = %d\n", *share_para);
if(*share_para > 10){
printf("[tesThread] --- exit\n");
break;
}
sleep(1);
}
pthread_exit(NULL);
}

线程创建操作如下所示。pId 变量用于存储进程(线程)ID,share_para 变量用于保存输入参数。

pthread_t pId;
int share_para = 0;
if(0 == CreateNormalThread(testThreadBody, &share_para,
&pId))
{
while(1){
printf("[mainThread] --- \n");
share_para++;
sleep(1);
}
}