转自:http://blog.csdn.net/rstevens/archive/2007/04/10/1559289.aspx
1.
网络子系统
1.1.
网络子系统概述
Linux
内核中,与网络相关的代码是一个相对独立的子系统,称为网络子系统。
网络子系统是一个层次化的结构,可分为以下几个层次:
1、
Socket
层
Linux
在发展过程中,采用
BSD socket APIs
作为自己的网络相关的
API
接口。同时,
Linux
的目标又要能支持各种不同的协议族,而且这些协议族都可以使用
BSD socket APIs
作为应用层的编程接口。因此,在
socket APIs
与协议族层之间抽象出一个
socket
层,用于将
user space
的
socket API
调用,转给具体的协议族做处理。
2、
协议族层(
INET
协议族、
INET6
协议族等)
Linux
网络子系统功能上相当完备,它不仅支持
INET
协议族(也就是通常所说的
TCP/IP stack
),而且还支持其它很多种协议族,如
DECnet, ROSE, NETBEUI
等。
INET6
就是一种新增加的协议族。
对于
INET
、
INET6
协议族来说,
又进一步划分为传输层和网络层。
3、
设备驱动层
设备驱动层则主要将协议族层与物理的网络设备隔离开。它不在本文的讨论范围之内。
下图是
Linux
网络系统层次结构图。
1.2.
网络子系统的初始化
·
Socket
层的初始化:
Init()->do_basic_setup()->sock_init()
Sock_init()
:对
sock
和
skbuff
结构进行
SLAB
内存的初始化工作
·
各种网络协议族的初始化:
Do_initcalls()
:
对于编译到内核中的功能模块(而不是以模块的形式动态加载),它的初始化函数会在这个地方被调用到。
内核映象中专门有一个初始化段,所有编译到内核中的功能模块的初始化函数都会加入到这个段中;而
do_initcalls()
就是依次执行初始化段中的这些函数。
INET
协议族通常是被编译进内核的;它的模块初始化函数是
net/ipv4/af_inet.c
中的
inet_init()
而
INET6
是作为一个模块编译的。它的模块初始化函数是
net/ipv6/af_inet6.c
中的
inet6_init()
2.
协议族
Linux
网络子系统可以支持不同的协议族,
Linux
所支持的协议族定义在
include/linux/socket.h
2.1.
协议族数据结构
协议族数据结构是
struct net_proto_family
。
net_proto_family {
int
family;
int
(
*
create)(
struct
socket
*
sock,
int
protocol);
short
authentication;
short
encryption;
short
encrypt_net;
struct
module
*
owner;
};
这个结构中,最重要的是
create
函数,一个新的协议族,必须提供此函数的实现。这是因为:
不同的网络协议族,从
user space
的使用方法来说,都是一样的,都是先调用
socket()
来创建一个
socket fd
,然后通过这个
fd
发送
/
接收数据。
在
user space
通过
socket()
系统调用进入内核后,根据第一个参数协议族类型,来调用相应协议族的
create()
函数。对
INET6
来说,这个函数是
inet6_create()
。
因此,要实现一个新的协议族,首先需要提供一个
create()
的实现。关于
create()
里面具体做了什么,后面再叙述。
Linux
系统通过这种方式,可以很方便的支持新的网络协议族,而不用修改已有的代码。这很好的符合了
“开
-
闭原则”,对扩展开放,对修改封闭。
2.2.
协议族注册
Linux
维护一个
struct net_proto_family
的数组
net_families[]
如果要支持一个新的网络协议族,那么需要定义自己的
struct net_proto_family
,并且通过调用
sock_register
将它注册到
net_families[]
中。
3.
socket
层的主要数据结构
socket
层又叫
“socket access protocol layer”
。它处于
BSD socket APIs
与底层具体的协议族之间。这是一个抽象层,它起着承上启下的作用。在这一层的数据结构也有着这种特点。
3.1.
Struct socket
在
user space
,通过
socket()
创建的
socket fd
,在内核中对应的就是一个
struct socket
。
socket {
socket_state state;
unsigned
long
flags;
struct
proto_ops
*
ops;
struct
fasync_struct
*
fasync_list;
struct
file
*
file;
struct
sock
*
sk;
wait_queue_head_t wait;
short
type;
};
它定义于
include/linux/net.h
中
。
3.2.
Struct proto_ops
Struct socket
的
ops
域指向一个
struct proto_ops
结构,
struct proto_ops
定义于
include/linux/net.h
中,它是
socket
层提供给上层的接口,这个结构中,都是
BSD socket API
的具体实现的函数指针。
proto_ops {
int
family;
struct
module
*
owner;
int
(
*
release) (
struct
socket
*
sock);
int
(
*
bind) (
struct
socket
*
sock,
struct
sockaddr
*
myaddr,
int
sockaddr_len);
int
(
*
connect) (
struct
socket
*
sock,
struct
sockaddr
*
vaddr,
int
sockaddr_len,
int
flags);
int
(
*
socketpair)(
struct
socket
*
sock1,
struct
socket
*
sock2);
int
(
*
accept) (
struct
socket
*
sock,
struct
socket
*
newsock,
int
flags);
int
(
*
getname) (
struct
socket
*
sock,
struct
sockaddr
*
addr,
int
*
sockaddr_len,
int
peer);
unsigned
int
(
*
poll) (
struct
file
*
file,
struct
socket
*
sock,
struct
poll_table_struct
*
wait);
int
(
*
ioctl) (
struct
socket
*
sock, unsigned
int
cmd,
unsigned
long
arg);
int
(
*
listen) (
struct
socket
*
sock,
int
len);
int
(
*
shutdown) (
struct
socket
*
sock,
int
flags);
int
(
*
setsockopt)(
struct
socket
*
sock,
int
level,
int
optname,
char
__user
*
optval,
int
optlen);
int
(
*
getsockopt)(
struct
socket
*
sock,
int
level,
int
optname,
char
__user
*
optval,
int
__user
*
optlen);
int
(
*
sendmsg) (
struct
kiocb
*
iocb,
struct
socket
*
sock,
struct
msghdr
*
m, size_t total_len);
int
(
*
recvmsg) (
struct
kiocb
*
iocb,
struct
socket
*
sock,
struct
msghdr
*
m, size_t total_len,
int
flags);
int
(
*
mmap) (
struct
file
*
file,
struct
socket
*
sock,
struct
vm_area_struct
*
vma);
ssize_t (
*
sendpage) (
struct
socket
*
sock,
struct
page
*
page,
int
offset, size_t size,
int
flags);
};
一个
socket API
通过系统调用进入内核后,首先由
socket
层处理。
Socket
层找到对应的
struct socket
,通过它找到
struct proto_ops
,然后由它所指向的函数进行进一步处理。
以
sendmsg()
这个函数为例,从
user space
通过系统调用进入
kernel
后,由
sys_sendmsg()
、
sock_sendmsg()
依次处理,然后交给
struct proto_ops
的
sendmsg()
处理。
4.
传输层、网络层的主要数据结构
Socket
层之下,是具体的协议族。
对
INET
和
INET6
来说,又分为传输层和网络层。
这两层重要的数据结构有
struct sock
和
struct proto
。
4.1.
Struct sock
struct sock
定义于
include/net/sock.h
中,用于
INET
和
INET6
协议族。
应用层的
socket fd
,在
socket
层对应的是
struct socket
。
struct socket
很简单,并不做什么具体的事情,它通过
sk
域与一个
struct sock
关联。因此,对应用层的一个
socket fd
来说,在内核中对应的是一个
struct socket
加上一个
struct sock
结构,
struct socket
负责
socket
层的处理,
struct sock
负责传输层、网络层的处理。
在
2.4
内核中,这个结构非常杂乱,到
2.6
内核,对它做了简化,但仍然有很多成员。我们在这里不具体了解它的作用,只要知道它所处的层次即可。
4.2.
Struct proto
struct sock
通过
sk_prot
域指向
struct proto
结构。
proto {
void
(
*
close)(
struct
sock
*
sk,
long
timeout);
int
(
*
connect)(
struct
sock
*
sk,
struct
sockaddr
*
uaddr,
int
addr_len);
int
(
*
disconnect)(
struct
sock
*
sk,
int
flags);
struct
sock
*
(
*
accept) (
struct
sock
*
sk,
int
flags,
int
*
err);
int
(
*
ioctl)(
struct
sock
*
sk,
int
cmd,
unsigned
long
arg);
int
(
*
init)(
struct
sock
*
sk);
int
(
*
destroy)(
struct
sock
*
sk);
void
(
*
shutdown)(
struct
sock
*
sk,
int
how);
int
(
*
setsockopt)(
struct
sock
*
sk,
int
level,
int
optname,
char
__user
*
optval,
int
optlen);
int
(
*
getsockopt)(
struct
sock
*
sk,
int
level,
int
optname,
char
__user
*
optval,
int
__user
*
option);
int
(
*
sendmsg)(
struct
kiocb
*
iocb,
struct
sock
*
sk,
struct
msghdr
*
msg, size_t len);
int
(
*
recvmsg)(
struct
kiocb
*
iocb,
struct
sock
*
sk,