菜菜博士

博士在网络的家

链接到 ucrtbase.dll

使用 vc6.0 以后的 VS 编译器,最恼火的一点就是生成的 exe 依赖 msvcrXXX.dll。 其中 XXX 是个版本号。而且 msvcrXXX.dll 系统是不自带的。 于是每个 exe 都得带上 vcredist.exe 安装程序。 过于蛋疼。 正统的解决方法是改使用静态C库。 但是会带来二进制体积暴涨的问题。 十分的怀念 VC6.0 可以生成exe 依赖 msvcr...

boost.fcontext 为何快?

在 批评 libco 的 上下文切换代码的时候,我曾经说,协程的上下文切换代码,并不需要恢复所有的寄存器。 只需要恢复调用约定里规定的 “非易失性寄存器”。 只有操作系统进行抢占式调度的时候,才需要恢复所有的寄存器。因为抢占式调度,可以在任何时间任何地方中断线程。如果不恢复所有寄存器,则在线程的角度看来就是寄存器的值可以任何时间任何地方突然“自主改变”。 而由用户执行的协程调度则不然。上...

点评 腾讯 的 协程库 libco

序 libco 是 腾讯开源的一个协程库。噱头很大,动不动就是承载了微信后台亿万并发。 敢说个不马上被喷成狗头。但是我偏就是要对这些光有名头没啥技术的东西祛魅。 上一片文章 里,点评了云风的 coroutine库,其中提到了 libco 犯了和云风一样的错。 其实 libco 的错误要比云风还要多的多。 废话不多说,接下来点评 libco libco 错在哪里 首先错误的,仓库上...

点评 云风 的 C库 coroutine

序 在知乎上看到两篇 吹嘘 云风的 coroutine 库的文章。 人啊就是这样的,出名了以后,就是垃圾也有人吹捧。 今天来点评下,云风的 coroutine 到底垃圾在哪里。又或者说,一个优秀的协程,应该优秀在哪里。 云风错在哪里 首先一个优秀的协程库,要做到“自然”。 什么是“自然”呢?就是要做到尽量不改变原来写同步阻塞IO逻辑的时候的代码。 因此引出第一错: 1 协程库...

基于 ucontext + iocp4linux 的超简协程库

虽然说,使用 异步最佳的实践是使用协程。 然而 c++20 的协程并不总是可用的。总不能说,不能更新编译器的地方就不配写代码吧。 因此,我在上一篇文章里说道,可以继续忍受回调地狱。或者使用有栈协程。 当然,如果使用 asio , 那么一切问题都不存在。asio 支持 有栈协程,无栈协程,daff‘s device 协程,还有最基础的,回调模式。 asio 简直就是个“宇宙级” 的异步库...

深入异步IO 第二篇: 正确使用 IOCP,正确设计 proactor

前言 干了那么多年码农,遇到 IO 从来都是写异步代码。从最初直面裸 epoll 写,到后来基本上依赖 asio 写。 最近研究 io_uring 后,试着用 io_uring 把 IOCP 那套 API 实现了。 以前呆在 asio 的舒适区,根本没关注过其他程序员对异步的理解。但是 windows 程序员是最多的,当我研究IOCP的时候, 以前被我忽略掉的那群写 IOCP 的程序员,...

深入理解异步IO

前言 干了那么多年码农,遇到 IO 从来都是写异步代码。从最初直面裸 epoll 写,到后来基本上依赖 asio 写。 最近研究 io_uring 后,试着用 io_uring 把 IOCP 那套 API 实现了。 以前呆在 asio 的舒适区,根本没关注过其他程序员对异步的理解。但是 windows 程序员是最多的,当我研究IOCP的时候, 以前被我忽略掉的那群写 IOCP 的程序员,...

批量提交IO提升性能

在基本完成 iocp4linux 后,我抽空在 rockpi 上测了下性能。 使用仓库里自带的例子 test/web_server/server.cpp,我测得了大约 6000req/s 的性能。 还不错。但是和 PC 上超过十万的性能相比,有那么点。。。弱鸡。 但是我通过 htop 注意到了一点。wrk 压测的时候, cpu1 是 cpu 100%。而且都是 红色的。说明cpu 都...

超轻量级 IOCP 协程库

在 上一篇 文章里,我提出了 iocp4linux 。 在编写 iocp4linux 的过程中,我需要写一些测试代码。 一开始,我随便的找了一个基于 IOCP 的 echo test 和一个简单的 web server。 首先确保这弄来的例子能在 windows 上编译通过。 然后修改 ifdef _WIN32守卫,在 linux 平台上改为使用 iocp.h 头文件。 然后很轻松的...

IOCP 移植到Linux上

序 windows 高性能IO使用的是 proactor 模型,而古代 Linux 上则是 reactor 模型。 因此跨平台的网络库,通常会选择实现为其中一种模型,然后在另一个平台上使用模拟。 比如 asio 使用 proactor 模型。Linux 上使用 epoll 模拟。 又比如 libevent 使用 reactor 模型, windows 上使用 iocp 模拟。 而将 i...

用iouring扩展asio

序 什么叫扩展asio? Beast给Asio增加了 HTTP 协议,不叫扩展asio。那什么叫扩展asio? 举个例子,给 asio 的 socket 对象,基于io_uring提供的 IORING_OP_SHUTDOWN 增加 async_shutdown() 接口,就算扩展asio。因为原本asio并没有异步shutdown功能。而且这个功能并不能靠组合原有的asio接口实现。这就叫...

重叠IO(proactor)是最理想的IO模型

reactor 和 proactor , 这种毫无联系的英文专用名词,很容易把人弄晕。 回归正途,使用精确的汉语描述,而非翻译,则正确的说法是: 多路复用IO 和 重叠 IO 模式。 所谓多路复用,顾名思义,就是好几个IO复用。。。 复用了谁?复用了线程。 它对比的是最传统的UNIX网络编程: 一个连接一个线程。并发就要开多线程。连接多了,线程就多,系统压力非常大。 而多路复用,则可以...

真正的重叠IO

不知有没有人想过,为何微软将windows 的异步API称为“重叠IO”而不是异步IO。 在我看来,重叠IO和异步IO的区别,主要在于发起IO后的动作。 重叠IO, 发起 IO 后,继续执行应用层的逻辑。应用可以选择在任何何时的地点再选择同步(阻塞等待 or 异步等待)IO的结果。 异步IO, 发起 IO 后,立即挂起当前业务逻辑(也就是立即非阻塞等待 IO 结果),回到事件循环,以便执...

不要 new 一个 char 数组当缓冲区

性能调优是一个有魔力的工作。最近研究到了 std::pmr::, 就想着看能否有些老代码能改进改进,提高下性能。 但是,测试的时候,发现还是有一些路径的效率不理想。 经过好久的排查,最终定位代码到这么一行 auto buffer_size = 5*1024*1024; auto buffer = std::make_unique<char[]>(buffer_size); ...

用本机 clang 进行交叉编译

你在 A 平台上编译一份代码,编译出来的结果,在 A 平台无法运行,只能在 B 平台运行。这个就叫交叉编译。 通常使用交叉编译,是因为 B 平台太弱鸡,性能无法胜任编译工作。 因此大部分交叉编译,都是发生在 x86 上为 arm 编译。 为了进行交叉编译,你需要使用一种专门为交叉编译而开发的工具 —— 交叉编译器。 在 Gentoo 上,交叉编译器可以使用工具 ”crossdev” ...

可变模板参数包迭代

序 如果你写了一个模板函数,模板函数里使用了 可变参数。比如你写了个 template<typename... Args> int print(Args... args); 那么,要如何去访问这些可变的参数呢? 方法 1 使用递归法。比如定义2个 print, 一个是单参数的,另一个是 2个模板参数的。 template<typename Arg> in...

把闭包变成函数指针—— trampoline 原理解析

序 十年前,我曾经写过一个让 C 形式的回调函数支持 闭包的小转换工具, 见这。 那时候,我说过,要想把 boost.function 传给 C 接口,那这个 C 接口,必须得带一个 void* user_data 的参数。 比如 typedef int (*callback_t)(int arg1, int arg2, void* user_data); bool registe...

重提类型擦除器

序 十年前,我曾经写过一篇有关类型擦除器的文章, 见这。 十年后,我打算再探讨下类型擦除器。 C++ 对 多态 的支持,在核心层面,就两条: 虚继承 和 模板。 假设你要设计一个 线程池。这个池你可以“投递”各种任务进去。 什么叫”任务“呢? 就是一段代码。于是,早期你想到,可以使用 函数指针。 于是你的 线程池长这样 class threadpool { typedef v...

类型萃取技术:函数签名当模板参数

序 在 上一篇 文章里,我提到了 std::function 的模板参数,是一个叫函数签名 的东西。 什么是函数签名? 比如 main 函数,他的签名是 int(int, char**)。所谓函数签名,是指函数声明去掉 函数名和变量名 后得到的一个精简描述。 这个精简描述,指导编译器如何按”调用约定“产生具体的汇编指令以调用一个函数。 函数名,不过是在最后的 call 指令里,提供了...

不需要void*user_data的闭包封装

一般来说,如果 C api 接受一个回调,通常会额外允许设置一个 void* 的回调参数。 用户可以把一些额外的参数用这个 void* 传给回调函数。 比如 typedef void (*read_function_t)(const char* read_buf, int read_size, void* user_data); int register_read_callback(re...