06 2010存档

公用电话

| | Comments (0) | TrackBacks (0)
去年公司电话紧张,于是收了我的座机,让我和边上的同事(赵明华)共用一部。
于是我在旺旺上的签名写成:
”分机:70663(请门口赵大爷叫一声)“
我的旺旺名字(也就是花名)是”三百“。

结果,朋友不久就来了个消息:
“你们公司三百个人用一个电话号码?!“

如厕

| | Comments (2) | TrackBacks (0)
我到26层去查故障,顺便上个厕所。一进厕所就撞见苏宁。
苏:你搬下来了?
我:没有啊
苏:那你是专程来这里上厕所的?
我:是的
苏:难得啊。我代表26层全体马桶欢迎你。
我:嗯,你来代表,你是他们的“桶帅”嘛。马桶里面最帅的。
linux内核里对于要发送到网络上去的包,是通过sk_buffer结构组织的,这个结构主要就是包含一个指针和一个长度,指针指向要发送数据的开头处,长度当然就是要发送的长度。当我们使用send系统调用时,内核要把用户空间的数据拷往内核空间在(创建一个副本),然后构造sk_buffer指向这个内核空间里的副本,接着把sk_buffer排到队列里去,等待网卡处理。
后来诞生了sendfile,sendfile会直接将sK_buffer指向文件在内存中的cache,这样就节省了一次拷贝。
这里有个注意事项:sendfile不适合频繁改动的文件。假如你调用了一次sendfile,sk_buffer指向文件的某块cache,然后被放入队列;接着,你改动了文件的内容,那么排在发送队列里的那个sk_buffer指向的cache内容就变化了!那发出去的消息就是你改动后的数据。在不知道sk_buffer是否已发往网络的情况下,一边改动文件一边调用sendfile会造成发送出去的内容不确定。所以,sendfile更适合用在静态文件的场合。
当然,如果有个办法能查询sk_buffer是否真的发送出去了(而不是呆在发送队列里),那么“改一次文件内容,调一次sendfile;再改一次文件内容,再调一次sendfile”也是一个不错的节省拷贝时间的发送策略。
做一个文件系统(姑且叫它“蝗虫文件系统“),需要在进程退出时自动删除它打开的文件。另外此文件还支持poll(我自己做的,一般的文件系统不支持),支持poll当然需要等待队列,所以我把一个等待队列放在文件对应的struct inode里,实现该文件struct file_operations里的flush方法,在flush时删除文件并释放该等待队列(kfree)。
这样做似乎一切顺利,直到应用程序开始使用epoll:先在蝗虫文件系统里创建几个文件,再把它们的fd放入epoll(epoll_ctl),然后进程退出,如此多来几次,内核就panic了。还好我用的是QEMU,可以清楚看见在什么地方panic的,结果是 __fput --> eventpoll_release --> eventpoll_release_file --> ep_remove --> ep_unregister_pollwait --> remove_wait_queue,看看ep_unregister_pollwait的代码:

[fs/eventpoll.c --> ep_unregister_pollwait]
  1109 static void ep_unregister_pollwait(struct eventpoll *ep, struct epitem *epi)
  1110 {
  1111     int nwait;
  1112     struct list_head *lsthead = &epi->pwqlist;
  1113     struct eppoll_entry *pwq;
  1114
  1115     /* This is called without locks, so we need the atomic exchange */
  1116     nwait = xchg(&epi->nwait, 0);
  1117
  1118     if (nwait) {
  1119         while (!list_empty(lsthead)) {
  1120             pwq = list_entry(lsthead->next, struct eppoll_entry, llink);
  1121
  1122             ep_list_del(&pwq->llink);
  1123             remove_wait_queue(pwq->whead, &pwq->wait);
  1124             kmem_cache_free(pwq_cache, pwq);
  1125         }
  1126     }
  1127 }

在把多个文件句柄加入epoll里的时候,这些文件句柄对应的inode上的等待队列要被epoll挂在一个数据结构上(struct eppoll_entry),当进程退出,这些等待队列当然要从数据结构上拿掉,但是,这些等待队列已经被我kfree了,所以panic。看来把kfree放在flush是不行了,那放哪儿?有哪个调用是比eventpoll_release发生的更晚的?有!

[fs/file_table.c --> __fput]
  153 void fastcall __fput(struct file *file)
  154 {
  155     struct dentry *dentry = file->f_dentry;
  156     struct vfsmount *mnt = file->f_vfsmnt;
  157     struct inode *inode = dentry->d_inode;
  158
  159     might_sleep();
  160
  161     fsnotify_close(file);
  162     /*
  163      * The function eventpoll_release() should be the first called
  164      * in the file cleanup chain.
  165      */
  166     eventpoll_release(file);
  167     locks_remove_flock(file);
  168
  169     if (file->f_op && file->f_op->release)
  170         file->f_op->release(inode, file);
  171     security_file_free(file);
  172     if (unlikely(inode->i_cdev != NULL))
  173         cdev_put(inode->i_cdev);
  174     fops_put(file->f_op);
  175     if (file->f_mode & FMODE_WRITE)
  176         put_write_access(inode);
  177     file_kill(file);
  178     file->f_dentry = NULL;
  179     file->f_vfsmnt = NULL;
  180     file_free(file);
  181     dput(dentry);
  182     mntput(mnt);
  183 }

struct file_operations里的release方法就是在epoll释放之后调用的,所以应该把kfree挪到release里去执行。
照此改之,没有panic了。
不知道是我听错了还是别人说错了,我一直以为 tail -f 是通过poll实现的,而每个ext2(或ext3)文件都支持poll。当文件有新数据时,针对此文件的poll会返回结果。 

昨天想参考kernel是怎么让设备实现poll的,看了ext2文件系统,发现它没提供poll接口。于是自己写了个程序来试试,对着一个ext2文件epoll,结果,即使文件里的数据增加了,epoll_wait也没有任何反应。就socket支持poll,硬盘文件不支持的。那tail -f是怎么实现的?下了coreutils一看,喔,其实是每1秒检查一遍文件,看大小有否变化,如果有,则异步的read该文件,取得新数据,打印到终端......
想想也对,tail命令出现的很早,那时候说不定还没有poll呢,它就用这个简单办法不也解决了问题么。

关于存档

This page is an archive of entries from 06 2010 listed from newest to oldest.

05 2010 is the previous archive.

07 2010 is the next archive.

Find recent content on the main index or look in the 存档 to find all content.