今晚

2014-03-31 03:19:24

记住这个凌晨。 活在世上真是太不可思议了。

活在这珍贵的人间
太阳强烈
水波温柔
一层层白云覆盖着

踩在青草上
感到自己是彻底干净的黑土块

活在这珍贵的人间
泥土高溅
扑打面颊
活在这珍贵的人间
人类和植物一样幸福
爱情和雨水一样幸福

阅读全文 →

Altan's Show

2014-03-16 23:23:59

At the Forbidden City Concert Hall.

We're young but we're daily growing.

阅读全文 →

新年快乐2014

2013-12-29 15:39:21

一月的时候,我还在赖在学校里享受没有闹钟的生活。这样的生活是如此闲适,以至于我还隔三差五 地应hui哥的邀请去游个泳。过完年回来,找房子成了第一要务。搜遍了各大网站,最后还是向中介低了头。 那几天里满街飞扬的沙尘、略微转暖的天气,混杂着摊贩的嘈杂和中介的忽悠,在泛黄的天幕下向我招手, 哦,这就是芍药居。

三月群里组织饭局,被人问起工作,我总是跟他们说,“我下个月就去报到!”。你看最后表明语气的感叹号, 听起来满怀对未来的憧憬是不是?其实我还挺喜欢这种无忧无虑的中间状态,就像是接受一种能让人感到舒适, 却会缓慢夺人性命的毒药。所幸的是,时间的脚步终将把我拉回现实。

正式入职之后的三个多月里,我做的事情只有一件。装怪的说法叫做“努力让自己变成团队中的一员”, 其实就是江湖上说的“刷存在感”。我在学校里累积的存在感,一点一点地被新的环境和新的工作消磨殆尽。 每天在工位上脑补各种名词、各种黑话,阅读各种代码,我这才发现周报这个东西有多难写。 我作为组里的最低水平,每天的工作就是秀下限。不过,现在想起来,让我感到庆幸的是, 我在那段时间里好歹还算做了些像样的笔记,但愿以后再来做这些事情的人不要再像我一样步履维艰。

阅读全文 →

Cara Dillon's show

2013-12-14 14:11:09

At Mako Live House, joy and pity were imprinted.

阅读全文 →

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设置该用户的组列表。

阅读全文 →