libphenom源码笔记

2013-10-05 09:48:36

看了下Facebook前段时间放出来的libphenom,简单总结一下。

网络相关

libphenom提供了自定义的socket描述符ph_socket_t和通用的地址结构phenom_sockaddrph_sock结构封装了读写buffer、用于NBIO的job结构、超时时长、事件发生后的callback等信息。 被enable的ph_sock将由NBIO pool管理。

监听和连接都是封装成job来异步化:

  • ph_socket_connect(s, addr, timeout, func, arg)
    • s上发起对addr的异步连接,超时时间timeout,结束后(可能连接失败)调用func(arg)。这个callback的类型为ph_socket_connect_func,若出错则status为对应的errno
  • ph_sock_resolve_and_connect(),解析域名并发起连接。
    • 根据resolver参数指定的resolver解析域名。如果是PH_SOCK_CONNECT_RESOLVE_SYSTEM,调用ph_dns_getaddrinfo(),将解析相关的数据、callback封装为job后通过ph_job_set_pool()加入DNS线程池。如果是PH_SOCK_CONNECT_RESOLVE_ARES则调用ARES库解析。
  • 一个listener的job被调度时,调用其callback accept_dispatch()。这个函数通过accept4()accept()来接收新连接,并对客户端socket调用listener上的acceptor()

以通过getaddrinfo()解析域名为例,

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
ph_sock_resolve_and_connect()        // 回调func(arg)
    ph_dns_getaddrinfo()             // 设置info->func = did_sys_resolve, info->arg = rac;
        ph_job_set_pool()            // 有事件时 dns_addrinfo(info)
         
// 事件发生时
dns_addrinfo()          
    info->func()                        // func()即did_sys_resolve()
        did_sys_resolve()
            attempt_connect()           // 解析成功后发起连接
                ph_socket_connect()     // 回调connected_sock()
            rac->func()                 // 解析不成功

Job

Job的定义ph_job_def包含callback、memtype和destructor。 新创建的job会根据其定义中的memtype来分配内存,并设置callback。 每种具体的job,例如ph_listener_t,都有对应的ph_job_def

创建一个job时,调用ph_job_alloc(),传入job对应的定义来获取和初始化动态分配的对象。

阅读全文 →

APUE笔记-信号

2013-05-01 17:18:39

Session leader终止时产生SIGHUP,发送给所有前台进程。由于daemon没有控制终端, 通常不会收到这一信号,因此SIGHUP常用于通知daemon读取更新的配置文件。

向读端终止的管道,或向连接已断开的SOCK_STREAM类型的socket写数据将产生SIGPIPE

后台进程读终端将产生SIGTTIN。若读进程忽略或屏蔽SIGTTIN,或读进程所在组是孤儿组, 则不产生SIGTTIN,读操作返回EIO

不允许后台进程写终端时,后台进程对终端的写操作将产生SIGTTOU。 若写进程忽略或屏蔽SIGTTOU,或写进程所在组是孤儿组,则写操作返回EIO

exec将所有信号捕获重置为默认动作,其余未捕获信号不变(exec之后, caller的内存布局已无意义)。

Reentrant functions。包含资源分配(如malloc)、操作全局数据结构(如getpwnam)等动作的函数在信号发生时可能出现问题。 大多数标准I/O库中的函数因使用了全局数据结构而成为nonreentrant。但即使是reentrant,也要注意在signal handler里保存和恢复每个线程的errno

Unreliable signals的缺点:a.信号产生后,处理动作会被重置为默认动作; b.只能捕获或者忽略信号。这两点会造成信号丢失:信号在某个控制之外的时刻产生了,但程序处理不到。 为了保证语义正确,不依赖于具体实现,请用sigaction

Reliable signal。触发信号的事件发生时,信号被generated。 一个信号产生之后,kernel在进程表中设置一个标记。信号处理动作发生时,信号被delivered。 这之间信号被pending。

若信号按默认动作处理,或被捕获,则除非进程unblock信号,或者将信号处理动作改为ignore, 否则信号一直被pending。

若系统多次deliver一个信号,则称该信号被queued。大多数UNIX系统,除非支持POSIX.1的real-time extension, 都不会排队信号,只会deliver一次。POSIX.1也没有指明信号deliver给进程的顺序。

阅读全文 →

APUE笔记-进程关系

2013-03-15 16:07:26

终端登录

使用sysvinit时,init对每个每个终端设备forkexec一个getty程序。 getty调用open以读写方式打开终端设备,将描述符0、1、2连接到打开的终端设备。 getty打印出输入用户名的提示符,等待用户输入。

输入用户名后,getty通过exec调用login程序,传入一个初始的环境供login使用。 login根据输入的用户名,调用getpwnam函数,获取相应用户的passwd结构。 login调用getpass函数,显示输入密码的提示符,读取用户输入。 用户输入的密码由crypt函数加密,并与登录用户passwd结构中pw_passwd成员比较以验证密码是否正确。 若连续若干次登录错误,login调用exit退出。init获知其退出后,重新forkexec一个getty程序。

若登录正确,login

  • 调用chdir修改用户的home目录
  • 调用chown修改终端设备的拥有者
  • 修改终端设备的读写权限
  • 调用initgroupssetgid设置用户的gid
  • 初始化环境,如HOMESHELLUSERPATH
  • 调用setuid修改用户的uid
  • 调用exec启动shell

此时shell的描述符0、1、2均连到终端设备,shell读取其配置文件并执行一些初始化操作。

阅读全文 →

APUE笔记-进程控制

2013-03-11 10:25:01

进程创建

fork创建子进程。父子进程共享text段,但子进程拥有自己独立的堆、栈、数据段等内存区域。 实际系统中常利用copy on write来推迟和减少内存区域的复制。

子进程继承父进程的:

  • ruid, euid, rgid, egid
  • supplimentary gids
  • 进程组id
  • 会话id
  • 控制终端
  • suid和sgid标记
  • 当前工作目录(cwd)
  • 根目录
  • umask
  • 信号屏蔽和配置
  • 已打开的描述符的FD_CLOEXEC标记
  • 环境
  • 共享内存段
  • 内存映射
  • 资源限制

vfork创建的子进程共享父进程的内存空间,直到其调用execexit, 并保证子进程先运行,直到其调用execexit,通常在vfork后调用exec来spawn一个新的进程。

阅读全文 →

APUE笔记-进程环境

2013-03-06 10:43:05

程序执行和退出

Loader先于main执行,退出时调用exit, 以相反顺序调用atexit注册的exit handlers, 并进行一些清理工作(flush缓冲的写,fclose文件流,删除临时文件等)。 _exit_Exit直接将控制返回给内核,清理工作是否进行依赖于实现。

mainreturn不带返回值,或者main没有被定义为返回整数,则进程退出状态不确定。 C99中,若main被定义为返回整数,但没有return或调用exit,则返回值为0。

atexit可注册的exit hander数量受ATEXIT_MAX控制。POSIX.1规定,若调用了exec, 则之前注册的exit handler将被清除。

环境变量

环境指针extern char **environ,指向环境列表,其中每个指针指向一个name=value形式的环境字符串。 这些字符串通常被保存在进程地址空间中栈以上的区域。 getenvputenvsetenvunsetenv用于操作环境列表。 当需要增加环境变量时,若原有空间不够,则调用malloc为新串分配堆空间,并mallocrealloc一个新的环境列表, 将原有的列表复制到新列表中,更新environ指针指向堆上的新列表。realloc可能导致数据复制,小心指针失效。

非局部跳转

manpage说

The stack context will be invalidate if the function which called setjmp returns.

另外,longjmp后是否恢复栈上auto变量的值取决于实现和优化。 将变量声明为volatile将使其保留longjmp前最后一次更新的值。

进程资源限制

soft limit为当前限制,hard limit是对soft limit的限制,即soft limit可调节的最大值。 在hard limit范围内调节soft limit,或降低hard limit不需要超级用户权限。上调hard limit需超级用户权限。

setrlimit设置的资源限制将影响调用进程和所有子进程。

阅读全文 →

APUE笔记-系统数据文件

2013-03-04 13:44:09

密码文件

读取/etc/passwd中的信息:

  • 获取某个uid的账户信息getpwuidls调用)
  • 获取某个用户名对应的账户信息getpwnamlogin调用)

getpwent遍历所有/etc/passwd中的条目,一次返回一个struct passwd *,不保证顺序。 setpwent打开密码文件,重置到文件开头。endpwent关闭文件。

为保护加密的用户密码,防止“猜密码->加密->比较”形式的攻击,系统将加密密码保存在/etc/shadow,权限为rw-------。 使用getspnam获取一个struct spwd *getspentsetspentendspent以类似方式遍历shadow password文件。

组文件

遍历/etc/groupgetgrentsetgrentendgrent

每个组可设置组密码(gpasswd命令),用newgrp命令可切换当前用户的egid(id命令可查看)。

Supplementary GID

NGROUPS_MAX变量限制一个用户最多可加入的组数,通常为16。

getgroups获取当前用户加入的所有组的gid。

initgroups遍历/etc/group找出某个用户加入的组,并调用setgroups设置该用户的组列表。

阅读全文 →

APUE笔记-标准IO库

2013-01-18 17:00:42

标准I/O库(以下简写为stdio)屏蔽底层细节,如缓冲管理、选择最佳的I/O chunk size等等。

流和FILE

用stdio函数打开或创建一个文件称为"associate a stream with the file", 表示为FILE结构,其中封装了fd、文件指针、buffer指针、error flag、buffer大小等成员。

流可按其orientation区分为byte-oriented和wide-oriented, 即用单个字符还是宽字符来表示流数据。规则如下:

  • when a stream is created, it has no orientation
  • if a multibyte I/O function is used on a stream without orientation, it's set to wide-oriented
  • if a byte I/O function is used on a stream without orientation, it's set to byte-oriented

fwide函数可设置或查询一个流的orientation。

Stdio

各种函数

  • 打开流
    • fopen, freopen, fdopen
  • Character-at-a-time I/O
    • getc, getchar, fgetc, ungetc
    • putc, putchar, fputc
    • 注意getcputc可能被实现为宏,所以要避免参数的副作用
  • Line-at-a-time I/O
    • fgets, gets
    • fputs, puts
  • Direct (binary) I/O
    • fread
    • fwrite
  • 格式化
    • printfscanf家族
  • 定位
    • ftell, rewind, fseek, fgetpos, fsetpos
  • 临时文件
    • 文件名:tmpnam, tempnam
    • 文件:tmpfile(打开方式为wb+fclose或进程终止时被清除), mkstemp(返回fd,文件可读写,不自动清除)
  • Misc
    • ferror, feof, clearerr, fileno
阅读全文 →

APUE笔记-文件与目录

2013-01-09 14:50:23

文件系统的概念结构

层次关系

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
disk            -> partition
partition       -> boot block 
                   super block 
                   cylinder groups
cylinder groups -> super block copy
                   cylinder group info
                   inode map
                   block bitmap
                   inodes
                   data blocks

inodes和data blocks

每个目录和文件用一个inode记录其元信息。inode保存了指向文件data block或目录directory block的指针。 文件的data block包含其实际数据,目录directory block包含一系列directory entry, 每个entry记录了该目录下文件的inode号(ino_t)和文件名。

由于inode号指定了同一文件系统中的inode,所以硬链接不可跨越文件系统。

同一文件系统中mv文件只需要unlink旧的directory entry并创建一个新的,无需移动文件数据。

阅读全文 →

新年快乐2013

2012-12-31 15:47:16

2012年终于过了!!!再不写总结就不幸福了!!!

毕业这个恼人的问题终于解决了。五六月份的时候想了一个比较冷门的点子,随便做了点实验, 扔出去一篇论文竟然中了个Rank C的世博会。其实文章很水,中的原因主要是因为点子太冷,很少被人提及, 于是也就显出一点微弱得可怜的新意了。借此机会去美帝游荡了一趟,也算是开了下眼界。

有了篇还算说得过去的论文,也就不怕答辩的时候被人找茬了。 我毕设论文用了LaTeX排版,模板是史上流传下来的,实验室上一届有师兄就是用的这个模板。 学校的毕设论文交上去首先要进行形式审查,也就是学校/学院找来一帮人, 翻看每个人的论文,标出所有他们觉得格式有问题的地方, 再发下去让你改。审查那两天我正好在美帝,天天半夜收到北京同学的短信通知近况, 一大早就得起来改。带着困意改论文其实问题不大,关键是他们让改的地方着实令人哭笑不得。 有一条意见是说我论文目录中章节标题和页码之间的点太大。 其实我想一下用word的同学们就平衡了,光是引用编号和参考文献就已经能让他们头疼了……

找工作也是今年的大事之一。我给自己列的想去的地方只有两个,豆瓣和淘宝,方向是偏后台的研发。 喜欢豆瓣是因为它的产品、技术风格、氛围和公司文化;喜欢淘宝是因为它们的平台优势和技术实力。 那段时间总共投了大概十份简历,但大都因为口味的原因翘掉了。 到最后我花时间准备了的公司都挂了,拿到offer的笔试面试都是没怎么准备、临场发挥的结果。

阅读全文 →

Austin

2012-12-30 09:24:18

我还是决定把这次行程记录在这儿。

因为12月中旬有硕士毕业答辩,要赶着回来忙相关的事情,所以这次去Austin开会讲论文,行程是很紧张的, 除了参会之外基本上就没有太多时间到处游荡了。来回的机票都是Delta Airlines的, 去的时候航班很折腾人,先从北京到Seatle,再转去Minneapolis,最后才到Austin,路上一共差不多18个小时。 不过毕竟是第一次出国,心里还是很激动的,这点旅途劳累也就算不上什么了。

从北京到Seatle的航班上大都还是中国人,其中有不少是出国看望孩子的父母。 好像这种国际航线旅客都不太多,最后我旁边的两个座位都是空的,整个机舱里也有不少空座位。 因为是向东的飞行,当机舱逐渐被黄昏和黑夜包围时,真有一种时间加快了的感觉。我想着后面还有两趟航班, 为了保存点体力,也顺便调整下时差,飞机上大多数时间里我都在睡觉。 其间空乘发来了传说中的I-94表格和海关报关表让填写,之前从网上查过攻略,很快就搞定了。 期望从飞机上看到太平洋的想法果然是不现实的。四周一片漆黑,只能看见飞机下厚厚的云层, 像是覆雪的丘陵起起伏伏,蔓延到黑暗的边缘。

阅读全文 →