DongHao: 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呢,它就用这个简单办法不也解决了问题么。