写程序,严禁偷懒
作者 佚名技术
来源 Linux系统
浏览
发布时间 2012-05-12
调程序真的不能偷懒、想当然,真的,实实在在的体会到. 做了半年的linux内核编程,前段时间一直在对socket网络通信的内核函数作改动,为了某种科研目的(呵呵,老板一再强调,科研出成果之前,一定不能外泄,不具体解释什么目的了).目标很简单,要让两个进程共享一个socket,并且在读/写的时候对数据进行同步.其实就linux文件系统本身而言,允许两个进程共享文件.其具体方式为两个dentry可以指向一个inode结构,dentry再分别连上file结构再挂向进程的文件描述符表,并在inode结构中增加引用计数就OK了.但是对于socket,虽然作为一种特殊文件,并不提供这种共享方式. 于是,对socket通信所涉及的一些基本数据结构,以及主要操作函数进行改动.为了简单化,我努力将编码限定在Linux内核中的BSD层(socket层),也就是net/socket.c这个C文件,并未触及TCP层,当然,对文件系统相关改动不仅限于此.改写很顺利,sys_socket(), sys_bind(), sys_listen(), sys_accept(), sys_connect()等函数很快完成,无非是添加一些同步点,主进程该做的做,从进程不该做的别做,只是在sock_attach_fd()时增加inode的引用计数上遇到了点小麻烦. 可是当修改到读数据,或者说接收数据相关函数时,问题出现了.Socket读写有两条路可走,就读数据而言,一是socket自身的receive(),二是文件系统的read(),对用户层提供了不同的系统调用接口,其调用过程分别为: socket sys_recv()->sys_recvfrom()->sock_recvmsg()->__sock_recvmsg() fs sys_read()->sock_aio_read()->do_sock_read()->__sock_recvmsg() 可见,可以在__sock_recvmsg()上将其截获.在socket层,数据包为msghdr结构,,只需在函数__sock_recvmsg()中将msg一分为二,则两个进程能同时获得数据.在具体实现过程中,strcut msghdr msg中的msg->msg_iov是数据块指针,msg->msg_iovlen是数据块长度.主进程从下层接收数据,封装入msg中,再将msg结构及msg中指针所指的全部数据用memcpy()复制进内核堆区.从进程直接从内核堆区的msg生成自己的msg,绕过下层协议,达到数据一分为二的目的.思路很清晰,可是在测试程序中,主进程能完整地接收到客户端数据,从进程打印出的字符串一直为空.我百思不得其解,对内核函数及复制过程检查了多次,毫无头绪.
冯师兄不愧为linux内核高手,一语道破玄机,跟一下测试程序中的字符串指针值,看看是不是在内核真的memcpy在这个地址上的字符数组里了.我开始半信半疑,两个进程在被fork后装载的相同的代码,执行结果不一致,怎么会是用户空间指针问题呢?反正想不出什么解决办法,试一试吧,这一跟踪,吓了我一大跳,这个指针值,进了内核果然会改变,在执行完receive之后,地址发生了偏移,向后移动了正好msg->msg_iovlen的长度.这下我全明白了,复制的数据块内容是从空数据开始的,打印出来的当然为空了.仔细一查,果然,在__sock_recvmsg() -> sock_common_recvmsg() -> tcp_recvmsg() -> skb_copy_datagram_iovec() -> memcpy_toiovec()中,我找到了iov->iov_len -= copy; iov->iov_base = copy; 隐藏的够深…… 聪明反被聪明误啊,本想在表面上,以简单的方式达到目的,结果却忽略了实质,走了更大的弯路.另外一个方面,在调试过程中想当然的认为某某地方100%不会有问题,结果问题却恰恰出在那里. 总结:写程序不能偷懒,不能投机取巧,不能想当然. 还有,感谢冯师兄.
|
||
凌众科技专业提供服务器租用、服务器托管、企业邮局、虚拟主机等服务,公司网站:http://www.lingzhong.cn 为了给广大客户了解更多的技术信息,本技术文章收集来源于网络,凌众科技尊重文章作者的版权,如果有涉及你的版权有必要删除你的文章,请和我们联系。以上信息与文章正文是不可分割的一部分,如果您要转载本文章,请保留以上信息,谢谢! |
你可能对下面的文章感兴趣
关于写程序,严禁偷懒的所有评论