dedecms织梦内容管理系统    
首页 | java | C/C++ | PHP | 操作系统 | ajax | 脚本编程 | 安全技术 | 本站下载页 | flex | CRM | 专题 | QQ群 | 测试中心 | 会员中心 | 积分规则
  当前位置:主页>操作系统>linux>文章内容
Linux网络代码导读v0.2
来源:未知     作者:    

1 前言

许多人在分析linux代码时对网络部分(主要是src/linux/net,src/linux/include/net及
src/linux/include/linux目录下的文件)比较感兴趣,确实,尽管已经从书本上学到了大
量的TCP/IP原理,不读源码的话,头脑中还是建立不起具体的印象。而分析这部分代码的
一个问题便是代码众多而资料很少。这篇文章的目的就是勾勒出一个框架,让读者能够大致
能够了解TCP/IP究竟是怎么工作的。以前见到的许多代码分析都是基于2.0内核的,在新的
内核中许多变了名字,这尤其给初学者带来了困难,本文是以2.4.0-test9的代码作例子,
这样对照代码时可能更清晰些。

其实网络部分的代码我只对防火墙部分一行行仔细分析过,其他许多地方也只是一知半解,
如果理解有误,欢迎指正。

建议在看本文的同时,用source insight(www.soucedyn.com)建立一个项目,同时看代码,
这样可能效果更好点。我也用过其他的一些工具,但在分析大量的代码的时候,没有一个工
具比它更方便的了。


2 正文

ISO的七层模型都非常熟悉了,当然,对于internet,用四层模型更为适合。在这两份模型里,
网络协议以层次的形式出现。而LINUX的内核代码中,严格分出清楚的层次却比较困难,因为
除了一些"内核线程(kernel thread外)",整个内核其实是个单一的进程。因此所谓"网络层"
,只是一组相关的函数,而各层之间大多通过一般的调用的方式完成交互。

而从逻辑上,网络部分的代码更应该这样分层更为合理:
.BSD socket层:这一部分处理BSD socket相关操作,每个socket在内核中以struct socket结构体现。
这一部分的文件主要有:/net/socket.c /net/protocols.c etc
.INET socket层:BSD socket是个可以用于各种网络协议的,而当用于tcp/ip,即建立了AF_INET
形式的socket时,还需要保留些额外的参数,于是就有了struct sock结构。
文件主要有:/net/ipv4/protocol.c /net/ipv4/af_inet.c /net/core/sock.c etc
.TCP/UDP层:处理传输层的操作,传输层用struct inet_protocol和struct proto两个结构表示。
文件主要有:/net/ipv4/udp.c /net/ipv4/datagram.c /net/ipv4/tcp.c /net/ipv4/tcp_input.c
/net/ipv4//tcp_output.c /net/ipv4/tcp_minisocks.c /net/ipv4/tcp_output.c
/net/ipv4/tcp_timer.c etc
.IP层:处理网络层的操作,网络层用struct packet_type结构表示。
文件主要有:/net/ipv4/ip_forward.c ip_fragment.c ip_input.c ip_output.c etc.
.数据链路层和驱动程序:每个网络设备以struct net_device表示,通用的处理在dev.c中,
驱动程序都在/driver/net目录下。

网络部分还有很多其他文件,如防火墙,路由等,一般根据看到名字便能猜测出相应的处理,此处不再赘述。

现在我要给出一张表,全文的内容就是为了说明这张表(如果你觉得我在文章中的语言比较乏味,尽可
抛掉他们,结合这张表自己看代码)。在我最初看网络部分代码时,比较喜欢《linux kernel internals》
的第八章的一段,其中有一个进程A通过网络远程向另一进程B发包的例子,详细介绍了一个数据包如何
从网络堆栈中走过的过程。我觉得这样可以更迅速的帮助读者看清森林的全貌,因此本文参照这种结构来
叙述。

^
| sys_read fs/read_write.c
| sock_read net/socket.c
| sock_recvmsg net/socket.c
| inet_recvmsg net/ipv4/af_inet.c
| udp_recvmsg net/ipv4/udp.c
| skb_recv_datagram net/core/datagram.c
| -------------------------------------------
| sock_queue_rcv_skb include/net/sock.h
| udp_queue_rcv_skb net/ipv4/udp.c
| udp_rcv net/ipv4/udp.c
| ip_local_deliver_finish net/ipv4/ip_input.c
| ip_local_deliver net/ipv4/ip_input.c
| ip_recv net/ipv4/ip_input.c
| net_rx_action net/dev.c
| -------------------------------------------
| netif_rx net/dev.c
| el3_rx driver/net/3c309.c
| el3_interrupt driver/net/3c309.c

==========================

| sys_write fs/read_write.c
| sock_writev net/socket.c
| sock_sendmsg net/socket.c
| inet_sendmsg net/ipv4/af_inet.c
| udp_sendmsg net/ipv4/udp.c
| ip_build_xmit net/ipv4/ip_output.c
| output_maybe_reroute net/ipv4/ip_output.c
| ip_output net/ipv4/ip_output.c
| ip_finish_output net/ipv4/ip_output.c
| dev_queue_xmit net/dev.c
| --------------------------------------------
| el3_start_xmit driver/net/3c309.c
V



我们假设的环境如下:有两台主机通过互联网联在一起,其中一台机子运行这一个进程A,
另外一台运行进程B,进程A将向进程B发出一条信息,比如"Hello",而B接受此信息。
TCP处理本身非常复杂,为了便于叙述,在后面我们将用UDP作为例子。


2.1 建立套接字

在数据发送之前,要建立一个套接字(socket),在两边的程序中都会调用如下语句:

...
int sockfd;
sockfd=socket(AF_INET,SOCK_DGRAM,0);
...

这是个系统调用,因此会通过0x80中断进入系统内核,调用内核中的相应.当寻找
系统调用在内核中的对应流程时,一般前面加入"sys_"再找就是了,如对fork来说,就是
调用sys_fork。但是socket相关调用有些特殊,所有的这类调用都是通过一个入口,即
sys_socketcall进入系统内核,然后再通过参数调用具体的sys_socket,socket_bind等。

sys_socket会调用sock_create产生一个struct socket结构(见include/linux/net.h),
每个套接字在内核中都有一个这样的结构对应,在初始化了此结构的一些通用成员后(如
分配inode,根据第二个参数为type项赋值等),会根据其一个参数作响应的调度,即这
一句:
...
net_families[family]->create(sock, protocol);
...

我们的程序的第一个参数是AF_INET,所以此会指向inet_create();(net_families
是个数组,保留了网络协议族(net families)的信息,而这些协议族用sock_register加载。)

在struct socket结构结构中最重要的信息保留在struct sock结构中,这个结构在网络代码中经常
使用,建议把它和其他常见结构(如struct sk_buff)打印出来放在手边。在inet_create会为此
结构分配内存,并根据套接字类型(其实就是socket的第二个参数),作各自不同的初始化:
...
if (sk->prot->init)
sk->prot->init(sk);
...

如果类型是SOCK_STREAM的话会调用tcp_v4_init_sock,而SOCK_DGRAM类型的socket没有额外的初始化了,
到此socket调用结束。

还有一个值得注意的地方是当inet_create()调用完后,会接着调用sock_map_fd,这个
中会为套接字分配一个文件描述符并分配一个file文件。在应用层便可象处理文件一样
处理套接字了。

开始的时候可能有些流程难以跟下去,主要便是这些的实际指向会根据类型变化。


2.2 发送数据

当进程A想发送数据时,程序中会调用如下语句(如果用send的话会走类似的流程,略):
...
write(sockfd,"Hello",strlen("Hello"));
...

write在内核中对应的函数就是sys_write,此首先根据文件描述符找到struct file结构,如果此文件
存在(file非空)且可写(file->f_mode & FMODE_WRITE为true),便调用此文件结构的写操作:
...
if (file->f_op && (write = file->f_op->write) != NULL)
ret = write(file, buf, count, &file->f_pos);
...

其中f_op是个struct file_operations结构,在sock_map_fd中将其指向socket_file_ops,其
定义如下(/net/socket.c):
static struct file_operations socket_file_ops = {
llseek: sock_lseek,
read: sock_read,
write: sock_write,
poll: sock_poll,
ioctl: sock_ioctl,
mmap: sock_mmap,
open: sock_no_open, /* special open code to disallow open via /proc */
release: sock_close,
fasync: sock_fasync,
readv: sock_readv,
writev: sock_writev
};

此时wirte函数显然指向了sock_write,我们跟下去看,此将一个字符串缓冲整理成struct msghdr,
最后调用了sock_sendmsg.

sock_sendmsg中的scm_send我不了解(scm是Socket level control messages的简写),好在它也不是很关键,
我们注意到这句:
...
sock->ops->sendmsg(sock, msg, size, &scm);
...

又是个函数,sock->ops在inet_create()中被初始化,由于我们我们是UDP的套接字,sock->ops
指向了inet_dgram_ops(即sock->ops = &inet_dgram_ops;),其定义在net/ipv4/Af_inet.c中:
struct proto_ops inet_dgram_ops = {
family: PF_INET,

release: inet_release,
bind: inet_bind,
connect: inet_dgram_connect,
socketpair: sock_no_socketpair,
accept: sock_no_accept,
getname: inet_getname,
poll: datagram_poll,
ioctl: inet_ioctl,
listen: sock_no_listen,
shutdown: inet_shutdown,
setsockopt: inet_setsockopt,
getsockopt: inet_getsockopt,
sendmsg: inet_sendmsg,
recvmsg: inet_recvmsg,
mmap: sock_no_mmap,
};

因此我们要看得便是inet_sendmsg()函数了,而马上,这个函数又通过函数调用了另一:
...
sk->prot->sendmsg(sk, msg, size);
...

我们不得不再次寻找其具体指向。看到这里,说点题外话,怎么才能找到其具体定义呢?我一般是这样:
对上例而言,sk是个struct sock结构,到其定义(linux/net/sock.h中)出看到prot是个struct proto
结构,此时我们便在源代码树中寻找所有此结构的实例(这些诸如跳到定义,寻找引用等工作在source
insight中实在太方便快速了^_^),很快便会发现诸如udp_prot,tcp_prot,raw_prot等,猜测是用了
udp_prot,便再找一下它在源代码中的引用情况,果然发现在inet_create中有这么一句:
...
prot=&udp_prot;
...

其实如果前面看inet_create时仔细一点会早点发现了,但我总没有这么细心:)。

[1]  

 

 

上一篇:linux kernel 2.4.5 ipv4 socket层的一点解释   下一篇:Linux内核模块编程之字符设备文件
[收藏] [推荐] [评论(0条)] [返回顶部] [打印本页] [关闭窗口]  
用户名: 新注册) 密码: 匿名评论
评论内容:(不能超过250字,需审核后才会公布,请自觉遵守互联网相关政策法规。
 §最新评论
  热点文章
·Linux常用基本命令及应用技巧
·写得蛮好的linux学习笔记
·学会在Linux下对硬盘分区
·找回Redhat的超级用户密码
·Linux下C语言编程
·GDB教程
·Cron服务配置详解
·Linux与Unix二大操作系统编程的
·Linux 2.6 内核的嵌入式系统应用
·Linux和Windows系统调用的比较
·vim命令(一)
·vim 命令(二)
  相关文章
·linux kernel 2.4.5 ipv4 socket
·Linux内核模块编程之字符设备文
·linux内核模块和驱动程序的编写
·Unix(Linux)C编程问题精粹
·Linux 进程调度原理
·使用 GDB 调试 Linux 软件
·C常用的LinuxC语言函数库
·Linux核心代码分析
·关于Linux下编写和编译程序的几
·Linux库函数升级权威经验
·Linux下C语言编程--信号处理函数
·Linux ioctl() Primer
  相关信息
copy right @ 百家拳软件项目研究室 2007 辽ICP备07011763