非阻塞TCP流和HttpClient - phxrpc代码阅读(6)

写在前面

其实这点东西有点鸡肋。因为TCP流在前面已经讲过,难点在于“流”和“流缓冲区”部分。而HttpClient只是TCP流的一个应用,代码不多,且重点在于HTTP协议的调教上面。

不过因为前面有写阻塞TCP流,还是前后呼应,把非阻塞TCP流也小小的讲解一下。顺便饶一段HttpClient的讲解,算是充实一下内容吧。

非阻塞TCP流缓冲区 - UThreadTcpStreamBuf

这个其实没啥可讲的,传入一个socket,然后读写分别调用UThreadRecvUThreadSend,IO复用和协程切换的复杂操作都被封装在里面了。剩下的操作都由基类函数来解决。

非阻塞TCP流 - UThreadTcpStream

确实没啥可说的,你们自己去读代码吧。。。

非阻塞TCP流和阻塞TCP流的区别是~~它不阻塞~~,在阻塞TCP流中,我们传入的是一个TCP流,而非阻塞TCP流传入的是一个协程调度器和一个TCP流。

这个很好理解,一个阻塞流自然会占满一个线程的IO和CPU —— 在阻塞流IO读写时,CPU空闲;在CPU忙时,IO空闲。

而非阻塞流会将自己IO wait的时间托管给epoll,把剩下的时间用于CPU计算(和一些overhead上)。所以一个线程可以handle多个socket,协程调度器就是必须的了。之后的读写操作就交由我们前面讨论过的epoll和ucontext协程来共同完成了。

HttpClient …

more ...

使用epoll驱动ucontext - phxrpc代码阅读(5)

用pipe叫醒你 — EpollNotifier

class EpollNotifier类型封装了一个使用pipe传递信号的Notifier类。

Run()函数(其实我觉得叫Register或Activate会更好)首先声明了两个单向的pipe:pipe_fds_,从文档中我们可以知道pipe_fds_[0]是读管道,而pipe_fds_[1]是写管道。这里有一丁点反直觉,就是pipe拿了两个fd,但是仍旧是单工的。

然后将读fd设为O_NONBLOCK以供epoll调度,最后将Func()函数传入scheduler_中。

这里跑个题,想起了当年我大一的时候上过的通信导论的选修课。那会我还没有沉迷代码,还是一个积极乐观好好学习的新时代大学生。自从开始写了代码,人就越来越废物了,连女朋友都找不到了。
年轻人们啊,有饭辙干点啥都行,千万别写码啊。

Func()函数做的事情很简单,就是从管道里尝试poll一段数据,拿到数据后直接扔掉。因为管道里传来的数据并没有实际意义,这样设计的主要意义在于唤醒epoll。

我们可以从Notify()函数中看出 …

more ...

ucontext - phxrpc代码阅读(4)

写在前面

国庆假期过半,phxrpc的代码阅读大概要小小告一段落啦。因为这两天还要读工作相关的代码,以及最后几天还有一次短途旅行。

所以非阻塞TCP流可能要留到下一篇了,这一篇只涉及非阻塞TCP流使用到的ucontext协程库,及其使用的一些框架代码。

161013更新:这点破东西写到今天才写完,GG。

什么是ucontext

"Subroutines are special cases of ... coroutines." –Donald Knuth.

首先我们来看一下,什么是线程。线程是进程内一条执行流的状态,包含了硬件状态(硬件计数器,寄存器,条件码等)和堆栈中的数据。

线程通常只有一个入口和一个出口。当线程返回时,线程的生命周期也结束了。所以,通常线程的执行由内核调度。

协程的定义与线程类似,也是硬件状态+堆栈的状态组合。但是与线程不同的是,协程可以有多个出口。可以通过yield来暂停自己,调用其它协程。再次启动时,会从上次挂起的地方继续运行。

phxrpc中的ucontext

phxrpc提供了system和boost两种ucontext的实现,所以提供了一个uthread_context_base的基类。其实在这里我是有一点怀疑虚函数的性能的,不过好在协程的切换以及网络IO操作还是比较耗性能的,所以虚函数多出来的几次内存寻址也并非不能接受 …

more ...

打造你的专属键盘 — ErgoDone

写在前面

低能预警:这是最没有技术含量的一篇博客,无关人员可以撤离。

部分图片来自网络,侵删

这个十一假期,亲手DIY了一把键盘。当然,我只搞定了焊接部分,把不同的组件拼在了一起。

在焊接的过程中,也学习到了一些新知识。写在这里,记录下来。

ErgoDone介绍

ErgoDone是著名开源硬件ErgoDox的中国特色版本,精简了一些硬件以控制成本。总体价格在500RMB左右,是工薪阶级装逼界的一颗新星。

Ergodox的一个版本:

ErgoDone明显就屌丝了许多:

键盘主控

ErgoDone使用了Arduino pro micro做为主控,基中使用了mega32u4做为芯片,提供了模拟USB输入设备的相关函数(包括键盘、鼠标)。网上有许多现成的范例,用这款芯片(以及另一版使用mega32u4的Arduino开发版,Arduino Leonardo)制作体感鼠标(搭配陀螺仪)、游戏摇杆(搭配相关硬件)以及各种专用输入设备。

Arduino pro micro淘宝价不到20块钱,相对来说算是非常超值了。

焊接

由于本人算是菜的抠脚的业余选手,在这个键盘上的主要工作就是焊接。然而,就在焊接这样一件相对简单的工作上,我也是踩了不少坑 …

more ...

阻塞TCP流 - phxrpc代码阅读(3)

写在前面

phxrpc的流(streamstreambuf)与网络访问其实是耦合在一起的,所以本文可以结合着第一篇笔记一起来看。虽然我非常想吐槽这种强耦合性的设计,但是我决定还是好好理解phxrpc的设计之后。。。攒一波大的:)

BlockTcpStreamBuf

class BlockTcpStreamBuf继承自BaseTcpStreamBuf。其中重写了precvpsend两个函数,并且持有了一个文件描述符(file descriptor):socket_

precvpsend直接调用了<sys/socket.h>中的recv(2)send(2),并没有其它操作。

网络相关的操作,则由class BlockTcpStream来负责。BlockTcpStreamBuf只负责IO部分。

if (BaseTcpUtils::SetNonBlock(sockfd, false …
more ...

定时器以及其它 - phxrpc阅读笔记(2)

写在前面

phxrpc使用了协程(ucontext)和IO复用技术(epoll)来实现网络通信。定时器在其中起到了非常重要的作用。下面我们就来分析一下phxrpc的timer.[h|cpp]中的代码。

system_clock vs steady_clock

system_clocksteadly_clock都是来自<chrono>库,都是用来获取当前时间的。

system_clock用来从系统时钟获取时钟时间(wall clock time),而steadly_clock获取的是时钟tick,而且保证随着时间的推移,时钟tick数不会变小。

然而实际上,在某些系统下,这两个时钟的实现是一致的。详细信息可以参考这里

注:在clang++ 4.2.1, g++ 5.4 下实验,这两个时钟是不同的。所以个人认为在这里最好不要做任何无意义的假设。

几毫秒的安睡

void …
more ...