Anrs Hu

anything what I know...

Urllib2 可能比 Urllib 阻塞得更久

socket._fileobject 的读缓冲区默认大小(_rbufsize)是 8192,也就是说调用 readline() 时,默认情况下它会先读 8KB 的数据到 buffer,然后再从中取出一行数据返回给调用方。如果关闭了读缓冲,readline() 会一个字节接一个字节的读数据,只要读到 \n 就立即返回。

假设一个 HTTP 响应头中的 Transfer-Encoding 是 chunked,并且每块 chunk 都是一行小于 8KB 的数据,然后客户端希望一收到 chunk 就立即对其进行处理。但是因为 readline() 希望先读 8KB 的数据,只要不够 8KB 就一直阻塞,直到收到足够的 chunk 为止(或者 EOF)。

而 urllib.urlopen 和 urllib2.urlopen 创建 socket._fileobject 的行为是不一样的,urllib 关闭了读缓冲,而 urllib2 则使用了默认的 8KB 大小的缓冲区。

所以第一次调用 urllib2.urlopen(URL).readline() 时,会比第一次调用 urllib.urlopen(URL).readline() 慢得多。如果服务器针对第二块 chunk 的处理需要较长时间的话,那 urllib2 的 readline() 就会一直阻塞在第一次的 readline() 调用上,哪怕已经收到了一行数据也是如此。要解决这个问题只要在 readline() 前把 response.fp._rbufsize 设为 0 就可以了。

Orphan or Not

进程死后,他的子进程会变成孤儿,但某些场景下我们希望父死子殉,为此 hongqn 专门搞了 CaoE 来自动支持这件事。(CaoE 这个名字来自这里)

但实际上,在进程死时,会给他的子进程们重新寻父,寻父无果的情况下才会让 init 成为这些子进程的父进程。寻父函数是 kernel/exit.c:find_new_reaper(),这个函数顶上有段注释是:

/*
 * When we die, we re-parent all our children.
 * Try to give them to another thread in our thread
 * group, and if no such member exists, give it to
 * the child reaper process (ie “init”) in our pid
 * space.
 */
static struct task_struct *find_new_reaper(struct task_struct *father)

注释写得很清楚,并不是所有情况下该函数都会返回 init 进程的,下面是寻父过程的代码段:

thread = father;
while_each_thread(father, thread) {
    if (thread->flags & PF_EXITING)
        continue;
    if (unlikely(pid_ns->child_reaper == father))
        pid_ns->child_reaper = thread;
    return thread;
}

find_new_reaper() 一开始就遍历待死进程的线程组,试图找到其他还存活着的进程,如果找到了,就直接返回。遍历过程是通过 while_each_thread 宏和 next_thread() 函数实现的:

#define while_each_thread(g, t) while ((t = next_thread(t)) != g)

static inline struct task_struct *next_thread(const struct task_struct *p) {
    return list_entry_rcu(p->thread_group.next, struct task_struct, thread_group);
}

可以看到只有 find_new_reaper() 在待死进程的线程组中没有找到其他活着的进程,才会返回 init 进程。

/*
 * We can not clear ->child_reaper or leave it alone.
 * There may by stealth EXIT_DEAD tasks on ->children,
 * forget_original_parent() must move them somewhere.
 */
pid_ns->child_reaper = init_pid_ns.child_reaper;
…
return pid_ns->child_reaper;

顺便说下我在查进程终结的代码时,发现了 LKD 3rd 中文版这本神作,今天还跟员外在 twitter 上讨论该书的奇葩翻译来着。我目前看了大概过一半,基本上都是边看英文电子版边翻中文版撑过来的。比如对 mutex_trylock() 返回值的描述,原文是”returns one if successful and the lock is acquired and zero otherwise”,译文变成了“ 如果成功则返回1;否则锁被获取,返回值是0”(标点我都没改)。

我觉得原文是有点歧义,如果能写成”returns one if successful and the lock is acquired, and zero otherwise”就清楚明白了,可是,就算有歧义那函数名中的 trylock 总明白无误了吧,有 try 失败了还能得到锁的函数吗?好吧,就算函数名不足以说明问题,原文又有点歧义,那我直接翻代码吧,找到了 mutex_trylock() 的函数声明(在 <linux/mutex.h> 文件中):

/*
 * NOTE: mutex_trylock() follows the spin_trylock() convention,
 * not the down_trylock() convention!
 *
 * Returns 1 if the mutex has been acquired successfully, and 0 on contention.
*/
extern int mutex_trylock(struct mutex *lock);

看见没有,代码作者 Ingo Molnar 的注释可写得清楚明白准确无误没歧义的,还有个闪瞎黄金狗眼的硕大逗号。所以可以有理由怀疑,中文版某位译者的英文造诣绝对高过计算机造诣的,就这水平去译散文译哲学专著那多好多造福人类啊。

尝试 Octopress

原来 WordPress 的内容懒得导入 Octopress 了,直接猛击这里浏览吧!