文章目录
- 示例一. Cxx11SGXDemo
- 1.1 README
- 1.2 重点代码分析
- 1.2.1 App/App.cpp
- 1.2.2 App/TrustedLibrary/Libcxx.cpp
- 1.2.3 Enclave.edl
- 1.2.4 Enclave/TrustedLibrary/Libcxx.cpp
- 1.2.5 Enclave/Enclave.cpp+Enclave.h
- 1.3 编译执行
- 1.4 总结
- 示例二. Cxx14SGXDemo
- 示例三. Cxx17SGXDemo
- 四. 参考链接
- 五. 感谢支持
下面将给出一些sgx源码包中的示例分析,从中学习SGX的基本使用方法:
关于 SGX 开发运行环境的搭建可参考之前的一篇博客:【SGX系列教程】(一)。
示例一. Cxx11SGXDemo
1.1 README
-----------------------
Purpose of Cxx11SGXDemo
-----------------------
The project demonstrates serveral C++11 features inside the Enclave:
- lambda expressions;
- rvalue references and move semantics;
- automatic type deduction with auto and decltype;
- nullptr type;
- strongly typed enum classes;
- Range-based for statements;
- static_assert keyword for compile-time assertion;
- initializer lists and uniform initialization syntax;
- New virtual function controls: override, final, default, and delete;
- delegating constructors;
- new container classes (unordered_set, unordered_map, unordered_multiset, and unordered_multimap);
- tuple class;
- function object wrapper;
- atomic, mutexes, condition_variables;
- new smart pointer classes: shared_ptr, unique_ptr;
- new c++ algorithms: all_of, any_of, none_of;
- variadic templates;
- SFINAE;
---------------------------------------------
How to Build/Execute the C++11 sample program
---------------------------------------------
1. Install Intel(R) Software Guard Extensions (Intel(R) SGX) SDK for Linux* OS
2. Enclave test key(two options):
a. Install openssl first, then the project will generate a test key<Enclave_private_test.pem> automatically when you build the project.
b. Rename your test key(3072-bit RSA private key) to <Enclave_private_test.pem> and put it under the <Enclave> folder.
3. Make sure your environment is set:
$ source ${sgx-sdk-install-path}/environment
4. Build the project with the prepared Makefile:
a. Hardware Mode, Debug build:
$ make
b. Hardware Mode, Pre-release build:
$ make SGX_PRERELEASE=1 SGX_DEBUG=0
c. Hardware Mode, Release build:
$ make SGX_DEBUG=0
d. Simulation Mode, Debug build:
$ make SGX_MODE=SIM
e. Simulation Mode, Pre-release build:
$ make SGX_MODE=SIM SGX_PRERELEASE=1 SGX_DEBUG=0
f. Simulation Mode, Release build:
$ make SGX_MODE=SIM SGX_DEBUG=0
5. Execute the binary directly:
$ ./app
6. Remember to "make clean" before switching build mode
-------------------------------------------------
Launch token initialization
-------------------------------------------------
If using libsgx-enclave-common or sgxpsw under version 2.4, an initialized variable launch_token needs to be passed as the 3rd parameter of API sgx_create_enclave. For example,
sgx_launch_token_t launch_token = {0};
sgx_create_enclave(ENCLAVE_FILENAME, SGX_DEBUG_FLAG, launch_token, NULL, &global_eid, NULL);
工程的主要功能是测试c++11的一些特性在enclave中的执行情况,根据上面README
中的内容总结一下Cxx11SGXDemo
的执行步骤如下:
- 首先需要安装
SGX sdk
,以及等等对SGX的支持测试,具体可参考之前的一篇博客:【SGX系列教程】(一); - 安装
openssl
,目的是为enclave.so
生成签名私钥:
openssl genrsa -out Enclave/Enclave_private_test.pem -3 3072
- 确保sdk路径生效(source),基本在安装sdk时会指定安装路径为:
/opt/intel/sgxsdk
; - 使用Makefile构建工程:
- 硬件模式, Debug build:
make
- 硬件模式, Pre-release build:
make SGX_PRERELEASE=1 SGX_DEBUG=0
- 硬件模式, Release build:
make SGX_DEBUG=0
- 仿真模式, Debug build:
make SGX_MODE=SIM
- 仿真模式, Pre-release build:
make SGX_MODE=SIM SGX_PRERELEASE=1 SGX_DEBUG=0
- 仿真模式, Release build:
make SGX_MODE=SIM SGX_DEBUG=0
- 硬件模式, Debug build:
- 执行:
./app
- 在切换编译模式之前记得
make clean
!
1.2 重点代码分析
SGX工程的整个构建流程在上一篇文章中已经给出给出了详细教程:【SGX系列教程】(二)第一个 SGX 程序: HelloWorld,因此在此处仅给出部分重点代码分析,主要讲清楚enclave如何支持c++11的运行,并提供哪些支持,其他通用代码不做展开分析。
文件目录如下图,主要关注图中框出来的两个文件,其中Enclave文件夹中的Libxx.cpp提供enclave中函数执行功能,是系统的核心功能文件(将在后面详细分析文件功能),并封装成ECALL函数由外部调用;而App文件夹中的Libxx.cpp负责调用上述的ECALL函数。
整个程序调用过程可先总结为下图:
1.2.1 App/App.cpp
#include <stdio.h> // 标准输入输出库,提供如 printf 等函数
#include <string.h> // 字符串处理函数库
#include <assert.h> // 断言函数库,用于调试
# include <unistd.h> // 提供对 POSIX 操作系统 API 的访问,包括文件和进程
# include <pwd.h> // 提供对用户密码信息的访问
# define MAX_PATH FILENAME_MAX // 定义 MAX_PATH 为系统最大文件名长度
#include "sgx_urts.h" // SGX 用户运行时库,提供 SGX 相关 API
#include "App.h" // 应用程序相关的头文件
#include "Enclave_u.h" // Untrusted Enclave 的接口头文件
/* 全局 EID 由多个线程共享 */
sgx_enclave_id_t global_eid = 0; // 定义一个全局的 SGX enclave ID
/* 错误代码结构体 */
typedef struct _sgx_errlist_t {
sgx_status_t err; // 错误码
const char *msg; // 错误信息
const char *sug; // 错误建议
} sgx_errlist_t;
/* 由 sgx_create_enclave 返回的错误代码 */
static sgx_errlist_t sgx_errlist[] = { // 错误列表,包含常见的 SGX 错误码及其信息
{
SGX_ERROR_UNEXPECTED,
"Unexpected error occurred.",
NULL
},
{
SGX_ERROR_INVALID_PARAMETER,
"Invalid parameter.",
NULL
},
{
SGX_ERROR_OUT_OF_MEMORY,
"Out of memory.",
NULL
},
{
SGX_ERROR_ENCLAVE_LOST,
"Power transition occurred.",
"Please refer to the sample \"PowerTransition\" for details."
},
{
SGX_ERROR_INVALID_ENCLAVE,
"Invalid enclave image.",
NULL
},
{
SGX_ERROR_INVALID_ENCLAVE_ID,
"Invalid enclave identification.",
NULL
},
{
SGX_ERROR_INVALID_SIGNATURE,
"Invalid enclave signature.",
NULL
},
{
SGX_ERROR_OUT_OF_EPC,
"Out of EPC memory.",
NULL
},
{
SGX_ERROR_NO_DEVICE,
"Invalid SGX device.",
"Please make sure SGX module is enabled in the BIOS, and install SGX driver afterwards."
},
{
SGX_ERROR_MEMORY_MAP_CONFLICT,
"Memory map conflicted.",
NULL
},
{
SGX_ERROR_INVALID_METADATA,
"Invalid enclave metadata.",
NULL
},
{
SGX_ERROR_DEVICE_BUSY,
"SGX device was busy.",
NULL
},
{
SGX_ERROR_INVALID_VERSION,
"Enclave version was invalid.",
NULL
},
{
SGX_ERROR_INVALID_ATTRIBUTE,
"Enclave was not authorized.",
NULL
},
{
SGX_ERROR_ENCLAVE_FILE_ACCESS,
"Can't open enclave file.",
NULL
},
{
SGX_ERROR_NDEBUG_ENCLAVE,
"The enclave is signed as product enclave, and can not be created as debuggable enclave.",
NULL
},
{
SGX_ERROR_MEMORY_MAP_FAILURE,
"Failed to reserve memory for the enclave.",
NULL
},
};
/* 打印错误信息 */
void print_error_message(sgx_status_t ret)
{
size_t idx = 0; // 循环索引
size_t ttl = sizeof sgx_errlist / sizeof sgx_errlist[0]; // 错误列表的总长度
// 遍历错误列表,查找对应的错误
for (idx = 0; idx < ttl; idx++) {
if (ret == sgx_errlist[idx].err) { // 找到对应的错误码
if (NULL != sgx_errlist[idx].sug) // 如果存在建议,打印建议
printf("Info: %s\n", sgx_errlist[idx].sug);
printf("Error: %s\n", sgx_errlist[idx].msg); // 打印错误信息
break;
}
}
if (idx == ttl) // 如果遍历后未找到相应错误码,打印未预期的错误发生
printf("Error: Unexpected error occurred.\n");
}
/* 初始化信任域:
* 调用 sgx_create_enclave 来初始化一个信任域实例
*/
int initialize_enclave(void)
{
sgx_status_t ret = SGX_ERROR_UNEXPECTED; // 初始化错误状态
/* 调用 sgx_create_enclave 来初始化一个信任域实例 */
/* 调试支持:将第二个参数设置为 1 */
ret = sgx_create_enclave(ENCLAVE_FILENAME, SGX_DEBUG_FLAG, NULL, NULL, &global_eid, NULL);
if (ret != SGX_SUCCESS) { // 如果初始化失败,打印错误信息
print_error_message(ret);
return -1; // 返回错误状态
}
return 0; // 成功初始化,返回0
}
/* OCall 函数,由enclave.cpp中函数调用:ocall_print_string(buf) */
void ocall_print_string(const char *str)
{
printf("%s", str);
}
/* 主应用程序main入口 */
int SGX_CDECL main(int argc, char *argv[])
{
(void)(argc); // 防止未使用参数警告
(void)(argv);
/* 初始化信任域 */
if (initialize_enclave() < 0) { // 如果信任域初始化失败,打印信息并退出
printf("Enter a character before exit ...\n");
getchar(); // 等待用户输入
return -1;
}
/* 使用信任库函数,定义在App/TrustedLibrary/Libcxx.cpp文件中*/
ecall_libcxx_functions(); // 调用 ECALL 函数,利用受信任的 C++11库
/* 销毁信任域 */
sgx_destroy_enclave(global_eid); // 销毁信任域实例
printf("Info: Cxx11DemoEnclave successfully returned.\n");
// 打印成功信息
//printf("Enter a character before exit ...\n");
//getchar(); // 此行代码被注释掉,仅供调试时使用
return 0; // 返回0,表示程序正常退出
}
上述实现了一个简单的SGX应用程序,主要功能包括:初始化SGX信任域、调用受信任代码、打印错误信息和与主机间通信。用于展示如何在C++中利用Intel SGX进行安全函数的调用和管理。可以看出函数ecall_libcxx_functions()
是核心功能函数。下面介绍该函数。
1.2.2 App/TrustedLibrary/Libcxx.cpp
#include <stdio.h>
#include "../App.h"
#include "Enclave_u.h"
#include <thread>
// 该函数调用了标准的C++11功能。
// 这个函数是互斥锁演示的一部分
void demo_counter_without_mutex()
{
sgx_status_t ret = SGX_ERROR_UNEXPECTED;
// ecall调用不使用保护的互斥锁计数演示
ret = ecall_mutex_demo_no_protection(global_eid);
if (ret != SGX_SUCCESS)
abort(); // 如果调用失败,程序终止
}
// 这个函数是互斥锁演示的一部分
void demo_counter_mutex()
{
sgx_status_t ret = SGX_ERROR_UNEXPECTED;
// ecall调用使用保护的互斥锁计数演示
ret = ecall_mutex_demo(global_eid);
if (ret != SGX_SUCCESS)
abort(); // 如果调用失败,程序终止
}
// 这个函数是条件变量演示的处理线程使用
void demo_cond_var_run()
{
sgx_status_t ret = SGX_ERROR_UNEXPECTED;
// ecall调用条件变量运行演示
ret = ecall_condition_variable_run(global_eid);
if (ret != SGX_SUCCESS)
abort(); // 如果调用失败,程序终止
}
// 这个函数是条件变量演示的加载线程使用
void demo_cond_var_load()
{
sgx_status_t ret = SGX_ERROR_UNEXPECTED;
// ecall调用条件变量加载演示
ret = ecall_condition_variable_load(global_eid);
if (ret != SGX_SUCCESS)
abort(); // 如果调用失败,程序终止
}
// 示例展示了C++11库和编译器特性,被App.cpp中main函数调用
void ecall_libcxx_functions(void)
{
sgx_status_t ret = SGX_ERROR_UNEXPECTED;
// lambda函数特性的示例
ret = ecall_lambdas_demo(global_eid);
if (ret != SGX_SUCCESS)
abort(); // 如果调用失败,程序终止
// auto特性的示例
ret = ecall_auto_demo(global_eid);
if (ret != SGX_SUCCESS)
abort(); // 如果调用失败,程序终止
// decltype特性的示例
ret = ecall_decltype_demo(global_eid);
if (ret != SGX_SUCCESS)
abort(); // 如果调用失败,程序终止
// 强类型枚举特性的示例
ret = ecall_strongly_typed_enum_demo(global_eid);
if (ret != SGX_SUCCESS)
abort(); // 如果调用失败,程序终止
// 基于范围的for循环特性的示例
ret = ecall_range_based_for_loops_demo(global_eid);
if (ret != SGX_SUCCESS)
abort(); // 如果调用失败,程序终止
// 静态断言特性的示例
ret = ecall_static_assert_demo(global_eid);
if (ret != SGX_SUCCESS)
abort(); // 如果调用失败,程序终止
// 虚函数控制特性的示例:override, final, default, and delete
ret = ecall_virtual_function_control_demo(global_eid);
if (ret != SGX_SUCCESS)
abort(); // 如果调用失败,程序终止
// 委派构造函数特性的示例
ret = ecall_delegating_constructors_demo(global_eid);
if (ret != SGX_SUCCESS)
abort(); // 如果调用失败,程序终止
// std::function特性的示例
ret = ecall_std_function_demo(global_eid);
if (ret != SGX_SUCCESS)
abort(); // 如果调用失败,程序终止
// 算法特性的示例(std::all_of, std::any_of, std::none_of)
ret = ecall_cxx11_algorithms_demo(global_eid);
if (ret != SGX_SUCCESS)
abort(); // 如果调用失败,程序终止
// 可变参数模板特性的示例
ret = ecall_variadic_templates_demo(global_eid);
if (ret != SGX_SUCCESS)
abort(); // 如果调用失败,程序终止
// SFINAE特性的示例
ret = ecall_SFINAE_demo(global_eid);
if (ret != SGX_SUCCESS)
abort(); // 如果调用失败,程序终止
// 初始化列表特性的示例
ret = ecall_initializer_list_demo(global_eid);
if (ret != SGX_SUCCESS)
abort(); // 如果调用失败,程序终止
// 右值引用特性的示例
ret = ecall_rvalue_demo(global_eid);
if (ret != SGX_SUCCESS)
abort(); // 如果调用失败,程序终止
// nullptr特性的示例
ret = ecall_nullptr_demo(global_eid);
if (ret != SGX_SUCCESS)
abort(); // 如果调用失败,程序终止
// 枚举类特性的示例
ret = ecall_enum_class_demo(global_eid);
if (ret != SGX_SUCCESS)
abort(); // 如果调用失败,程序终止
// 新容器类特性的示例(unordered_set, unordered_map, unordered_multiset, 和 unordered_multimap)
ret = ecall_new_container_classes_demo(global_eid);
if (ret != SGX_SUCCESS)
abort(); // 如果调用失败,程序终止
// 元组特性的示例
ret = ecall_tuple_demo(global_eid);
if (ret != SGX_SUCCESS)
abort(); // 如果调用失败,程序终止
// shared_ptr特性的示例
ret = ecall_shared_ptr_demo(global_eid);
if (ret != SGX_SUCCESS)
abort(); // 如果调用失败,程序终止
// 原子操作特性的示例
ret = ecall_atomic_demo(global_eid);
if (ret != SGX_SUCCESS)
abort(); // 如果调用失败,程序终止
// 以下线程是互斥锁演示的一部分
std::thread t1(demo_counter_without_mutex);
std::thread t2(demo_counter_without_mutex);
std::thread t3(demo_counter_without_mutex);
t1.join();
t2.join();
t3.join();
ret = ecall_print_final_value_no_protection(global_eid);
if (ret != SGX_SUCCESS)
abort(); // 如果调用失败,程序终止
// 以下线程是互斥锁演示的一部分
std::thread tm1(demo_counter_mutex);
std::thread tm2(demo_counter_mutex);
std::thread tm3(demo_counter_mutex);
tm1.join();
tm2.join();
tm3.join();
ret = ecall_print_final_value_mutex_demo(global_eid);
if (ret != SGX_SUCCESS)
abort(); // 如果调用失败,程序终止
// 以下线程是条件变量演示的一部分
std::thread th1(demo_cond_var_run);
std::thread th2(demo_cond_var_load);
th2.join();
th1.join();
}
ecall_libcxx_functions
函数:该函数展示了C++11的新特性和库功能。包括lambda表达式、auto、decltype、强类型枚举、基于范围的for循环、静态断言、虚函数控制、委派构造函数、std::function、算法、可变参数模板、SFINAE、初始化列表、右值引用、nullptr、枚举类、新容器类、元组、shared_ptr和原子操作等。函数通过调用一系列封装在Enclave中的示例函数,展示了这些特性。demo_counter_without_mutex
和demo_counter_mutex
函数:演示了使用和不使用互斥锁进行计数的区别。demo_cond_var_run
和demo_cond_var_load
函数:演示了条件变量的使用。
上述ecall开头的函数主要通过edl文件中定义的接口调用Enclave/TrustedLibrary/Libcxx.cpp
中的函数。下面介绍Enclave.edl
文件内容。
1.2.3 Enclave.edl
Enclave.edl原始文件内容如下:
/* Enclave.edl - Top EDL file. */
enclave {
/* Import ECALL/OCALL from sub-directory EDLs.
* [from]: specifies the location of EDL file.
* [import]: specifies the functions to import,
* [*]: implies to import all functions.
*/
from "TrustedLibrary/Libcxx.edl" import *;
from "sgx_tstdc.edl" import *;
/*
* ocall_print_string - invokes OCALL to display string buffer inside the enclave.
* [in]: copy the string buffer to App outside.
* [string]: specifies 'str' is a NULL terminated buffer.
*/
untrusted {
void ocall_print_string([in, string] const char *str);
};
};
可以看出,上面的原文件中只定义了untrusted
部分的函数接口。而对于trusted
部分的函数接口则是通过从sub-directory EDLs Import
的方式导入的,类似于c语言的Include的头文件导入。将导入放进来之后,最终形成的完整的 Enclave.edl
文件内容如下:
/* Enclave.edl - Top EDL file. */
enclave {
trusted {
public void ecall_lambdas_demo(void);
public void ecall_auto_demo(void);
public void ecall_decltype_demo(void);
public void ecall_strongly_typed_enum_demo(void);
public void ecall_range_based_for_loops_demo(void);
public void ecall_static_assert_demo(void);
public void ecall_virtual_function_control_demo(void);
public void ecall_delegating_constructors_demo(void);
public void ecall_std_function_demo(void);
public void ecall_cxx11_algorithms_demo(void);
public void ecall_variadic_templates_demo(void);
public void ecall_SFINAE_demo(void);
public void ecall_initializer_list_demo(void);
public void ecall_rvalue_demo(void);
public void ecall_nullptr_demo(void);
public void ecall_enum_class_demo(void);
public void ecall_new_container_classes_demo(void);
public void ecall_tuple_demo(void);
public void ecall_shared_ptr_demo(void);
public void ecall_atomic_demo(void);
public void ecall_mutex_demo(void);
public void ecall_print_final_value_mutex_demo(void);
public void ecall_mutex_demo_no_protection(void);
public void ecall_print_final_value_no_protection(void);
public void ecall_condition_variable_run(void);
public void ecall_condition_variable_load(void);
};
/*
* ocall_print_string - invokes OCALL to display string buffer inside the enclave.
* [in]: copy the string buffer to App outside.
* [string]: specifies 'str' is a NULL terminated buffer.
*/
untrusted {
void ocall_print_string([in, string] const char *str);
};
};
edl
中定义的这些函数接口规定了REE和TEE之间交互的函数接口,trusted中定义的这些函数就是上面在App/TrustedLibrary/Libcxx.cpp
中调用的那些函数。而ocall_print_string([in, string] const char *str)
就是enclave主动调用的外部接口,将在Enclave/Enclave.cpp
调用。下面给出这些ecall函数的实现源码,在Enclave/TrustedLibrary/Libcxx.cpp
路径文件中。
1.2.4 Enclave/TrustedLibrary/Libcxx.cpp
#include <string>
#include <vector>
#include <iterator>
#include <typeinfo>
#include <functional>
#include <algorithm>
#include <unordered_set>
#include <unordered_map>
#include <initializer_list>
#include <tuple>
#include <memory>
#include <atomic>
#include <mutex>
#include <condition_variable>
#include <map>
#include "../Enclave.h" // 引入上层 Enclave 相关的头文件
#include "Enclave_t.h" // 引入自动生成的 Enclave受信部分的头文件
// Feature name : Lambda functions
// Feature description : 用于创建可以捕获作用域中变量的函数对象
// Demo description : 显示 lambda 捕获选项和一些基本用法
void ecall_lambdas_demo()
{
// Lambdas 捕获选项:
int local_var = 0;
[] { return true; }; // 不捕获任何变量
[&] { return ++local_var; }; // 通过引用捕获所有变量
[&local_var] { return ++local_var; }; // 通过引用捕获 local_var
[&, local_var] { return local_var; }; // 通过引用捕获所有变量,local_var 通过值捕获
[=] { return local_var; }; // 通过值捕获所有变量
[local_var] { return local_var; }; // 通过值捕获 local_var
[=, &local_var] { return ++local_var; }; // 通过值捕获所有变量,local_var 通过引用捕获
// 使用 Lambdas 的示例:
std::vector<int> v { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
printf("[Lambdas] 初始数组使用 lambdas: { ");
// 使用 Lambdas 打印数组中的元素
std::for_each(std::begin(v), std::end(v), [](int elem) { printf("%d ", elem); }); // 捕获规范
printf("}.\n");
// 使用 Lambda 作为一元谓词调用 find_if,找到第一个奇数
auto first_odd_element = std::find_if(std::begin(v), std::end(v), [=](int elem) { return elem % 2 == 1; });
if (first_odd_element != std::end(v))
printf("[Lambdas] 数组中的第一个奇数是 %d. \n", *first_odd_element);
else
printf("[Lambdas] 在数组中未找到奇数。\n");
// 使用 Lambda 作为一元谓词调用 count_if,计算偶数的数量
long long number_of_even_elements = std::count_if(std::begin(v), std::end(v), [=](int val) { return val % 2 == 0; });
printf("[Lambdas] 数组中的偶数数量是 %lld.\n", number_of_even_elements);
// 使用 Lambdas 对数组元素进行排序
std::sort(std::begin(v), std::end(v), [](int e1, int e2) { return e2 < e1; });
// 使用 Lambdas 打印排序后的数组元素
printf("[Lambdas] 排序后的数组: { ");
std::for_each(std::begin(v), std::end(v), [](int elem) { printf("%d ", elem); });
printf("}. \n");
printf("\n"); // 演示结束
}
// ecall_auto_demo 的辅助函数:
void sample_func_auto_demo()
{
printf("[auto] 调用函数 sample_func_auto_demo.\n");
}
// Feature name : auto
// Feature description : 用于自动类型推导
// Demo description : 显示 auto 关键字在不同类型下的基本用法
void ecall_auto_demo()
{
double local_var = 0.0;
auto a = 7; // 自动推导变量 a 的类型为 int
printf("[auto] a 的类型是 int. typeid = %s.\n", typeid(a).name());
const auto b1 = local_var, *b2 = &local_var; // auto 可以与 const 或 & 一起使用
printf("[auto] b1 的类型是 const double. typeid = %s.\n", typeid(b1).name());
printf("[auto] b2 的类型是 const double*. typeid = %s.\n", typeid(b2).name());
(void)b1;
(void)b2;
auto c = 0, *d = &a; // 当推导类型相同时可以初始化多个变量
printf("[auto] c 的类型是 int. typeid = %s.\n", typeid(c).name());
printf("[auto] d 的类型是 int*. typeid = %s.\n", typeid(d).name());
(void)c;
(void)d;
auto lambda = [] {}; // 可以用来定义 lambdas
printf("[auto] lambda 的类型是 [] {}. typeid = %s.\n", typeid(lambda).name());
(void)lambda;
auto func = sample_func_auto_demo; // 可以用来推导函数类型
printf("[auto] func 的类型是 void(__cdecl*)(void). typeid = %s.\n", typeid(func).name());
func();
printf("\n"); // 演示结束
}
// Feature name : decltype
// Feature description : 用于类型推导
// Demo description : 显示 decltype 关键字在不同类型下的基本用法
void ecall_decltype_demo()
{
int a = 0 ;
decltype(a) b = 0; // 声明同类型变量 b
printf("[decltype] b 的类型是 int. typeid = %s.\n", typeid(b).name());
double c = 0;
decltype(a + c) sum = a + c; // 推导不同类型变量相加的结果类型
// 在模板中很常用
printf("[decltype] sum 的类型是 double. typeid = %s.\n", typeid(sum).name());
(void)sum;
(void)b;
printf("\n"); // 演示结束
}
// Feature name : enum classes
// Feature description : 新的枚举类型,解决旧枚举类型中的问题:
// 枚举值的范围未限定以及与 int 类型的比较可能性
// Demo description : 显示 enum classes 的基本用法
void ecall_strongly_typed_enum_demo()
{
// 在 enum class 中可以设定底层类型。这里设定为 char。
enum class DaysOfWeek : char { MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY };
// 初始化 DaysOfWeek 类型的变量
DaysOfWeek random_day = DaysOfWeek::MONDAY;
(void)random_day;
// 不强制规定底层类型
enum class Weekend { SATURDAY, SUNDAY };
}
// Feature name : Range based for loops
// Feature description : 方便读取容器中的元素
// Demo description : 显示在 C 数组和 vector 中的基本用法
void ecall_range_based_for_loops_demo()
{
char array_of_letters[] = { 'a', 'b', 'c', 'd' };
std::vector<char> vector_of_letters = { 'a', 'b', 'c', 'd' };
printf("[range_based_for_loops] 使用 range based for loop 打印数组内容: { ");
for (auto elem : array_of_letters)
printf("%c ", elem);
printf("}. \n");
printf("[range_based_for_loops] 使用 range based for loop 打印 vector 内容: { ");
for (auto elem : vector_of_letters)
printf("%c ", elem);
printf("}.\n");
printf("\n"); // 演示结束
}
// Feature name : static_assert
// Feature description : 用于编译时断言
// Demo description : 显示 static_assert 在编译时操作中的基本用法
void ecall_static_assert_demo()
{
static_assert(sizeof(int) < sizeof(double), "错误:sizeof(int) < sizeof(double)");
const int a = 0;
static_assert(a == 0, "错误:a 的值不为 0");
}
// Feature name : New virtual function controls : override, final, default, and delete
// Feature description : - delete : 删除的函数不能被继承
// - final : 使用 final 修饰的函数不能被子类重写
// - default : 告知编译器生成一个默认函数
// - override : 确保派生类中的虚函数重写基类中的函数
// Demo description : 显示新虚函数控制的基本用法
/* ecall_virtual_function_control_demo 的辅助类 */
class Base
{
public:
virtual void f_cannot_be_inherited() final {}; // 使用 final 修饰,不能被继承的方法
Base(const Base &) = delete; // 删除拷贝构造函数,不允许拷贝
Base() = default; // 默认构造函数
virtual void f_must_be_overridden() {}; // 需要在派生类中重写的方法
virtual ~Base() {}; // 虚析构函数,保证派生类对象的正确析构
};
/* ecall_virtual_function_control_demo 的辅助类 */
class Derived : Base
{
public:
/* 注释中的代码无法编译
* 由于基类中的方法使用了 final 关键字,所以子类不能重写这个方法
* virtual double f_cannot_be_inherited() {};
*/
/* 关键字 override 确保此函数重写了基类成员函数 */
virtual void f_must_be_overridden() override {};
};
void ecall_virtual_function_control_demo()
{
// 使用默认构造函数生成对象
Base a;
// 尝试使用拷贝构造函数,此处代码会因该函数被删除而无法编译
// Base b = a;
}
// Feature name : Delegating constructors
// Feature description : 类的构造函数可能具有相同的代码,可以委托给另一个构造函数以避免代码重复
// Demo description : 显示委托构造函数的基本使用方法
// ecall_delegating_constructors_demo 的辅助类
class DemoDelegatingConstructors
{
int a, b, c;
public:
DemoDelegatingConstructors(int param_a, int param_b, int param_c)
{
this->a = param_a;
this->b = param_b;
this->c = param_c;
/* 公共初始化 */
switch (c)
{
case 1:
printf("[delegating constructors] 调用自 DemoDelegatingConstructors(int a, int b).\n");
break;
case 2:
printf("[delegating constructors] 调用自 DemoDelegatingConstructors(int a).\n");
break;
default:
printf("[delegating constructors] 调用自 DemoDelegatingConstructors(int a, int b, int c).\n");
break;
}
}
DemoDelegatingConstructors(int param_a, int param_b) : DemoDelegatingConstructors(param_a, param_b, 1) {}
DemoDelegatingConstructors(int param_a) : DemoDelegatingConstructors(param_a, 0, 2) {}
};
void ecall_delegating_constructors_demo()
{
DemoDelegatingConstructors a(1, 2, 3);
DemoDelegatingConstructors b(1, 2);
DemoDelegatingConstructors c(1);
printf("\n"); // 演示结束
}
void sample_std_function1() // ecall_std_function_demo 的辅助函数
{
printf("[std_function] 调用 sample_std_function1\n");
}
// Feature name : std::function
// Feature description : 用于存储和调用可调用对象
// Demo description : 显示 std::function 的基本使用方法
void ecall_std_function_demo()
{
// 函数示例
std::function<void()> funct = sample_std_function1;
funct();
// Lambda 示例
std::function<void()> funct_lambda = [] { printf("[std_function] 调用一个 lambda 函数\n"); };
funct_lambda();
printf("\n"); // 演示结束
}
// Feature name : std::all_of, std::any_of, std::none_of
// Feature description : 新的 C++11 算法
// Demo description : 显示 std::all_of, std::any_of 和 std::none_of 的基本使用方法
void ecall_cxx11_algorithms_demo()
{
std::vector<int> v = { 0, 1, 2, 3, 4, 5 };
bool are_all_of = all_of(begin(v), end(v), [](int e) { return e % 2 == 0; });
printf("[cxx11_algorithms] 所有元素在 { 0 1 2 3 4 5 } 中都是偶数是 %s.\n", are_all_of ? "true" : "false");
bool are_any_of = any_of(begin(v), end(v), [](int e) { return e % 2 == 0; });
printf("[cxx11_algorithms] { 0 1 2 3 4 5 } 中的一些元素为偶数是 %s.\n", are_any_of ? "true" : "false");
bool are_none_of = none_of(begin(v), end(v), [](int e) { return e % 2 == 0; });
printf("[cxx11_algorithms] { 0 1 2 3 4 5 } 中无元素为偶数是 %s.\n", are_none_of ? "true" : "false");
printf("\n"); // 演示结束
}
template<typename T> // ecall_variadic_templates_demo 的辅助模板
T sum(T elem)
{
return elem;
}
// Feature name : variadic templates
// Feature description : 可以有多个参数的模板
// Demo description : 显示变参模板的基本使用方法
template<typename T, typename... Args>
T sum(T elem1, T elem2, Args... args)
{
return elem1 + elem2 + sum(args...);
}
void ecall_variadic_templates_demo()
{
int computed_sum = sum(1, 2, 3, 4, 5);
printf("[variadic_templates] 参数 (1, 2, 3, 4, 5) 的和是 %d.\n", computed_sum);
printf("\n"); // 演示结束
}
// Feature name : Substitution failure is not an error (SFINAE)
// Feature description : 说明模板中替换错误不导致错误的情况
// Demo description : 显示 SFINAE 的基本使用方法
/*第一个替换候选者*/
template <typename T> void f(typename T::A*) { printf("[sfinae] 第一个替换候选者匹配.\n"); };
/*第二个替换候选者*/
template <typename T> void f(T) { printf("[sfinae] 第二个替换候选者匹配.\n"); }
void ecall_SFINAE_demo()
{
f<int>(0x0); // 尽管第一个候选者替换失败,第二个候选者仍然匹配
printf("\n"); // 演示结束
}
// Feature name : Initializer lists
// Feature description : 一个 std::initializer_list<T> 类型的对象是一个轻量级代理对象,提供对 const T 类型对象数组的访问
// Demo description : 演示在构造函数中使用初始化列表的用法
class Number
{
public:
Number(const std::initializer_list<int> &v) {
for (auto i : v) {
elements.push_back(i);
}
}
void print_elements() {
printf("[initializer_list] 向量中的元素是:");
for (auto item : elements) {
printf(" %d", item);
}
printf(".\n");
}
private:
std::vector<int> elements;
};
void ecall_initializer_list_demo()
{
printf("[initializer_list] 在构造函数中使用初始化列表.\n");
Number m = {10, 9, 8, 7, 6, 5, 4, 3, 2, 1};
m.print_elements();
printf("\n"); //演示结束
}
// ecall_rvalue_demo 的辅助类
class DemoBuffer
{
public:
unsigned int size = 100;
char *buffer;
DemoBuffer(int param_size)
{
this->size = param_size;
buffer = new char[size];
printf("[rvalue] 调用构造函数 : DemoBuffer(int size).\n");
}
// 传统拷贝构造函数需要为新副本分配内存
// 拷贝一个大数组是一个耗时操作
DemoBuffer(const DemoBuffer &rhs)
{
this->size = rhs.size;
buffer = new char[rhs.size];
memcpy(buffer, rhs.buffer, size);
printf("[rvalue] 调用拷贝构造函数 : DemoBuffer(const DemoBuffer &rhs).\n");
}
// 典型的移动构造函数可以重用 buffer 指向的内存
DemoBuffer(DemoBuffer &&rhs)
{
buffer = rhs.buffer;
size = rhs.size;
// 重置 rhs 的状态
rhs.buffer = NULL;
rhs.size = 0;
printf("[rvalue] 调用移动构造函数 : DemoBuffer(DemoBuffer && rhs).\n");
}
~DemoBuffer()
{
delete [] buffer;
}
};
// ecall_rvalue_demo 的辅助函数
DemoBuffer foobar(int a)
{
DemoBuffer x(100);
DemoBuffer y(100);
if (a > 0)
return x;
else
return y;
}
// Feature name : Rvalue references and move semantics;
// Feature description : 通过消除复制操作来优化内存使用
// Demo description : 显示右值引用、移动构造函数和移动赋值运算符的基本使用方法
void ecall_rvalue_demo()
{
// 这将调用构造函数
printf("[rvalue] DemoBuffer a(100).\n");
DemoBuffer a(100);
printf("[rvalue] 调用 foobar(100). \n");
// 使用临时对象初始化变量 d ,将调用移动构造函数
// 这有助于减少操作的内存开销
DemoBuffer d(foobar(100));
// 这将调用拷贝构造函数。a 的状态不会改变
printf("[rvalue] DemoBuffer b(a).\n");
DemoBuffer b(a);
printf("[rvalue] DemoBuffer c(std::move(a)).\n");
// 显式将 a 转换为右值,以便 c 使用移动构造函数进行创建
// a 的状态将被重置
DemoBuffer c(std::move(a));
printf("\n"); // 演示结束
}
// Feature name : Nullptr
// Feature description : 解决将 NULL 转换为整型的问题
// Demo description : 显示 nullptr 的基本使用方法
// 重载候选 1
void nullptr_overload_candidate(int i) {
(void)i;
printf("[nullptr] 调用了 void nullptr_overload_candidate(int i).\n");
}
// 重载候选 2
void nullptr_overload_candidate(int* ptr) {
(void)ptr;
printf("[nullptr] 调用了 void nullptr_overload_candidate(int* ptr).\n");
}
template<class F, class A>
void Fwd(F f, A a)
{
f(a);
}
void g(int* i)
{
(void)i;
printf("[nullptr] 调用了函数 %s\n", __FUNCTION__);
}
void ecall_nullptr_demo()
{
// NULL 可以转换为整型(如 int),将调用重载候选 1
nullptr_overload_candidate(NULL);
// nullptr 不能转换为整型(如 int),将调用重载候选 2
nullptr_overload_candidate(nullptr);
g(NULL); // 合法
g(0); // 合法
Fwd(g, nullptr); // 合法
//Fwd(g, NULL); // 错误:没有函数 g(int)
printf("\n"); // 演示结束
}
// Feature name : Scoped enums
// Feature description : 新的枚举类型,解决旧枚举类型中的问题
// Demo description : 显示 scoped enums 的基本使用方法
enum class Color { orange, brown, green = 30, blue, red };
void ecall_enum_class_demo()
{
int n = 0;
Color color1 = Color::brown;
switch (color1)
{
case Color::orange: printf("[enum class] orange"); break;
case Color::brown: printf("[enum class] brown"); break;
case Color::green: printf("[enum class] green"); break;
case Color::blue: printf("[enum class] blue"); break;
case Color::red: printf("[enum class] red"); break;
}
// n = color1; // 不允许:没有 scoped enum 到 int 的转换
n = static_cast<int>(color1); // 合法,n = static_cast<int>(Color::brown) = 1
printf(" - int = %d\n", n);
Color color2 = Color::red;
switch (color2)
{
case Color::orange: printf("[enum class] orange"); break;
case Color::brown: printf("[enum class] brown"); break;
case Color::green: printf("[enum class] green"); break;
case Color::blue: printf("[enum class] blue"); break;
case Color::red: printf("[enum class] red"); break;
}
n = static_cast<int>(color2); // 合法,n = static_cast<int>(Color::red) = 32
printf(" - int = %d\n", n);
Color color3 = Color::green;
switch (color3)
{
case Color::orange: printf("[enum class] orange"); break;
case Color::brown: printf("[enum class] brown"); break;
case Color::green: printf("[enum class] green"); break;
case Color::blue: printf("[enum class] blue"); break;
case Color::red: printf("[enum class] red"); break;
}
n = static_cast<int>(color3); // 合法,n = static_cast<int>(Color::green) = 30
printf(" - int = %d\n", n);
printf("\n");
}
// Feature name : new container classes
// Feature description : unordered_set, unordered_map, unordered_multiset 和 unordered_multimap
// Demo description : 显示新容器类的基本使用方法
void ecall_new_container_classes_demo()
{
// unordered_set
// 一个基于哈希表的无序集合,提供快速查找功能
std::unordered_set<int> set_of_numbers = { 0, 1, 2, 3, 4, 5 };
const int searchVal = 3;
std::unordered_set<int>::const_iterator got = set_of_numbers.find(searchVal);
if (got != set_of_numbers.end())
printf("[new_container_classes] unordered_set { 0, 1, 2, 3, 4, 5} 包含值 %d.\n", searchVal);
else
printf("[new_container_classes] unordered_set { 0, 1, 2, 3, 4, 5} 不包含值 %d.\n", searchVal);
// unordered_multiset
// 一个基于哈希表的无序多重集合,可以包含重复元素
std::unordered_multiset<int> multiset_of_numbers = { 0, 1, 2, 3, 3, 3 };
printf("[new_container_classes] unordered_multiset { 0, 1, 2, 3, 3, 3} 包含 %d 个值 %d.\n",
(int)multiset_of_numbers.count(searchVal), searchVal);
// unordered_map
std::unordered_map<std::string, int> grades{ { "A", 10 },{ "B", 8 },{ "C", 7 },{ "D", 5 },{ "E", 3 } };
printf("[new_container_classes] unordered_map 元素: {");
for (auto pair : grades) {
printf("[%s %d] ", pair.first.c_str(), pair.second);
}
printf("}.\n");
// unordered_multimap
std::unordered_multimap<std::string, int> multimap_grades{ { "A", 10 },{ "B", 8 },{ "B", 7 },{ "E", 5 },{ "E", 3 },{ "E",1 } };
printf("[new_container_classes] unordered_multimap 元素: {");
for (auto pair : multimap_grades) {
printf("[%s %d] ", pair.first.c_str(), pair.second);
}
printf("}.\n");
printf("\n"); // 演示结束
}
// Feature name : Tuple
// Feature description : 可以包含多种类型元素的对象,这些元素可以通过索引访问
// Demo description : 显示 tuple 的基本使用,包括创建和访问
void ecall_tuple_demo()
{
// 使用 std::make_tuple 创建 tuple
char array_of_letters[4] = {'A','B','C','D'};
std::vector<char> vector_of_letters = { 'A','B','C','D' };
std::map<char, char> map_of_letters = { {'B','b' } };
// 使用 tuple 构造函数创建 tuple
std::tuple<int, std::string> tuple_sample_with_constructor(42, "Sample tuple");
(void)tuple_sample_with_constructor;
// 使用 std::make_tuple 创建 tuple
auto tuple_sample = std::make_tuple("<TupleSample 的第一个元素>", 1, 7.9, vector_of_letters, array_of_letters, map_of_letters);
// 使用 std::get<索引> 访问 tupleSample 中的元素
printf("[tuple] 显示 TupleSample 中的第一个元素: %s.\n", std::get<0>(tuple_sample));
printf("[tuple] 显示 TupleSample 中的第二个元素: %d.\n", std::get<1>(tuple_sample));
printf("[tuple] 显示 TupleSample 中的第三个元素: %f.\n", std::get<2>(tuple_sample));
// 从 tuple 中获取 vector
std::vector<char> temp_vector = std::get<3>(tuple_sample);
(void)temp_vector;
// 从 tuple 中获取数组
int first_elem_of_array = std::get<4>(tuple_sample)[0];
(void)first_elem_of_array;
// 从 tuple 中获取 map
std::map<char, char> temp_map = std::get<5>(tuple_sample);
(void)temp_map;
printf("\n"); // 演示结束
}
// Feature name : new smart pointers
// Feature description : shared_ptr 和 unique_ptr
// Demo description : 显示智能指针的基本使用
// ecall_shared_ptr_demo 的辅助类
class DemoSmartPtr
{
std::string smartPointerType;
public:
DemoSmartPtr(std::string param_smartPointerType)
{
printf("[smart_ptr] 构造对象 DemoSmartPtr 使用 %s.\n", param_smartPointerType.c_str());
this->smartPointerType = param_smartPointerType;
}
~DemoSmartPtr()
{
printf("[smart_ptr] 析构对象 DemoSmartPtr 使用 %s.\n", smartPointerType.c_str());
}
};
void ecall_shared_ptr_demo()
{
// std::shared_ptr 是一个智能指针,使用引用计数管理对象的生命周期
// 当最后一个智能指针不再指向它时,对象会被释放
// 使用 std::make_shared 创建智能指针
auto shared_ptr = std::make_shared<DemoSmartPtr>("shared_ptr.");
printf("[smart_ptr] shared_ptr 的引用计数 = %ld.\n", shared_ptr.use_count());
auto shared_ptr2 = shared_ptr;
printf("[smart_ptr] 创建另一个智能指针后,shared_ptr 的引用计数 = %ld.\n", shared_ptr.use_count());
shared_ptr2.reset();
printf("[smart_ptr] 释放所有权后,shared_ptr 的引用计数 = %ld.\n", shared_ptr.use_count());
// std::unique_ptr 是一个独占所有权的智能指针
// 与 shared_ptr 不同,一旦独占指针指向对象,其他独占指针不能再指向相同对象
std::unique_ptr<DemoSmartPtr> unique_ptr(new DemoSmartPtr("unique_ptr"));
// 变量超出作用域时,both shared_ptr 和 unique_ptr 释放各自拥有的对象
// 演示结束
}
// Feature name : atomic
// Feature description : atomic 库提供细粒度的原子操作组件,允许无锁并发编程。
// 每个原子操作与涉及同一对象的任何其他原子操作是不可分割的。
// 原子对象是无数据竞争的。
// Demo description : 显示 atomic 类型、对象和函数在信任域中的使用。
void ecall_atomic_demo()
{
printf("[atomic] atomic 类型、对象和函数演示.\n");
printf("[atomic_store] 定义一个初始值为 5 的 atomic_char 对象.\n");
std::atomic_char atc(5);
printf("[atomic_store] atomic 对象中当前存储的值是: %d\n", atc.load());
printf("[atomic_store] 将 atomic 对象的值替换为非原子值 3.\n");
std::atomic_store<char>(&atc, 3);
printf("[atomic_store] atomic 对象的新值是: %d.\n", atc.load());
printf("\n");
printf("[atomic_store_explicit] 定义一个初始值为 5 的 atomic_short 对象.\n");
std::atomic_short ats(5);
printf("[atomic_store_explicit] atomic 对象中当前存储的值是: %d.\n", ats.load());
printf("[atomic_store_explicit] 将 atomic 对象的值替换为非原子值 3.\n");
std::atomic_store_explicit<short>(&ats, 3, std::memory_order_seq_cst);
printf("[atomic_store] atomic 对象的新值是: %d.\n", ats.load());
printf("\n");
printf("[atomic_load] 定义一个初始值为 4 的 atomic_int 对象.\n");
std::atomic_int ati1(4);
printf("[atomic_load] 获取 atomic 对象的值并将其保存到 int 变量中.\n");
int val = std::atomic_load(&ati1);
printf("[atomic_load] 获取的值是 %d.\n", val);
printf("\n");
printf("[atomic_load_explicit] 定义一个初始值为 2 的 atomic_int 对象.\n");
std::atomic_int ati2(2);
printf("[atomic_load_explicit] 获取 atomic 对象的值并将其保存到 int 变量中.\n");
int val1 = std::atomic_load_explicit(&ati2, std::memory_order_seq_cst);
printf("[atomic_load_explicit] 获取的值是 %d.\n", val1);
printf("\n");
printf("[atomic_fetch_add] 定义一个初始值为 7 的 atomic_int 对象.\n");
std::atomic_int ati(7);
printf("[atomic_fetch_add] atomic 对象中当前存储的值是: %d.\n", ati.load());
printf("[atomic_fetch_add] 将非原子值 8 添加到 atomic 对象中.\n");
std::atomic_fetch_add(&ati, 8);
printf("[atomic_fetch_add] atomic 对象的新值是: %d.\n", ati.load());
printf("\n");
printf("[atomic_fetch_add_explicit] 定义一个初始值为 7 的 atomic_uint 对象.\n");
std::atomic_uint atui(7);
printf("[atomic_fetch_add_explicit] atomic 对象中当前存储的值是: %u.\n", atui.load());
printf("[atomic_fetch_add_explicit] 将非原子值 8 添加到 atomic 对象中.\n");
std::atomic_fetch_add_explicit<unsigned int>(&atui, 8, std::memory_order_seq_cst);
printf("[atomic_fetch_add_explicit] atomic 对象的新值是: %u.\n", atui.load());
printf("\n");
printf("[atomic_fetch_sub] 定义一个初始值为 20 的 atomic_long 对象.\n");
std::atomic_long atl(20);
printf("[atomic_fetch_sub] atomic 对象中当前存储的值是: %ld.\n", atl.load());
printf("[atomic_fetch_sub] 从 atomic 对象的值中减去非原子值 8.\n");
std::atomic_fetch_sub<long>(&atl, 8);
printf("[atomic_fetch_sub] atomic 对象的新值是: %ld.\n", atl.load());
printf("\n");
printf("[atomic_fetch_sub_explicit] 定义一个初始值为 20 的 atomic_llong 对象.\n");
std::atomic_llong atll(20);
printf("[atomic_fetch_sub_explicit] atomic 对象中当前存储的值是: %lld.\n", atll.load());
printf("[atomic_fetch_sub_explicit] 从 atomic 对象的值中减去非原子值 8.\n");
std::atomic_fetch_sub_explicit<long long>(&atll, 8, std::memory_order_seq_cst);
printf("[atomic_fetch_sub_explicit] atomic 对象的新值是: %lld.\n", atll.load());
printf("\n"); // 演示结束
}
// Feature name : mutex
// Feature description : mutex 类是一个同步原语,可以用来保护共享数据
// 防止多个线程同时访问数据。
// Demo description : 显示在多个线程中增量值时的 mutex 保护。
// 用于演示没有使用 mutex 的行为的结构
struct CounterWithoutMutex {
int value;
CounterWithoutMutex() : value(0) {}
void increment() {
++value;
}
};
CounterWithoutMutex counter_without_protection;
// 演示没有使用 mutex 保护的计数器增量的 E-call
void ecall_mutex_demo_no_protection()
{
for (int i = 0; i < 100000; ++i) {
counter_without_protection.increment();
}
}
// 用于获取最终值没有使用 mutex 保护的计数器的 E-call
void ecall_print_final_value_no_protection()
{
printf("[mutex] 在三个线程中没有 mutex 保护的增量,使用 100000 次循环。\n[mutex] 预期值是 300000。最终值是 %d.\n", counter_without_protection.value);
}
// 用于演示 mutex 保护的结构
struct CounterProtectedByMutex {
std::mutex mutex;
int value;
CounterProtectedByMutex() : value(0) {}
void increment() {
// 加锁以防止在不同线程中的同时增量
mutex.lock();
++value;
// 释放锁
mutex.unlock();
}
};
CounterProtectedByMutex counter_with_protection;
// 演示实际增量操作的 E-call
void ecall_mutex_demo()
{
for (int i = 0; i < 100000; ++i) {
counter_with_protection.increment();
}
}
// 用于获取最终值有 mutex 保护的计数器的 E-call
void ecall_print_final_value_mutex_demo()
{
printf("[mutex] 在三个线程中使用 mutex 保护的增量,使用 100000 次循环。\n[mutex] 预期值是 300000。最终值是 %d.\n", counter_with_protection.value);
}
// Feature name : condition_variable
// Feature description: condition_variable 类是一个同步原语,可以用来阻塞一个线程,
// 或同时阻塞多个线程,直到另一个线程修改共享变量(条件)
// 并通知 condition_variable。
// Demo description : 演示在两个线程环境中使用 condition_variable。一个线程用于加载数据,
// 另一个线程处理加载的数据。处理数据的线程等待数据加载线程完成加载,
// 并在加载完成后收到通知。
class DemoConditionVariable // condition_variable 演示使用的类
{
std::mutex mtx;
std::condition_variable cond_var;
bool data_loaded;
public:
DemoConditionVariable()
{
data_loaded = false;
}
void load_data()
{
// 模拟数据加载
printf("[condition_variable] 正在加载数据...\n");
{
// 加锁数据结构
std::lock_guard<std::mutex> guard(mtx);
// 设置标志为 true,表示数据加载完成
data_loaded = true;
}
// 通知解锁等待中的线程
cond_var.notify_one();
}
bool is_data_loaded()
{
return data_loaded;
}
void main_task()
{
printf("\n");
printf("[condition_variable] 运行 condition variable 演示。\n");
// 获取锁
std::unique_lock<std::mutex> lck(mtx);
printf("[condition_variable] 正在等待另一个线程加载数据。\n");
cond_var.wait(lck, std::bind(&DemoConditionVariable::is_data_loaded, this));
printf("[condition_variable] 处理已加载的数据。\n");
printf("[condition_variable] 完成。\n");
}
};
DemoConditionVariable app;
// condition_variable 演示用到的 E-call - 数据处理线程
void ecall_condition_variable_run()
{
app.main_task();
}
// condition_variable 演示用到的 E-call - 数据加载线程
void ecall_condition_variable_load()
{
app.load_data();
}
1.2.5 Enclave/Enclave.cpp+Enclave.h
- Enclave.h
#ifndef _ENCLAVE_H_
#define _ENCLAVE_H_
#include <stdlib.h>
#include <assert.h>
#if defined(__cplusplus)
extern "C" {
#endif
// printf函数,调用REE中OCALL函数完成打印,很重要,自己调试TEE程序时需要打印!!!
int printf(const char *fmt, ...);
#if defined(__cplusplus)
}
#endif
#endif /* !_ENCLAVE_H_ */
- Enclave.cpp
#include <stdarg.h>
#include <stdio.h> /* vsnprintf */
#include "Enclave.h"
#include "Enclave_t.h" /* print_string */
/*
* printf:
* Invokes OCALL to display the enclave buffer to the terminal.
*/
int printf(const char *fmt, ...)
{
char buf[BUFSIZ] = {'\0'};
va_list ap;
va_start(ap, fmt);
vsnprintf(buf, BUFSIZ, fmt, ap);
va_end(ap);
ocall_print_string(buf); // 在App.cpp中定义的:ocall_print_string(const char *str)
return 0;
}
这里面需要注意的只有一点,就是我们在调试enclave中程序时,printf
是一个重要的调试手段,但是在enclave无法直接调用printf函数,也不安全,因此此处采用OCALL
函数调用的方式将需要打印的内容通过buffer
传入到REE中打印出来,值得我们在后续程序设计中借鉴!!!
1.3 编译执行
make
./app
执行结果:
[Lambdas] 初始数组使用 lambdas: { 0 1 2 3 4 5 6 7 8 9 10 }.
[Lambdas] 数组中的第一个奇数是 1.
[Lambdas] 数组中的偶数数量是 6.
[Lambdas] 排序后的数组: { 10 9 8 7 6 5 4 3 2 1 0 }.
[auto] a 的类型是 int. typeid = i.
[auto] b1 的类型是 const double. typeid = d.
[auto] b2 的类型是 const double*. typeid = PKd.
[auto] c 的类型是 int. typeid = i.
[auto] d 的类型是 int*. typeid = Pi.
[auto] lambda 的类型是 [] {}. typeid = *Z15ecall_auto_demoEUlvE_.
[auto] func 的类型是 void(__cdecl*)(void). typeid = PFvvE.
[auto] 调用函数 sample_func_auto_demo.
[decltype] b 的类型是 int. typeid = i.
[decltype] sum 的类型是 double. typeid = d.
[range_based_for_loops] 使用 range based for loop 打印数组内容: { a b c d }.
[range_based_for_loops] 使用 range based for loop 打印 vector 内容: { a b c d }.
[delegating constructors] 调用自 DemoDelegatingConstructors(int a, int b, int c).
[delegating constructors] 调用自 DemoDelegatingConstructors(int a, int b).
[delegating constructors] 调用自 DemoDelegatingConstructors(int a).
[std_function] 调用 sample_std_function1
[std_function] 调用一个 lambda 函数
[cxx11_algorithms] 所有元素在 { 0 1 2 3 4 5 } 中都是偶数是 false.
[cxx11_algorithms] { 0 1 2 3 4 5 } 中的一些元素为偶数是 true.
[cxx11_algorithms] { 0 1 2 3 4 5 } 中无元素为偶数是 false.
[variadic_templates] 参数 (1, 2, 3, 4, 5) 的和是 15.
[sfinae] 第二个替换候选者匹配.
[initializer_list] 在构造函数中使用初始化列表.
[initializer_list] 向量中的元素是: 10 9 8 7 6 5 4 3 2 1.
[rvalue] DemoBuffer a(100).
[rvalue] 调用构造函数 : DemoBuffer(int size).
[rvalue] 调用 foobar(100).
[rvalue] 调用构造函数 : DemoBuffer(int size).
[rvalue] 调用构造函数 : DemoBuffer(int size).
[rvalue] 调用移动构造函数 : DemoBuffer(DemoBuffer && rhs).
[rvalue] DemoBuffer b(a).
[rvalue] 调用拷贝构造函数 : DemoBuffer(const DemoBuffer &rhs).
[rvalue] DemoBuffer c(std::move(a)).
[rvalue] 调用移动构造函数 : DemoBuffer(DemoBuffer && rhs).
[nullptr] 调用了 void nullptr_overload_candidate(int i).
[nullptr] 调用了 void nullptr_overload_candidate(int* ptr).
[nullptr] 调用了函数 g
[nullptr] 调用了函数 g
[nullptr] 调用了函数 g
[enum class] brown - int = 1
[enum class] red - int = 32
[enum class] green - int = 30
[new_container_classes] unordered_set { 0, 1, 2, 3, 4, 5} 包含值 3.
[new_container_classes] unordered_multiset { 0, 1, 2, 3, 3, 3} 包含 3 个值 3.
[new_container_classes] unordered_map 元素: {[E 3] [D 5] [C 7] [B 8] [A 10] }.
[new_container_classes] unordered_multimap 元素: {[E 5] [E 3] [E 1] [B 8] [B 7] [A 10] }.
[tuple] 显示 TupleSample 中的第一个元素: <TupleSample 的第一个元素>.
[tuple] 显示 TupleSample 中的第二个元素: 1.
[tuple] 显示 TupleSample 中的第三个元素: 7.900000.
[smart_ptr] 构造对象 DemoSmartPtr 使用 shared_ptr..
[smart_ptr] shared_ptr 的引用计数 = 1.
[smart_ptr] 创建另一个智能指针后,shared_ptr 的引用计数 = 2.
[smart_ptr] 释放所有权后,shared_ptr 的引用计数 = 1.
[smart_ptr] 构造对象 DemoSmartPtr 使用 unique_ptr.
[smart_ptr] 析构对象 DemoSmartPtr 使用 unique_ptr.
[smart_ptr] 析构对象 DemoSmartPtr 使用 shared_ptr..
[atomic] atomic 类型、对象和函数演示.
[atomic_store] 定义一个初始值为 5 的 atomic_char 对象.
[atomic_store] atomic 对象中当前存储的值是: 5
[atomic_store] 将 atomic 对象的值替换为非原子值 3.
[atomic_store] atomic 对象的新值是: 3.
[atomic_store_explicit] 定义一个初始值为 5 的 atomic_short 对象.
[atomic_store_explicit] atomic 对象中当前存储的值是: 5.
[atomic_store_explicit] 将 atomic 对象的值替换为非原子值 3.
[atomic_store] atomic 对象的新值是: 3.
[atomic_load] 定义一个初始值为 4 的 atomic_int 对象.
[atomic_load] 获取 atomic 对象的值并将其保存到 int 变量中.
[atomic_load] 获取的值是 4.
[atomic_load_explicit] 定义一个初始值为 2 的 atomic_int 对象.
[atomic_load_explicit] 获取 atomic 对象的值并将其保存到 int 变量中.
[atomic_load_explicit] 获取的值是 2.
[atomic_fetch_add] 定义一个初始值为 7 的 atomic_int 对象.
[atomic_fetch_add] atomic 对象中当前存储的值是: 7.
[atomic_fetch_add] 将非原子值 8 添加到 atomic 对象中.
[atomic_fetch_add] atomic 对象的新值是: 15.
[atomic_fetch_add_explicit] 定义一个初始值为 7 的 atomic_uint 对象.
[atomic_fetch_add_explicit] atomic 对象中当前存储的值是: 7.
[atomic_fetch_add_explicit] 将非原子值 8 添加到 atomic 对象中.
[atomic_fetch_add_explicit] atomic 对象的新值是: 15.
[atomic_fetch_sub] 定义一个初始值为 20 的 atomic_long 对象.
[atomic_fetch_sub] atomic 对象中当前存储的值是: 20.
[atomic_fetch_sub] 从 atomic 对象的值中减去非原子值 8.
[atomic_fetch_sub] atomic 对象的新值是: 12.
[atomic_fetch_sub_explicit] 定义一个初始值为 20 的 atomic_llong 对象.
[atomic_fetch_sub_explicit] atomic 对象中当前存储的值是: 20.
[atomic_fetch_sub_explicit] 从 atomic 对象的值中减去非原子值 8.
[atomic_fetch_sub_explicit] atomic 对象的新值是: 12.
[mutex] 在三个线程中没有 mutex 保护的增量,使用 100000 次循环。
[mutex] 预期值是 300000。最终值是 103921.
[mutex] 在三个线程中使用 mutex 保护的增量,使用 100000 次循环。
[mutex] 预期值是 300000。最终值是 300000.
[condition_variable] 正在加载数据...
[condition_variable] 运行 condition variable 演示。
[condition_variable] 正在等待另一个线程加载数据。
[condition_variable] 处理已加载的数据。
[condition_variable] 完成。
Info: Cxx11DemoEnclave successfully returned.
1.4 总结
官方的例子功能十分丰富,我们开发者要做的就是从例子中学习如何在enclave中开发自己想要的功能,形成标准的模块方法,并参考例子中的一些标准用法,后续将更新更多例子,展示更多功能,逐渐形成自己的enclave代码库!
示例二. Cxx14SGXDemo
基本流程与上一致,后续补充c++14新特性在enclave中的执行测试…
示例三. Cxx17SGXDemo
基本流程与上一致,后续补充c++17新特性在enclave中的执行测试…
四. 参考链接
Intel-SGX:SampleCode/Cxx11SGXDemo
五. 感谢支持
完结撒花!后续将持续输出,形成Intel SGX的系列教程,并结合密码学算法设计更复杂功能。希望看到这里的小伙伴能点个关注,也欢迎志同道合的小伙伴一起广泛交流。
码字实属不易,如果本文对你有10分帮助,就赏个10分把,感谢各位大佬支持!