分享
 
 
 

Linux网络代码导读v0.2

王朝system·作者佚名  2006-11-24
窄屏简体版  字體: |||超大  

1 前言

许多人在分析linux代码时对网络部分(主要是src/linux/net,src/linux/include/net及src/linux/include/linux目录下的文件)比较感兴趣,确实,尽管已经从书本上学到了大量的TCP/IP原理,不读[url=http://www.pccode.net].net" class="wordstyle"源码的话,头脑中还是建立不起具体的印象。而分析这部分代码的一个问题便是代码众多而资料很少。这篇文章的目的就是勾勒出一个框架,让读者能够大致能够了解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想发送数据时,程序中会调用如下语句(如果用sendto函数的话会走类似的流程,略):

...

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.

[1] [2] [3] 下一页

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有