免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12下一页
最近访问板块 发新帖
查看: 6122 | 回复: 11
打印 上一主题 下一主题

网络堆栈 架构 结合linux 2.4内核和linux 1.2.13网络堆栈 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2011-09-08 15:33 |只看该作者 |倒序浏览
刚刚学习请指教:
                   从Sock系统调用开始,都应该知道的是socket既作为系统内部进程间通信的一种系统调用,也是网络创建插口的系统调用。
1、插口的三个特征:网域,类型,规程

网域:说明插口属于哪一个网络,一族网络规程。节点命名的方法不同,如AF_INET为互联网插口,节点为IP地址;还有AF_IPX为Novell的IPX网插口,AF_X25为x.25插口。在一台计算机上的进程间为AF_UNIX.
  
类型:通信分为有连接和无连接两种,有连接的必须在传输数据时建立连接,而无连接的不需建立连接,只要建立了插口就可以直接进行传输数据。
  
规程:网域和类型就可以决定一种规程。AF_INET为无连接,规程为UDP(通信平台实现点对点通信平台)。在linux2.4中主要说明了unix域的插口。unix域根据对待网络的模式也分为两种,但是都提供可靠报文。

在网络环境下,由于网络设备有噪音或者其他不可靠原因分为有连接和无连接,有连接企图在不可靠下建立可靠报文传播,如TCP协议建立连接在发送数据之前,而UDP协议则不需要建立连接。

2、 linux系统调用

2.1 socket系统调用为通用调用函数
socket 调用总入口为sys_socketcall()(在net/socket.c)。其中有关于socket调用的所有函数,为sys_xx格式。

2.2 socket插口的创建。
有连接的模式为server/client关系。

2.2.1: 建立连接
2.2.1.1 分为SYS_LISTEN,server方首先需要向内核“挂号”。
2.2.1.2: SYS_CONNECT:在有连接的模式,client一方通过connect向server方挂号。
2.2.1.3: SYS_ACCEPCT: server通过accept()来接受连接请求。
2.2.1.4: send()为有连接模式设计的。但最终有sys_sendto()实现。

2.2.2 无连接报文发送
2.2.2.1: SYS_SENDTO:插口一经建立(并bind到一个插口地址)就可以通信。重要数据结构为msghdr,代表这一个报文。
2.2.2.2: SYS_RECV、SYS_RECVFROM、SYS_RECVMSG: 为当从某个插口s接受报文的。其中sys_recv是通过sys_recvfrom()实现,同sys_send和sys_sendto()关系。

3、创建插口流程
3.1:                sys_socket()
                         |
                  sock_create()
                         |                                             
      ____________________________________
      |                    |                     |
request_module()      sock_alloc()          NULL
                          |
                    socki_lookip()
                          |                                               
____________________________________________
    ||                                         ||
unix_create()                           inet_create()
    |                                 
unix_createl()
    |  
sock_init_data()
        |
unix_insert_socket()
         |
__unix_insert_socket()   END
         |
  sock_map_id()
  _______|_______
          |               
       d_alloc()
          |
        d_add()
          |
       mntget()
          |
       fd_install()
          |
        END
sys_socket() :使用了一种特殊文件系统sockfs,通过kern_mount()安装。
sock_create(): 在sockfs文件系统创建一个节点,socket数据结构类型。
request_module(): 如果指定网域的驱动程序没有安装,那么以模块形式安装进系统。
sock_alloc(): 创建文件节点以供映射。
unix_create(): 在unix域用unix_create(),在inet域用inet_create(),决定权在socket结构中的proto_ops,由字符"UNIX",或者"INET"网域字符串决定。
unix_create1(): 分配sock数据结构类型sk,并初始化,其中的sock_init_data()也是初始化队列、标志为等作用,sock结构中protinfo(proto:linux堆栈1.2.13)为定义一系列函数,往下调用。
在UNIX域中插口地址为一个节点(文件)的路径名.Inet域中IP地址和端口号作为插口地址。
unix_insert()为__unix_insert_socket()加锁,使进入临界区,只有一个线程或进程可操作。
__unix_insert_socket(): 为sock结构的单链队列,除此队列外,bind会根据地址将sock结构杂凑到另一个地址队列中去。
sock_map_id(): 此函数为了使sock和socket文件可以映射到file节点,使从文件系统的角度可以处理此sock节点。
d_alloc(): 为插口分配一个目录项。
mntget(): 获得对应的文件系统挂载点并赋值给f_vfsmnt。
fd_install(): 使是将file安装到当前进程文件描述符中。
结束unix域。


                                                             连载。。。。。。。

评分

参与人数 1可用积分 +2 收起 理由
瀚海书香 + 2 多谢分享!

查看全部评分

论坛徽章:
6
金牛座
日期:2013-10-08 10:19:10技术图书徽章
日期:2013-10-14 16:24:09CU十二周年纪念徽章
日期:2013-10-24 15:41:34狮子座
日期:2013-11-24 19:26:19未羊
日期:2014-01-23 15:50:002015年亚洲杯之阿联酋
日期:2015-05-09 14:36:15
2 [报告]
发表于 2011-09-08 16:45 |只看该作者
回复 1# wangzhen11aaa
期待下文。。。

论坛徽章:
0
3 [报告]
发表于 2011-09-08 21:49 |只看该作者
接:::::::::::::::::::::::::::::::::::::::::
                               |应用层|    应用程序
————————————————————————————————————————————
|                                  |                                                 |
| 表示层 ---------  | BSD Socket 层 |  socket.c        内核       |
| 会话层----------|INET Socket 层| af_inet.c ------------------------  |
|                                    |                        |               |      |   |
|  传输层 L5                         |<---------- tcp.c| TCP | udp.c| UDP| '''|   |
|                                    |                                                 |
|   网络层      L4               |   IP 层  | ip.c                                        |
|                                    |                                                |
| 链路层 L3                    |  设备接口层 | arp.c dev.c                            |
|                                    |                                                |
|                              | 硬件层 | 设备驱动程序                               |
---------------------------------------------
                                       |
   物理层                    |  网络设备物理接口层  |          硬件设备
----------------------------------------------
   BSD Socket 层和 Inet Socket 层函数并不实际处理,只是进行检查后层层下调。调用下层函数通过socket结构中ops字段完成的。ops字段为proto_ops结构类型。inet层对传输层的调用是通过sock结构的prot字段完成的。
如图所示,sock结构中的prot会因为协议不同而走不同路径。
struct proto_ops{
int family;
int (*create) (struct socket *sock,int protocol);
......
};
sock_create(int family, int type, int protocol, struct socket **res)
其中type 被赋值给sock->type = type;
SOCK_STREAM套接口 为TCP协议

    套接口中的流与UNIX中管道的概念相近.
    字节流中没有分界线,也没有边界;没有记录的长度工块的大小,在接收端也不存在分组的概念,在接收端获得的所有数据都返回到调用者的缓冲区中
有序性。
SOCK_DGRAM 为UDP协议

    当我们不需要数据传输的绝对有序性时和有需要数据传输的可靠性时,可以考虑使用SOCK_DGRAM.
    特性:
    1)分组发送后,可能无序的到达接收端。
    2)分组可以可能发生丢失。
    3)数据报分组有尺寸的大小限制,如果超出,可能会无法传送。
    4)分组可以在不建立连接的情况下被发送到远程进程,这就允许本地进程每次将消息发送给不同IP地址上的同样的端口。
SOCK_SEQPACKET

    本套接口对于X.25和AX.25协议非常重要,它与SOCK_STREAM的差别:SOCK_STERAM不保留消息边界,而SOCK_SEQPACKET保留.
    特性:
    1)保留消息边界
    2)数据字节的接收顺序与发送顺序一致
    3)保证发送数据字节被无错地传送到接收端
    4)数据是通过一对连接的套接口进行传送的


等等其他类型。

family在create 函数在sock_create()中 net_families[family]->create(sock, protocol); family为网域类型。每种网域都有net_proto_family数据结构,系统初始化时,指向相应网域的数据结构会填入数组net_families[]中,否则系统不支持给定网域,通过数组中对应元素来指向初始化函数,此结构只在linux2.4中。在linux网络堆栈中则为pops[]数组。在其中,直接将数据结构proto_ops赋值给pops
   
           inet_create()
                 |
    kmalloc(sizeof( *sk), GFP_KERNEL)
    分配数据结构类型sock,然后根据SOCK_XX类型来赋值,如TCP,prot = &tcp_prot。
     下面为数据结构proto结构将进行对传输层操作的调用,然后初始化sock数据结构。
struct proto{
struct sk_buff *( *wmalloc(struct sock *sk, unsigned long size, int force, int priority);
struct sk_buff * (* rmalloc(struct sock *sk, unsigned long size, int force, int priority);
void (*wfree) (struct sock *sk, struct sk_buff *sk, unsigned long size);
void  (*rfree) (struct sock *sk, struct sk_buff *sk, unsigned long size);
unsigned long (*rspace)(struct sock *sk);
unsigned long (*wspace )(struct sock *sk);
void (* close) (struct sock *sk, int timeout);
int (*read) (struct sock *sk, unsigned char *to, int len, int noblock, unsigned flags);
.........
};
对应TCP协议为tcp_proto,UDP协议为udp_proto.如果多个讨介子使用TCP协议就会有多个sock结构使用tcp_proto变量的函数,内核对这种管理通过在ptoto结构中定义sock_array数组来解决,使用该协议的套结字对应的sock结构被插入sock_array数组的链表中。其sock_array有256个元素,每个可以有256个链表,端口号可达65535个。


休息下。。。。。。

论坛徽章:
0
4 [报告]
发表于 2011-09-09 10:14 |只看该作者
接:
这里不得不介绍一些概况一些重要数据结构。
struct socket{
short type;  // 如上文。
socket_state state;   //插口的状态:SS_UNCONNECTED,SS_CONNECTED,SS_CONNECTING,SS_DISCONNECTED,第一个未连接(初始化时的状态),第二个为已经建立连接,第三个为连接正在被处理,最后为取消连接。//
long flags;   // SO_ACCEPTION 用于未连接socket,而且他们的进程在等待一个连接请求。其他SO_NOSPASE,SO_WAITDATA 表示不正常。
struct proto_ops *ops; //如上所示。
void *data;  保存私有数据。对于不同域的该指针指向不同数据结构。UNIX域指向unix_proto_data数据结构,INET指向sock数据结构。
struct socket *conn;  
struct socket *iconn;两个字段仅对UNIX域有意义。
struct socket *next; 构成socket结构链表。
struct wait_queue **wait; 等待队列。
struct inode *inode; 上文中创建的inode结构,在sock_socket->sock_create()->sock_alloc()中。
struct fasync_struct *fasync_list; fasync_struct构成的链表,用于同步文件读写。  
}

                             补充域协议协议初始化
域协议   初始化函数             说明
UNIX” unix_proto_init    本机进程间模拟网络的数据一种交换形式
                                               
"INET” inet_proto_ini     使用TCP/IP 协议族的网络数据包交换协议  
                                          
"802.2” p8022_proto_init 使用 802.2 协议    局域网数据包定义标准。
"SNAP” snap_proto_init 使 用 SNAP ( Subnetwork
                                Access Protocol)协议
"AX.25” ipx_proto_init “IPX” ipx_proto_init 使用 IPX 网络层协议进行数据包交换 Novell 网络协议)
“DDP” atalk_proto_init AppleTalk
使用struct net_proto数据结构
struct net_proto{
char *name //域协议名称。
void (*init_func) (struct net_proto*); /指针
}                                 
                             初始化
struct net_proto protocols[] =
#ifdef CONFIG_UNIX
{"UNIX", unix_proto_init},
#endif
........
};
举例在linux堆栈1.2.13中
inet_proto_init函数定义在/net/inet/af_inet.c中
调用 inet_proto_add; 进行传输层与网络层的连接。具体将inet_proto_base全局变量指向的inet_protocol结构队列中元素散列到struct inet_proto *inet_protos[]中,被网络层调用。inet_protol结构为一个传输层协议,定义了传输层协议号和接受函数。
                 数据包传送通道
                          | 硬件监听物理传输介质  |
                                      |
                                      |完全接受一个数据包
                                      |产生中断
                                      |
                        |系统调用驱动程序中断处理函数处理 |
                                       |
             数据从硬件到内核复制     |  封装为特定数据结构sk_buff,调用链路层函接口函数netif_rx                             | 将数据包暂存到内核维护的一个数据包队列backlog指向。                                  |
                                       |
                            |启动下半部处理函数net_bh|
                                      |从backlog队列中取数据包,进行本层处理后。
                                      |便利ptype_base指向的网络协议层队列,进行协议号匹配。调用接受函数。如ARP协议,调用arp_rcv函数,网络层调用ip_rcv.
                                      |                假设调用的为IP协议。
                                      |
                              | IP_rcv处理函数|
                                      |
                                      |用到inet_protos散列表进行匹配,找到inet_proto结构。                                  |              假设调用的为TCP协议
                              |tcp_rcv 处理函数 |
                                      |    完成从网络层到传输层的传递。
                                      |tcp协议使用的sock结构都在tcp_prot全局变量对应的ptoto结构之sock_array[]数组中。       |采用本地端口号为索引在tcp_prot对应的数组中找到sock结构队列                          |                  进而找到sock结构
                   | 将数据报挂在到sock结果缓冲队列中 (receive_queue)|
                                      |
                                      | 用户读取数据,首先根据文件描述符得到inode,然后得到socket结构,进而得到sock结构,    |       最后到达receive_queue队列。
                                      |
                                      |
                          |将数据包拷贝到用户缓冲区中|

举例: static struct inet_protocol tcp_protol =
{
tcp_rcv,//     处理函数。
NULL,// 初始化没有报文分段处理函数。
tcp_err, //错误控制
NULL,//
IPPROTO_TCP, //协议号
0,   //copy
NULL, //数据
"TCP" // 名字
};
其中 inet_protocol 数据结构
struct inet_protocol {
int (*handler ) (struct sk_buff *skb, struct device *dev, struct opions *opt, unsigned long daddr, unsigned short len, unsigned long saddr, int redo, struct inet_protocol *protocol);
int (*frag_handler)(......);//参数一样
void (*err_hander)(int err, unsigned char *buff, unsigned long daddr, unsigned long saddr, struct inet_protocol *protocol);
struct inet_protocol *next;
unsigned char protocol;
unsigned char copy :1;
void          *data;
char          *name;
};

论坛徽章:
0
5 [报告]
发表于 2011-09-09 10:50 |只看该作者
数据包接受通道创建概况
方法就是下层向上层提供发送接口
    从应用层 write()->sock_write() (net/socket.c)->inet_write() (net/inet/af_inet.c)->tcp_write() (net/inet/tcp.c)真正处理。
前两个如前所说只是作一些检查。在传输层进行处理所原因在于:数据的封装只有在传输层才行。
   
    tcp_write()函数完成数据的封装,将用户缓冲区赋值到内核缓冲区,封装到sk_buff结构。如果网络拥塞,则暂时缓存到write_queue队列中,稍后发送;否则则可以不经过write_queue直接发送出去。传输层协议调用ip_queue_xmit()函数将数据包发送到网络层进行处理。

   ip_queue_xmit()函数 对数据帧进行完善,调用dev_queue_xmit()函数将数据包送往链路层进行处理。同时将此数据包缓存到sock_send队列,保证可靠传输,此时,数据包已经从write_queue中删除。write_queue:从用户层结束新的数据包,没有用ip_queue_xmit传输出去。而send_queue则调用了ip_queue_xmit传输。ip_queue_xmit()直接调用dev_queue_xmit()函数进行发送。

   dev_queue_xmit()完成本层处理后,调用设备结构device的hard_start_xmit函数指针指向的具体硬件发送函数。对于NE系列网络设备的ei_start_xmit()函数,其首先将数据从内核赋值到网卡设备硬件缓冲区,操作具体的硬件寄存器,最终完成发送。

论坛徽章:
0
6 [报告]
发表于 2011-09-09 12:49 |只看该作者
兄弟,给弄个word或PDF版的吧,谢了{:2_168:}

论坛徽章:
0
7 [报告]
发表于 2011-09-09 13:42 |只看该作者
恩支持 , 整理个文档吧哈哈

论坛徽章:
0
8 [报告]
发表于 2011-09-09 23:04 |只看该作者
好文章!

论坛徽章:
0
9 [报告]
发表于 2011-09-12 15:17 |只看该作者
0.0

内核部分内容.pdf

483.34 KB, 下载次数: 52

论坛徽章:
0
10 [报告]
发表于 2011-09-12 15:19 |只看该作者
回复 9# wangzhen11aaa
总结会接续。
连载,一定要详细描述出整个过程。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP