手动管理的弊端 在简单的程序中,我们不大可能忘记释放 new 出来的指针,但是随着程序规模的增大,我们忘了 delete 的概率也随之增大。在 C++ 中 new 出来的指针,赋值意味着引用的传递,当赋值运算符同时展现出“值拷贝”和“引用传递”两种截然不同的语义时,就很容易导致“内存泄漏”。 手动管理内存带来的更严重的问题是,内存究竟要由谁来分配和释放呢?指针的赋值将同一对象的引用散播到程序各处,但是该对象的释放却只能发生一次。当在代码中用完了一个资源指针,该不该释放 delete 掉它?这个资源极有可能同时被多个对象拥有着,而这些对象中的任何一个都有可能在之后使用该资源,其余指向这个对象的指针就变成了“野指针”;那如果不 delete 呢?也许你就是这个资源指针的唯一使用者,如果你用完不 delete,内存就泄漏了。 资源的拥有者是系统,当我们需要时便向系统申请资源,当我们不需要时就让系统自己收回去(Garbage Collection)。当我们自己处理的时候,就容易出现各种各样的问题。 继续阅读 >>


杜肖孟 18/01/11 23:39:30
主要涉及到的类和实现文件有: Endian.h 提供了字节序转换的函数。 Socket.h/Socket.cc socketfd 的封装,提供了绑定地址、开始listen、接受连接等操作,并可设置套接字选项。 InetAddress.h/InetAddress.cc 套接字地址的封装,提供了多种方式初始化一个地址,还提供方法从地址中拿到 ip 和 port。 SocketsOps.h/SocketsOps.cc 封装了 socket 相关的一些操作,提供给 Socket 和 InetAddress 用。 这部分就是基本的 TCP 套接字编程和套接字选项的知识,代码逻辑也很简单,推荐看下 UNP卷一 的相关章节。 下面逐一看下这几个相关的文件。 字节序转换部分(Endian.h) #ifndef MUDUO_NET_ENDIAN_H #define MUDUO_NET_ENDIAN_H #include <stdint.h> #include <endian.h 继续阅读 >>


杜肖孟 18/01/09 15:14:11
eventfd 介绍 Linux 2.6.27后添加了一个新的特性,就是eventfd,是用来实现多进程或多线程的之间的事件通知的。 接口 #include <sys/eventfd.h> int eventfd(unsigned int initval, int flags); 这个函数会创建一个事件对象(eventfd object),返回一个文件描述符,用来实现进程或线程间的等待/通知(wait/notify)机制。内核为这个对象维护了一个无符号的64位整形计数器 counter,用第一个参数(initval)初始化这个计数器,创建时一般可将其设为0,后面有例子测试这个参数产生的效果。 flags 可以使用三个宏: EFD_CLOEXEC:给这个新的文件描述符设置 FD_CLOEXEC 标志,即 close-on-exec,这样在调用 exec 后会自动关闭文件描述符。因为通常执行另一个程序后,会用全新的程序替换子进程的正文,数据,堆和栈等,原来的文件描述符变量也不存 继续阅读 >>


杜肖孟 18/01/08 23:22:55
muduo 的定时器功能由三个 class 实现,TimerId、Timer 和 TimerQueue。 TimerId 类 它唯一标识一个 Timer 定时器。TimerId Class 同时保存Timer* 和 sequence_,这个 sequence_ 是每个 Timer 对象有一个全局递增的序列号 int64_t sequence_,用原子计数器(AtomicInt64)生成。 它主要用于注销定时器,这样就可以区分地址相同的先后两个 Timer 对象。 namespace muduo { namespace net { class Timer; /// /// An opaque identifier, for canceling Timer. /// /* 带有唯一标识的Timer,主要用于取消Timer */ class TimerId : public muduo::copyable { public: TimerId() : timer_(NULL), 继续阅读 >>


杜肖孟 18/01/07 15:41:05
本文分析一下Reactor模式的实现,关键是三个类:Channel、Poller、EventLoop。 事件分发类 Channel Channel 是 selectable IO channel,负责注册与响应IO事件,包括注册给Poller的 fd 及其监听的事件,以及事件发生了所调的回调函数。 每个Channel对象自始至终只负责一个 fd 的事件分发,封装了一系列该 fd 对应的操作,使用了回调函数,包括可读、可写、关闭和错误处理四个。 首先给定Channel所属的 loop,及其要处理的 fd;接着注册 fd 上需要监听的事件,如果是常用的读写事件的话,可以直接调用接口函数enableReading或enableWriting来注册对应fd上的事件,disable*是销毁指定的事件;然后通过 set*Callback 来设置事件发生时的回调。 注册事件时函数调用关系,如下:Channel::update()->EventLoop::updateChannel(Channel* 继续阅读 >>


杜肖孟 18/01/06 18:36:02
考虑让我的 web server 增加对 PHP 的支持,这就要用到 php 解析器来将客户端请求的 php 文件解析为静态资源,再由我的 web server 将其返回到客户端,php-fpm 就可以来帮我们完成这个工作。可是我的 web server 如何与 php-fpm 通信呢? 接下来就是本文的主角:FastCGI 。 FastCGI 实现与测试代码:https://github.com/Tanswer/FastCGI Web Server 项目地址:https://github.com/Tanswer/Xserver Description C language through FastCGI protocol, through php-fpm, php file parsed into html files. How to use Preparation: please make sure you have installed php-fpm. Testing enviro 继续阅读 >>


杜肖孟 17/12/23 14:46:54
32 位的平台上,线性地址空间为固定的 4GB,并且由于采用了保护机制,Linux内核将这 4GB 分为两部分,线性地址较高的 1GB(0xC0000000 到 0xFFFFFFFF )为共享的内核空间;而较低的 3GB 为每个进程的用户空间。由于每个进程都不能直接访问内核空间,而是通过系统调用间接进入内核,因此所有的进程都共享内核空间。而每个进程都拥有各自的用户空间,各个进程之间不能互相访问彼此的用户空间。 一个进程的用户地址空间主要由两个数据结构来描述。一个是 mm_struct 结构,它对进程的整个用户空间进行描述,简称内存描述符;另一个是 vm_area_struct 结构,它对用户空间中各个区间( 代码区、数据区等 )进行描述。 进程用户空间的描述 内存描述符 每个进程只有一个 mm_struct 结构,在每个进程的 task_struct 结构中,有一个指向该结构的指针。 struct mm_struct { struct vm_area_struct *m 继续阅读 >>


杜肖孟 17/12/10 14:47:55
链接器之所以存在或者产生,基本上是由于程序开发的模块化。这里讲的模块,主要是编译概念上的模块,通常他们按照功能划分,比如一个.c或者.cpp文件就是一个编译单元,就是一个模块,编译后就产生一个.o目标文件。为了最终生成一个可执行文件、静态库或者动态库,就需要把各个编译单元按照特定的约定组合到一起。这里特定的约定指的就是“目标文件格式”,它定义了目标文件、库文件和可执行文件的格式,这里组合这一过程就叫做链接。 链接主要有以下几个任务:空间与地址分配,符号解析,重定位,昨天晚上回宿舍躺床上后,一直在想这几个过程,哪个先进行,哪个后进行,试图从目标文件开始理清链接进行的顺序,知直到生成可执行文件。但是我失败了,索性不管了,我大概清楚这几个过程都做了什么工作就好,如果纠结的失眠了第二天就起不来了=_=。用到的样例代码 在文末。 空间与地址分配 主流操作系统中,可执行文件都是基于虚拟地址空间的,即每个可执行文件都有相同且独立的地址空间,并且文件中各个段(代码段,数据段,以及进程空间中的堆栈段)都有相似的布局 继续阅读 >>


杜肖孟 17/12/06 15:38:25
编译器编译源代码后生成的文件叫做目标文件,从结构上讲它是已经编译后的可执行文件格式,只是还没有经过链接,其中可能有些符号或有些地址还没有调整。它本身就是按照可执行文件格式存储的,跟真正的可执行文件在结构上稍有不同。 目标文件的格式: 现在 PC 平台流行的可执行文件格式主要是 Windows 下的 PE( Portable Executable,可移植可执行 ) 和 Linux 的 ELF( Executable and Linkable Format,可执行链接格式 ),它们都是 COFF (Common Object Fifle Format,一般目标文件格式 )格式的变种。从广义上看,目标文件和可执行文件的格式其实几乎是一样的,在 Linux 下,我们可以将他们统称为 ELF 文件;在 Windows 下可以统称为 PE-COFF 文件格式。 在 Linux 下使用 file 命令查看相应的文件格式: 目标文件是什么样的 目标文件是什么样的呢? 下面我们通过一个具体的例子 继续阅读 >>


杜肖孟 17/12/04 20:58:14
写在前面: 分页机制完成线性地址到物理地址的转换 80x86 规定分页机制是可选的。分段和分页没有什么必然联系,分段可以说是 Intel 的 CPU 一直保持着的一种机制,而分页只是保护模式下的一种内存管理策略。想开启分页机制,CPU必须工作在保护模式,而工作在保护模式可以不开启分页。 分页机制由控制寄存器 CR0 中的 PG 位启用,如PG=1则启用分页机制,把线性地址转换为物理地址;如果PG=0则直接把段机制产生的线性地址当作物理地址使用。 为什么要分页? 问题的本质是在目前只分段的情况, CPU 认为线性地址等于物理地址,而线性地址是由编译器编译出来的,它本身是连续的,所以物理地址也必须要连续才行,但我们可用的物理地址不连续。换句话说,如果线性地址连续,而物理地址可以不连续,不就解决了吗。所以要解除线性地址和物理地址一一对应的关系,然后将他们的关系重新建立,通过某种映射关系,可以将线性地址映射到任意物理地址。 页与页表 为了效率起见,将线性地址空间分成若干大小相等的片,称为页( 继续阅读 >>


杜肖孟 17/12/02 14:51:54