0%

安装 VMware workstation 15 player 时,提示:

VMware workstation 15 player 和 Hyper-V 在此版本的 windows 上不兼容。

请从系统中移除 hyper-v 角色或将 windows 更新到 windows 10 版本 20H1。

我选择了在“启用或关闭 windows 功能”中关闭 Hyper-V,需要重启。后续启动虚拟机的时候还是报错:

您的主机不满足在启用 Hyper-V 或 Device/Credential Guard 的情况下运行 VMware Workstation 的最低要求。

参考帖子 :在禁用 Device/Credential Guard 后,可以运行 VM 的方法 , 重启生效。

阅读全文 »

最近两会,科学上网又宕机了。打算专心学习一下 v2ray 的插件和配置,此笔记用于备忘操作过程中的细节。也涉及 linux 系统的基础操作。

为了安全(和方便),提出以下要求或目标:禁用账号密码登录,全部改用密钥。 建立 vps 后的第一步

关于 v2ray xray v2fly,可以看看 V2Ray、Trojan、XRay

阅读全文 »

建立 vps 后(主要用于科学上网)首先要保证后续的使用安全。否则再多的功能都是空中楼阁。

SSH 登录

非对称密钥算法的作用分为两种场景:

  • 加密数据:公钥加密,私钥解密。数据由前者发往后者,数据是安全的。
  • 认证:私钥加密,公钥解密。后者验证前者的身份,传输数据的安全性是没有保证的。

那么远程登录呢?

SSH 本身提供两种级别的验证方法:基于口令的安全验证;基于密钥的安全验证。引用来源

阅读全文 »

在工作中,遇到的几个关于构造函数的问题,以及语言边缘的坑(如何避开)。

显式类型转换函数和构造函数优先级?

1
2
3
Minimal(src);	// 不是类型转换语法吧?
// vs
(Minimal)src;

会先按照构造函数解析,还是

构造函数能不能相互调用?

http://www.cppblog.com/wolf/articles/63490.html

http://www.cnblogs.com/chio/archive/2007/10/20/931043.html

传统的 func(var) 调用方式肯定不行,因为这意味着在某构造函数中又创建了个临时对象。

阅读全文 »

检查内存泄漏

Linux 下使用 Valgrind;Windows 下使用 Visual Leak Detector。前者没接触过,后者的确不错。

虽然在 Windows 下可以使用 CRT:Find memory leaks with the CRT library,但因为其也会报全局变量以及 static 变量(报告早于这两者的析构),当项目体量较大,全局变量或 static 变量较多时,没有有效的区分,查找真实的内存泄漏就会比较棘手。

  1. Enable memory leak detection,如何启用

    只在启用 _DEBUG 时有效;若程序存在多个退出点,可以使用 _CrtSetDbgFlag 函数;报告默认在“输出”窗口打印,但可以自定义。

  2. Interpret the memory-leak report,报告怎么看

    重点强调了检测只针对 malloc 函数,无法检测 new 操作符。但其提供了使用 _malloc_dbg 实现的 operator new 重载版本:

  3. Set breakpoints on a memory allocation number,如何打断点

  4. Compare memory states,对比不同时刻的快照

  5. False positives,误报现象

阅读全文 »

如题,在此做一份备忘,记录在 Windows 下进行 C++ 开发用到的工具、插件及其意义。

当然多少会涉及到如何安装、配置这些工具,但不会多做介绍。

  1. Visual Studio,越新越好,但都有历史包袱
  2. Visual AssistX 破解费时费力,还有兼容问题。非重度用户,可探索 visual studio IDE 自身
  3. vcpkg 包管理器
  4. spdlog 通过 vcpkg 安装
  5. vld, Visual Leak Detector
  6. 不使用制表符,转换为 4 个空格。配置 IDE 实现。

Visual Studio2015 IDE 中需要安装的插件(vc2017 中可能已经内嵌了某些插件的功能)

阅读全文 »

结论

cdn 节点和源上相同文件的 etag 或 lm 是相同的。

http header response

https://kb.cnblogs.com/page/92320/

2019/7/23 14:00:43 请求源 101.200.199.142 cdnapp.ydtg.com.cn

Date 原始服务器消息发出的时间

1
2
3
4
5
6
7
8
9
10
11
HTTP/1.1 200 OK
Server: nginx
Date: Tue, 23 Jul 2019 06:00:42 GMT
Content-Type: application/octet-stream
Content-Length: 1200940
Last-Modified: Tue, 23 Jul 2019 00:58:00 GMT
Connection: keep-alive
ETag: "5d365b98-12532c"
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
Accept-Ranges: bytes
阅读全文 »

获取

看一下 pageheap 针对的是什么问题? 堆调试工具——pageheap的使用和原理分析

pageheap 和 gflags 什么关系?前者被包含到了后者中。

This version of GFlags includes the functionality of PageHeap (pageheap.exe), a tool that enables heap allocation monitoring in Windows.

如何安装 gflags?安装 WinDbg

GFlags is included in the Debugging Tools for Windows 10 (WinDbg).

如何 获取 WinDbg

  • 在安装过程中,通过 win 10 sdk 的方式(无论是通过 msvc2019 还是直接安装 win10 sdk)都没有成功。

  • 最终单独安装的 Windows Driver Kit (WDK)

    Step 2: Install WDK for Windows 10, version 1903

阅读全文 »

在项目中弃用 glog,改用 spdlog。花了三个小时,跟了一遍 example.cpp 中提到 xxx_example(),当底层跳转要进入 fmt 时就不再追踪。

学习 spdlog 源码

  1. 在单参数构造函数中明确使用 explicit 关键词,避免隐式构造,避免隐式类型转换。
  2. log.h 存放模板类声明,但在头文件末尾 #include log_impl.h,后者存放模板类的定义
  3. 颜色默认只针对 level 关键词,不同级别预设了不同的色彩。但也可以使用 %^ %$ 划定收尾。
  4. 线程安全,支持多线程
  5. 注册表工厂模式?
  6. fmt/spdlog 如何输出和二进制的?spdlog::to_hex 与 fmt 自身的二进制格式化输出有什么不同? fmt 对自定义类型的支持
  7. spdlog 本身是线程安全的,async_ 定位?
  8. spdlog 开启 FMT_HEADER_ONLY 预编译宏,使用 fmt
  9. spdlog 以 head-only 方式使用
阅读全文 »

asio 封装了平台差异性,在 windows 下本质就是 IOCP。使用 IOCP 请看这里

ASIO

Your program will have at least one I/O execution context, such as an asio::io_context object, asio::thread_pool object, or asio::system_context. This I/O execution context represents your program’s link to the operating system’s I/O services.

理解 C++ Executor 的设计理念,查看 asio Executor requirements,就只是朴素的 callable thing 的执行器概念

基于 Asio 的 C++ 网络编程

Boost asio 官方教程

Documentation (non-Boost)

查看 asio 源码,可以学到“错误代码 vs 异常”两种策略;可以学到“同步 vs 异步”两种接口形式。

如果依赖 IO 的结果,(单线程)异步 IO 和同步阻塞 IO 都要等到 IO 完成才能继续执行,那么异步的性能是否更好?如果是,为什么?—— 理解 iocp 之后是否就“豁然开朗”了呢

有一点也需要注意, 就是从 request 进来到完成, 应用内各个节点和调用流程都要支持异步 io 调用, 否则一个节点不支持, 就退化成多线程的解决方式。摘自

ps. 如果某个节点特别耗时,阻塞当前线程,其实也就退化了

异步IO模型需要一个消息循环,在消息循环中,主线程不断地重复“读取消息-处理消息”这一过程。摘自

在消息模型中,处理一个消息必须非常迅速,否则,主线程将无法及时处理消息队列中的其他消息,导致程序看上去停止响应。

要理解 asio,其实就是 windows 下的 iocp,就是要理解 Proactor

如果要在连续下载文件中使用 asio 异步模型,那么如何将下载后 sqlite3 入库改为嵌入 handler 中?

例子学习笔记

2019/8/29 16:07:03 因为多数函数都是模板,编码时信息提示本就是短板。结合 co_await 运算符使用时,更是得不到任何提示信息。只能“瞎写”参数数目和类型,编译报错 + 查看源码多次试验究竟怎么写入参。

daytime_client.cpp

结合 c++11 std::future 使用 asio 的异步接口,只是在局部提升效率:每个异步接口返回的 future,在其他异步接口中使用时要传入 future::get() 结果,**而非 future**。从全局来看依旧是阻塞的。

当然,有些业务场景恰好如此。一方面想改进每次同步 IO 的阻塞,因为两次 IO 之间可以做些其余工作;另一方面,下一次 IO 调用依赖前一次 IO 的结果。如果为了局部改进,整体改用「异步 + 回调」形式,开发耗时久:“回调地狱”真的很累,难写,看的人也累。

在上述场景中,如果没有 两次 IO 之间可以做些其余工作 的需求,其实再次“退化”改用同步接口即可。

协程

Boost ASIO supports 3 coroutine types:

无栈协程 ☆

Boost 库中的协程支持两种方式:

  • 一种是封装了 Boost.Coroutinespawn,是一个 stackful 类型的协程;
  • 一种是 asio 作者写出的 stackless 协程。

asio 的作者通过 Duff’s Device 技术 实现的无栈协程,用到了好多奇技淫巧。结合 coroutine 类 的手册和 官方 example 学习过程中有以下问题或心得:

std::function<> 模板约定的签名是无法适配默认参数的:即 void handler(asio::error_code, size_t len = 0) 是无法赋值给 std::function<void(asio::error_code)> 类型的。

一方面从 c++11 才开始支持 std::function<>,另一方面在 c++03 example 中 async_accept()/async_read_some()/async_write() 竟然全部可以使用回调对象:void operator()( asio::error_code ec = asio::error_code(), std::size_t length = 0) ,要知道 async_accpet() 的回调签名可是 void(asio::error_code)

几个关键的** 伪-关键词**,其实都是宏。宏是不支持断点调试的,除非手动把所有的宏展开。不过作为成熟的网络库,这些宏都是经过千锤百炼的,调试中根本无需展开。

达夫设备的 fall-through 理解起来太难了,我能理解简单的使用案例,但前述伪-关键词还是看不懂。查看 fallthrough

成员全部交给 std::shared_ptr 托管,因为函数对象需要频繁的拷贝:一方面拷贝开销不能太大;另一方面,回调重入时成员必须有效且正确。虽然 fork server(*this)() 浅拷贝,但实际上 所有 智能指针陆续通过 ptr.reset(new xx) 全部新申请了内存,而 socket_ 的浅拷贝本就是最佳解决方案。每个主动 socket 封装都携有一份用不到 acceptor_ 也只是个瑕不掩瑜的、无法避免的小问题。

刚刚介绍的协程,不需要任何编译器/底层库的支持。只使用 C 语言本身就支持的 Duff’s Device 技术就能实现。唯一的缺点是局部变量无法跨 yield 。所以所有变量都要定义为函数对象的成员变量。 另外需要把协程定义为函数对象,需要额外编写不少代码。

更多请参考:Boost中的协程—Boost.Asio中的coroutine类

有栈协程 ☆

Boost.Context 提供的协程包括两类:非对称型协程 asymmetric_coroutine 的和对称型协程 symmetric_coroutine,前者de协程知道唤醒自己的协程,当需要暂停的时候控制流转换给那个特定的协程;对称协程中所有的协程都是相等的,协程可以把控制流给任何一个其它的协程。

c++03 examplesc++11 examplesspawn 实例演示涉及的 Boost.Coroutine 库已经 被标记为 Deprecated,推荐使用 Boost.Coroutine2。尴尬的地方在于 asio::spawn() 并未就新的 Boost.Coroutine2 封装新的实现,只能凑合着使用 deprecated 特性。

Coroutines TS Support ☆☆☆

DEMO cpp17_examples

即将进入 c++20 的协程。二百多行代码就能写个聊天室,佩服。

Coroutines TS Support,这是唯一能找到的有效介绍,虽然内容不多,但说清楚了入参、出参的用法和意义。

Support for the Coroutines TS is provided via the awaitable class template, the use_awaitable completion token, and the co_spawn() function.

co_spawn

查看 co_spawn 函数的功能与签名:通过持有协程的返回值(第二个入参)以便 resume 协程。asio 库封装的协程,协程的调度对用户都是透明的(用户只提供了某些定制点,用户无需 resume 协程),完全由 co_spawn 底层实现。类比 thread,完全是 os 的定位。

Spawn a new coroutined-based thread of execution. 生成一个新的基于协程的执行线程。

对于用户,压下好奇心,不要执着于它是如何实现的,直接理解为“新启执行线程”是直观且无误的。专注业务而非库的功能。

对称协程只是非对称协程的一个特例,我们可以通过添加一个中立的第三方调度中心的方式将非对称协程转换成对称协程(只需要在所有协程“暂停”时将控制权转交给调度中心统一调度即可)引用来源

推测 co_spawn 内部就存在这么个“调度中心”。

coroutines_ts 中给出了协程的定义等。在 asio 中协程的显著标识是函数返回值:awaitable<void> foo()

The return type of a coroutine or asynchronous operation.

所以 co_spawn() 的第二个入参使用 lambda expression 时要显式地指出返回类型:要么 return xx 语句;要么 -> ret。因为缺省使用 void

-> ret, where ret specifiers the return type. If trailing-return-type is not present, the return type is implied by the function return statements (or void if it doesn’t return any value)

总结:协程是特异的函数,特征就是返回签名;协程也不能像普通函数那样直接调用,而是通过 co_spawn() 以便 suspend 后由调度中心 resume。

executor

在协程函数体的实现中,当需要 io_context 对象时,既可以通过函数参数传进来,也可以在函数内使用以下语句获取:

1
2
// asio::any_io_executor
auto executor = co_await this_coro::executor;

一度非常困惑两者的关系,context 和 executor 分别是什么概念

在 asio 中,execution context 是一个重量级的对象,不允许拷贝。executor 是对一个轻量级的句柄(handle)对象指向对应的execution context。

另外两个特殊值,co_spawn() 的第三个参数 asio::detached,和 async_xx() 的末尾参数 asio::use_awaitable,都是固定用法。

上述三个特殊值的类型定义都是空的,模板依赖其类型进行特例化,类型的定义反而不重要。

asio::use_awaitable 用于模板特例化之外有更多的用途吗?

顺序 co_spawn() 创建 1-2-3-4 协程,在各协程中 co_await 之前的逻辑也是 1-2-3-4 顺序执行!——这是个浅显的现象,了解协程的定义和实现后,这也是理所当然的:在第一次 co_await 时函数才返回。

Special Values

This completion token that can be passed as a handler to an asynchronous operation:

  • asio::use_awaitable
  • asio::detached
  • asio::use_future

asio::use_awaitable

The use_awaitable_t class, with its value use_awaitable, is used to represent the currently executing coroutine. This completion token may be passed as a handler to an asynchronous operation. For example:

1
2
3
4
5
awaitable<void> my_coroutine()
{
std::size_t n = co_await my_socket.async_read_some(buffer, use_awaitable);
...
}

When used with co_await, the initiating function (async_read_some in the above example) suspends the current coroutine. The coroutine is resumed when the asynchronous operation completes, and the result of the operation is returned.

asio::detached

The detached_t class is used to indicate that an asynchronous operation is detached. That is, there is no completion handler waiting for the operation’s result. A detached_t object may be passed as a handler to an asynchronous operation, typically using the special value asio::detached. For example:

my_socket.async_send(my_buffer, asio::detached);

asio::use_future

The use_future_t class is used to indicate that an asynchronous operation should return a std::future object. A use_future_t object may be passed as a handler to an asynchronous operation, typically using the special value asio::use_future. For example:

1
2
std::future<std::size_t> my_future
= my_socket.async_read_some(my_buffer, asio::use_future);

The initiating function (async_read_some in the above example) returns a future that will receive the result of the operation. If the operation completes with an error_code indicating failure, it is converted into a system_error and passed back to the caller via the future.

this_coro::executor

Awaitable object that returns the executor of the current coroutine.

constexpr executor_t executor;

协程里外的异常

1
2
3
4
5
6
7
#include <asio/redirect_error.hpp>

asio::error_code ec;
co_await async_write(socket, asio::buffer(msg), asio::redirect_error(use_awaitable, ec) );
if (ec)
{
}

redirect_error_t

Completion token type used to specify that an error produced by an asynchronous operation is captured to an error_code variable.

asio::redirect_err()缺点 无伤大雅

The redirect_error token transformation recovers the option to use the error_code interface, but it suffers from the same drawbacks that make pure error codes unappealing in the synchronous case.

co_await 和线程锁

协程中禁用线程锁!

不和谐的 / 疑惑

asio::async_read() 没有 asio::use_awaitable 版本的重载 (•_•)? boost beast 提供了

The secret of stackless coroutines is that they can suspend themselves only from the top-level function.

socket::async_read_some() 无法以 asio::streambuf 作为入参

1
2
3
4
5
6
7
8
9
10
11
12
auto size = response_.m_response_buf.size();
const auto remain = content_length - size;
//auto len = co_await asio::async_read(*ssocket_, response_.m_response_buf, asio::transfer_at_least(content_length - size),
// asio::redirect_error(asio::use_awaitable, ec));
for (size_t len = 0; !ec && len < remain; )
{
auto buf = response_.m_response_buf.prepare(std::min(remain, 1024ul));
auto bytes = co_await ssocket_->async_read_some(asio::buffer(buf),
asio::redirect_error(asio::use_awaitable, ec));
response_.m_response_buf.commit(bytes);
len += bytes;
}

在 msvc2015 中断点调试无法监视变量 (ÒωÓױ)!

如何编写协程类型

请移步 coroutine 学习总结