使用tmux

神级命令行软件 Tmux

  • 这是我在大学里认识到的最好用的神器,没有之一,提高效率那是没得说,主要至今未曾崩溃过,可见其可怕之处
  • 额外提一点就是,由于学校原因,没机会使用到 tmux 的远程连接黑魔法,所以本篇文章记录的是单机情况下的操作,远程连接没有用过,就不好置评
  • 但即便是单机情况下,tmux 也绝对是让人惊艳的一款终端软件

说白了它就是一个分屏工具

  • 我不会记录它的每个部件的具体名字,而是用什么地方的作用来描述它,还是那句老话,在其位谋其职。

迅速开始

  • 安装,直接从仓库里安装,但是一般而言,Linux会自带

    $ sudo apt-get install tmux
    
  • 完成之后,不用管其他的,直接在 Shell 里输入:

    $ tmux
    

    很好,进入到了另一个异次元,其实看起来和刚才没多大区别,也就是屏幕闪了一下。

  • 这就是最原始的tmux界面,接下来记录进行最有用的,也是最经常用的三个操作

    Read More

一个HTTP服务器的C之路(下)

趁着早起, 接着昨天

功能选择

  • 选择使用的epoll
  • 实际上,epoll应该是代表单进程的极端表现,最大程度的发挥一个核的最大实力,但是对多核来说就有些无法触及,但是在此处我们可以考虑将epoll扩展出去。
  • epoll的作用是监听已被注册到自身的那些文件描述符的各种事件(可读,可写等等)。我们可以考虑让监听套接字独享一个epoll(连接epoll),并且在其之下(逻辑中的下,实际上没有直接连接接触)用多线程/多进程建立几个处理新连接事物的专用epoll(事务epoll)。就是这么简单的思路。

    • 对比一下多线程还是多进程:
    • 多进程 模式是独立性较好,在忽略所谓的进程创建开销(对于本程序而言可以忽略,因为总是创建固定数量的进程,而不是在运行程序是一直创建,销毁)情况下,多进程还有一个缺点,那就是进程间通信(IPC)开销大,即便是使用共享内存也是如此,因为需要打开描述符open,映射mmap,关闭描述符close,(还隐含着解除映射munmap),这样的操作。
    • 多线程 模式是对于数据的独立性差,十分容易出错,特别是竞争条件的产生是多核编程中最为核心的问题。
  • 针对上面的问题,可以参考分布式系统的设计过程中,有一种叫做一致性哈希的设计思想,也就是不要让 事务epoll 相互竞争,而是让连接epoll自己将新的到的连接,分发给这些固定数量的事务epoll中的某一个,并且应该形成均衡发布

    • 后期会实现,当某一个线程意外退出以后,事务会均衡发放给离自己最近的线程。

      Read More

一个HTTP服务器的C之路(上)

一个HTTP服务器的C之路(下)

记录近来写的HTTP服务器的 经历,坑,收获

万事皆有起因

  • 大三了,又在反思自己和科班的差距,课少了,空闲时间突然多了起来,在这个大学里,这么一路走过来,自己摸索的不容易
  • 天天看着浏览器的F12界面和 Wireshark 的抓取交互界面,突然想要自己做一个HTTP服务器,首先冷静了三天,发现这个念头依旧强烈,墙裂。
  • 在翻看TCP/IP卷一协议无果后,于是我开始蹲点图书馆,借HTTP权威指南图解HTTP,然而事与愿违,这个号称“黑龙江最大图书馆”称呼的学校图书馆竟无法找到这本书。行吧,那我不借了总行吧
  • 回想起自己大二节选看的深入理解计算机操作系统(CSAPP)这本著名的书,似乎后面有一个Web服务器实现,虽然知道这大概是一个十分简陋的单处理的程序,但是对于理清自己思路很有帮助,特别是协议的交互
  • 翻看一开,还真的有,说来惭愧前面的进程部分至今还是崭新的。首先看到了CGI,后来发现了后方的GET方法实现,大概了解了交互的过程,这之间在w3cschool查找了(恶补了一下HTTP状态码)一些HTTP的状态,以及维基百科里查看了HTTP报文的组成,当然最详细的还是使用WireShark抓取的交互包,而最简单明了的方法则是用 F12查看交互信息。
  • 这之中有两个网站对我助力很大

  • Linux 的 man page 是另一个最有用的工具。

用C编写一个HTTP服务器?!

  • 我说了这个想法给我的一个同学,他以为我疯了。
  • 并没有,我在网上查了近来的许多模型,最后选择参考 事件驱动的epoll + 多线程进行实现,一方面直接避免了逻辑上的惊群现象,一方面也能充分利用多核。
  • 我怎么想的,在最开始我对照着CSAPP对最开始的版本代码进行编写,不得不说里面有一个对系统调用(read, write)的调用在后面给我一个大坑,也给我很多思路上的启发。

    • 启发就在于,将系统调用进行自己的包装,可以提供很干净的C代码,并且可以进行良好功能的编写,这里特指对数据的缓存错误处理,而这两个都是网络编程里的重头戏
    • 大坑在于,我到后期发现,一个新的客户端在连接上本服务器之后,其信息应该尽可能集中,而不是分散在各处不同的结构体中,不容易维护,而且出了错还不容易发现。特别在于CSAPP中的 rio形式的读写封装,无法进行长久性的数据缓存,在这里长久性的数据缓存指的是,假设对端(peer)正忙,或者自己的TCP发送缓冲区已满,那这个数据要怎么办?
  • 不过读了CSAPP之后,最基本的服务器工作原理还是可以知道的,至少我看到了某些让我目瞪口呆的HTTP协议之间的交互方式。

    • 比如报文还真的是如我想象那般,完全自己构造的。
    • 对于一个几乎没有高级字符串处理功能的C语言来说,这的确是一个苦活,但是正因为如此,才能接触到相对真实的HTTP服务器

      Read More

使用gdb

开始

  • 近来在写网络服务器程序,涉及到了多线程,且由于网络的环境复杂的原因,常有未知情况发生,导致在程序中自行添加调试语句显得有些吃力
  • gdb 请出山来是一个最为正确的选择

使用

  • gdb看起来是全命令行的东西,而且一启动就是一大片英文,实在让没用过的望而却步,其实真正常用的就只有那几个功能。
  • 接下来我可能不会对这些命令进行明确的定义功能,因为有时候某些功能放在不同的地方意义就不同了,所以我只用最简单的语言来记录它们。
  1. 首先是安装,在(Debian)系的发行版 Linux 上,使用

    apt-get install gdb
    

    回车等一会就行,可能有的发行版已经装好了,但有的却没有。

  2. 首先不要打开这个gdb,因为这里面的东西的确看起来挺复杂的

  3. 我们要先有一个程序的源文件,假设是 C语言main.c
  4. 在里面写好正确的语句后,对它进行编译,链接,也就是调用 GCC,这里使用GCC的命令是

    gcc -o main -g main.c
    

    这个 -g 标志非常必要,意味着生成一些符号信息,用人话说就是 gdb 所需要的东西,没有这个东西是无法使用 gdb进行调试的。

  5. 紧接着,在当前目录下就有一个名为 main 的可执行文件,看好当前用户是否有权限执行它之后,使用如下命令开启调试旅程:

    Read More

Effective Modern Cpp 概述(二)

(Item 15)

Item 12 - Declare overriding functions override.

  • 介绍了一个鲜为人知的 C++11 新特性,针对一个类成员函数的

    class Widget {
    public:
      ...
      void doWork() &;   // 只有当 *this 是 lvalue 时才调用
      void doWork() &&;  // 只有当 *this 是 rvalue 时才调用
    };
    ...
    Widget makeWidget(); // 制造一个临时的 Widget
    // 调用
    Widget w; // 默认构造函数创建
    w.doWork(); // 调用第一个版本(lvalue)
    makeWidget().doWork(); // 调用第二个版本
    
  • 对于 重写重载,这两个极易混淆,或者说不是混淆而是十分容易不小心就写错了,的功能,在 C++98 之前我们只能选择相信程序员写下的代码是准确无误的。有几个判断 重写 的条件

    1. 基类里的该函数必须是 virtual
    2. 基类里的该函数和派生类里的该函数的 函数名 必须相同
    3. 基类里的该函数和派生类里的该函数的 参数类型 必须相同
    4. 基类里的该函数和派生类里的该函数的 返回值类型 必须相同或者可以兼容(即继承关系)
    5. 基类里的该函数和派生类里的该函数的 const 属性必须相同
    6. 基类里的该函数和派生类里的该函数的 引用限定词 必须相同(C++11新增)

      Read More

Effective Modern C++概述(一)

(本文有 11 个 Item)

《Effective Modern C++》概要

  • 因为 Scott 大大的这本新书迟迟没有中译本,百般无奈之下,选择了英文原版阅读。
  • 记录自己阅读这本书的过程,以及一些细节,知道自己不可能读一遍就能将所有信息记下,故写成博文,方便自己及需要者查阅
  • 本文是我自己的感受,如果有想要体会完整的思想请自行查阅原书,最好配一本完备的C++的语法工具书(例如The C++ Programming Languag, 4th Edition--Bjarne Stroustrup),以便随时查询,当然还需要一个可编程平台。

代码尽量和原书相同,除非是想更详细的解释才会自行修改

Chapter 1 - Deducing Types

Item 1 - Understand template type dedution

  • 这节讲述了C++有两种类型推断(不许程序员介入), 自动类型推断(auto type dedution)模板类型推断(template type dedution), 两者的联系以及唯一的区别。
  • 首先介绍的是模板类型推断,这是来自(C++98)的特性了,现在以及在沿用,是最成功的特性之一。
  • 对于模板类型推断,一般形式可归纳为:

    template<typename T>
    void f(ParamType param);
    // 调用
    f(expr); // expr 可能是一条语句
    

    ParamType 是一种缩写,用来表示 可能 类型T会有一些限定词来修饰,例如const, & 之类的。

    例如 把ParamType 可以代表 const T &

  • 而此时,编译器在进行 模板类型推断 的时候会做两件事(两个推断的结果),它会判断 TParamType的类型分别是什么。当然,如果 ParamType 就是 T 那自然就相同了。
  • 因为 T 究竟是什么,取决于 ParamType
  • 进行推断的过程中,有三条编译器遵循的判断情况:
    1. ParamType 是一个指针或者引用,但不是一个 通用引用(Universal reference 不知道怎么翻译)。
    2. ParamType 是一个 Universal reference
    3. ParamType·不是一个指针也不是一个引用·
    4. 所以将模板类型推断分为三种情况。

Read More