linux 网络子系统
一、概述网络子系统概述 Linux 内核中,与网络相关的代码是一个相对独立的子系统,称为网络子系统。 网络子系统是一个层次化的结构,可分为以下几个层次: 1、 Socket 层 Linux 在发展过程中,采用 BSD socket APIs 作为自己的网络相关的 API 接口。同时, Linux 的目标又要能支持各种不同的协议族,而且这些协议族都可以使用 BSD socket APIs
一、概述
网络子系统概述 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 网络系统层次结构图。
二、 内核结构
按功能,Linux内核可以划分为5个不同的部分,每一部分处理一项明确的功能,同时又向其他内核组件提供这项功能。这种结构也反映到内核的源代码上——这5部分都形成了自己的子树。1进程管理
2内存管理
3文件系统
4设备驱动程序
5网络
三、 网络体系结构
Linux网络体系结构由以下五个部分组成 1)系统调用接口 2)协议无关几口 3)网络协议 4)设备无关接口 5 设备驱动程序。下面分别简述五个部分:
1)系统调用接口
系统调用接口是用户空间的应用程序正常访问内核的唯一合法途径(终端和陷入也可访问内核)。如:
asmlingkage long sys_getpid(void)
{
return current->pid;
}
系统调用一般由sys开头 ,前面的修饰符是asmlingkage,表示函数由堆栈获得参数。
2)协议无关接口
协议无关接口是由socket来实现的。它提供了一组通用函数来支持各种不同协议。
通过网络栈进行的通信都需要对 socket 进行操作。Linux 中的 socket 结构是 struct sock ,这个结构是在 linux/include/net/sock.h 中定义的。这个巨大的结构中包含了特定 socket 所需要的所有状态信息,其中包括 socket 所使用的特定协议和在 socket 上可以执行的一些操作。
网络子系统可以通过一个定义了自己功能的特殊结构来了解可用协议。每个协议都维护了一个名为 proto 的结构(可以在 linux/include/net/sock.h 中找到)。这个结构定义了可以在从 socket 层到传输层中执行特定的 socket 操作
3)网络协议
Linux支持多种网络协议,可以在<linux/socket.h>中查到所支持的网络协议:
#define AF_UNIX 1 /* Unix domain sockets */
#define AF_LOCAL 1 /* POSIX name for AF_UNIX */
#define AF_INET 2 /* Internet IP Protocol */
#define AF_AX25 3 /* Amateur Radio AX.25 */
#define AF_IPX 4 /* Novell IPX
… …
其中每一个所支持的协议对应net_family[]数组中的一项,net_family[]是结构体指针数组,其中的每一项都是一个结构体指针,指向一个net_proto_family 结构
struct net_proto_family {
int family;
int (*create) (struct socket * sock, int protocol);
short authentication;
short encryption;
short encrypt_net;
struct module *owner;
};这个结构体中注册了关于协议的信息。
4)设备无关接口
设备无关接口是由net_device实现的。任何设备和上层通信都是通过net_device设备无关接口。
它将协议与具有很多各种不同功能的硬件设备连接在一起。这一层提供了一组通用函数供底层网络设备驱动程序使用,让它们可以对高层协议栈进行操作。
首先,设备驱动程序可能会通过调用 register_netdevice 或 unregister_netdevice 在内核中进行注册或注销。调用者首先填写 net_device 结构,然后传递这个结构进行注册。内核调用它的 init 函数(如果定义了这种函数),然后执行一组健全性检查,并创建一个 sysfs 条目,然后将新设备添加到设备列表中(内核中的活动设备链表)。在 linux/include/linux/netdevice.h 中可以找到这个 net_device 结构。这些函数都是在 linux/net/core/dev.c 中实现的。
要从协议层向设备中发送 sk_buff ,就需要使用 dev_queue_xmit 函数。这个函数可以对 sk_buff 进行排队,从而由底层设备驱动程序进行最终传输(使用 sk_buff 中引用的 net_device 或 sk_buff->dev 所定义的网络设备)。dev 结构中包含了一个名为 hard_start_xmit 的方法,其中保存有发起 sk_buff 传输所使用的驱动程序函数。
报文的接收通常是使用 netif_rx 执行的。当底层设备驱动程序接收一个报文(包含在所分配的 sk_buff 中)时,就会通过调用 netif_rx 将 sk_buff 上传至网络层。然后,这个函数通过 netif_rx_schedule 将 sk_buff 在上层协议队列中进行排队,供以后进行处理。可以在 linux/net/core/dev.c 中找到 dev_queue_xmit 和 netif_rx 函数。
5)设备驱动程序
网络栈底部是负责管理物理网络设备的设备驱动程序。例如,包串口使用的 SLIP 驱动程序以及以太网设备使用的以太网驱动程序都是这一层的设备。
在进行初始化时,设备驱动程序会分配一个 net_device 结构,然后使用必须的程序对其进行初始化。这些程序中有一个是 dev->hard_start_xmit ,它定义了上层应该如何对 sk_buff 排队进行传输。这个程序的参数为 sk_buff 。这个函数的操作取决于底层硬件,但是通常 sk_buff 所描述的报文都会被移动到硬件环或队列中。就像是设备无关层中所描述的一样,对于 NAPI 兼容的网络驱动程序来说,帧的接收使用了 netif_rx 和 netif_receive_skb 接口。NAPI 驱动程序会对底层硬件的能力进行一些限制。
四、 核心数据结构
网络体系结构中有几个核心数据结构
1)sk_buff
网络层的数据都是通过sk_buff来传递的。
与sk_buff相关的一些数据结构有:socket sock proto proto_ops;
在Linux内核的网络实现中,使用了一个缓存结构(struct sk_buff)来管理网络报文,这个缓存区也叫套接字缓存。sk_buff是内核网络子系统中最重要的一种数据结构,它贯穿网络报文收发的整个周期。该结构在内核源码的include/linux/skbuff.h文件中定义。我们有必要了解结构中每个字段的意义。
一个套接字缓存由两部份组成:
· 报文数据:存储实际需要通过网络发送和接收的数据。
· 管理数据(struct sk_buff):管理报文所需的数据,在sk_buff结构中有一个head指针指向内存中报文数据开始的位置,有一个data指针指向报文数据在内存中的具体地址。head和data之间申请有足够多的空间用来存放报文头信息。
struct sk_buff结构在内存中的结构示意图:
sk_buff
----------------------------------- ------------> skb->head
| headroom |
|-----------------------------------| ------------> skb->data
| DATA |
| |
| |
| |
| |
|-----------------------------------| ------------> skb->tail
| tailroom |
----------------------------------- ------------> skb->end
先来看一下sk_buf的数据结构:
struct sk_buff
{
struct sk_buff *next,*prev;
struct sk_buff_head *list;
struct sock *sk; //sock结构指针
struct timeval stamp;
struct net_device *dev, *rx_dev;
union /* Transport layer header */
{
struct tcphdr *th;
struct udphdr *uh;
struct icmphdr *icmph;
struct igmphdr *igmph;
struct iphdr *ipiph;
struct spxhdr *spxh;
unsigned char *raw;
} h; //传输层头
union /* Network layer header */
{
struct iphdr *iph;
struct ipv6hdr *ipv6h;
struct arphdr *arph;
struct ipxhdr *ipxh;
unsigned char *raw;
} nh; //网络层头
union /* Link layer header */
{
struct ethhdr *ethernet;
unsigned char *raw;
} mac; //数据链路层头
struct dst_entry *dst;
char cb[48];
unsigned int len, csum; //数据长度,是否被消耗
volatile char used;
unsigned char is_clone, cloned, pkt_type, ip_summed; //是否克隆 已克隆 报文类型 校验和
__u32 priority;
atomic_t users;
unsigned short protocol, security;
unsigned int truesize;
unsigned char *head, *data, *tail, *end; //data和tail指向当前有效数据的头和尾
void (*destructor)(struct sk_buff *); //head和end指向socket的头和尾
…
};
其中pkt_type可以是如下类型之一
PACKET_HOST specifies packet a sent to the local host.
PACKET_BROADCAST specifies a broadcast packet.
PACKET_MULTICAST specifies a multicast packet.
PACKET_OTHERHOST specifies packets not destined for the local host, but received by special modes (e.g., the promiscuous mode)。
PACKET_OUTGOING specifies packets leaving the computer.
PACKET_LOOPBACK specifies packets sent from the local computer to itself.
PACKET_FASTROUTE specifies packets fast-forwarded between special network cards (fastroute is not covered in this book
设备无关层的统一接口。
net_device结构是Linux内核中所有网络设备的基础数据结构。包含网络适配器的硬件信息(中断、端口、驱动程序函数等)和高层网络协议的网络配置信息(IP地址、子网掩码等)。该结构的定义位于include/linux/netdevice.h
每个net_device结构表示一个网络设备,如eth0、eth1...。这些网络设备通过dev_base线性表链接起来。内核变量dev_base表示已注册网络设备列表的入口点,它指向列表的第一个元素(eth0)。然后各元素用next字段指向下一个元素(eth1)。使用ifconfig -a命令可以查看系统中所有已注册的网络设备。
net_device结构通过alloc_netdev函数分配,alloc_netdev函数位于net/core/dev.c文件中。该函数需要三个参数。
· 私有数据结构的大小
· 设备名,如eth0,eth1等。
· 配置例程,这些例程会初始化部分net_device字段。
分配成功则返回指向net_device结构的指针,分配失败则返回NULL。
struct net_device
{
/*
* This is the first field of the "visible" part of this structure
* (i.e. as seen by users in the "Space.c" file). It is the name
* the interface.
*/
char name[IFNAMSIZ]; 不必多说,就是ifconfig 后面的第一个参数
/* device name hash chain */
struct hlist_node name_hlist;
/*
* I/O specific fields
* FIXME: Merge these and struct ifmap into one
*/
unsigned long mem_end; /* shared mem end */
unsigned long mem_start; /* shared mem start */
unsigned long base_addr; /* device I/O address */
unsigned int irq; /* device IRQ number */
/*
* Some hardware also needs these fields, but they are not
* part of the usual set specified in Space.c.
*/
unsigned char if_port; /* Selectable AUI, TP,..*/
unsigned char dma; /* DMA channel */
unsigned long state;
struct list_head dev_list; 做什么用呢?估计是内核维护
/* The device initialization function. Called only once. */
int (*init)(struct net_device *dev);
/* ------- Fields preinitialized in Space.c finish here ------- */
/* Net device features */
unsigned long features;
#define NETIF_F_SG 1 /* Scatter/gather IO. */
#define NETIF_F_IP_CSUM 2 /* Can checksum only TCP/UDP over IPv4. */
#define NETIF_F_NO_CSUM 4 /* Does not require checksum. F.e. loopack. */
#define NETIF_F_HW_CSUM 8 /* Can checksum all the packets. */
#define NETIF_F_HIGHDMA 32 /* Can DMA to high memory. */
#define NETIF_F_FRAGLIST 64 /* Scatter/gather IO. */
#define NETIF_F_HW_VLAN_TX 128 /* Transmit VLAN hw acceleration */
#define NETIF_F_HW_VLAN_RX 256 /* Receive VLAN hw acceleration */
#define NETIF_F_HW_VLAN_FILTER 512 /* Receive filtering on VLAN */
#define NETIF_F_VLAN_CHALLENGED 1024 /* Device cannot handle VLAN packets */
#define NETIF_F_GSO 2048 /* Enable software GSO. */
#define NETIF_F_LLTX 4096 /* LockLess TX */
/* Segmentation offload features */
#define NETIF_F_GSO_SHIFT 16
#define NETIF_F_GSO_MASK 0xffff0000
#define NETIF_F_TSO (SKB_GSO_TCPV4 << NETIF_F_GSO_SHIFT)
#define NETIF_F_UFO (SKB_GSO_UDP << NETIF_F_GSO_SHIFT)
#define NETIF_F_GSO_ROBUST (SKB_GSO_DODGY << NETIF_F_GSO_SHIFT)
#define NETIF_F_TSO_ECN (SKB_GSO_TCP_ECN << NETIF_F_GSO_SHIFT)
#define NETIF_F_TSO6 (SKB_GSO_TCPV6 << NETIF_F_GSO_SHIFT)
/* List of features with software fallbacks. */
#define NETIF_F_GSO_SOFTWARE (NETIF_F_TSO | NETIF_F_TSO_ECN | NETIF_F_TSO6)
#define NETIF_F_GEN_CSUM (NETIF_F_NO_CSUM | NETIF_F_HW_CSUM)
#define NETIF_F_ALL_CSUM (NETIF_F_IP_CSUM | NETIF_F_GEN_CSUM)
struct net_device *next_sched;
/* Interface index. Unique device identifier */
int ifindex;
int iflink;
struct net_device_stats* (*get_stats)(struct net_device *dev);
struct net_device_stats stats; 网卡发送包的个数等等,ifconfig eth0看到的东西就是啦
#ifdef CONFIG_WIRELESS_EXT
/* List of functions to handle Wireless Extensions (instead of ioctl).
* See <net/iw_handler.h> for details. Jean II */
const struct iw_handler_def * wireless_handlers;
/* Instance data managed by the core of Wireless Extensions. */
struct iw_public_data * wireless_data;
#endif
const struct ethtool_ops *ethtool_ops;
/*
* This marks the end of the "visible" part of the structure. All
* fields hereafter are internal to the system, and may change at
* will (read: may be cleaned up at will).
*/
unsigned int flags; /* interface flags (a la BSD) */
unsigned short gflags;
unsigned short priv_flags; /* Like 'flags' but invisible to userspace. */
unsigned short padded; /* How much padding added by alloc_netdev() */
unsigned char operstate; /* RFC2863 operstate */
unsigned char link_mode; /* mapping policy to operstate */
unsigned mtu; /* interface MTU value */
unsigned short type; /* interface hardware type */
unsigned short hard_header_len; /* hardware hdr length */
struct net_device *master; /* Pointer to master device of a group,
* which this device is member of.
*/
/* Interface address info. */
unsigned char perm_addr[MAX_ADDR_LEN]; /* permanent hw address */
unsigned char addr_len; /* hardware address length */
unsigned short dev_id; /* for shared network cards */
struct dev_mc_list *mc_list; /* Multicast mac addresses */
int mc_count; /* Number of installed mcasts */
int promiscuity;
int allmulti;
/* Protocol specific pointers */
void *atalk_ptr; /* AppleTalk link */
void *ip_ptr; /* IPv4 specific data */
void *dn_ptr; /* DECnet specific data */
void *ip6_ptr; /* IPv6 specific data */
void *ec_ptr; /* Econet specific data */
void *ax25_ptr; /* AX.25 specific data */
struct wireless_dev *ieee80211_ptr; /* IEEE 802.11 specific data,
assign before registering */
/*
* Cache line mostly used on receive path (including eth_type_trans())
*/
struct list_head poll_list ____cacheline_aligned_in_smp;
/* Link to poll list */
int (*poll) (struct net_device *dev, int *quota);
int quota;
int weight;
unsigned long last_rx; /* Time of last Rx */
/* Interface address info used in eth_type_trans() */
unsigned char dev_addr[MAX_ADDR_LEN]; /* hw address, (before bcast
because most packets are unicast) */
unsigned char broadcast[MAX_ADDR_LEN]; /* hw bcast add */
/*
* Cache line mostly used on queue transmit path (qdisc)
*/
/* device queue lock */
spinlock_t queue_lock ____cacheline_aligned_in_smp;
struct Qdisc *qdisc;
struct Qdisc *qdisc_sleeping;
struct list_head qdisc_list;
unsigned long tx_queue_len; /* Max frames per queue allowed */
/* Partially transmitted GSO packet. */
struct sk_buff *gso_skb;
/* ingress path synchronizer */
spinlock_t ingress_lock;
struct Qdisc *qdisc_ingress;
/*
* One part is mostly used on xmit path (device)
*/
/* hard_start_xmit synchronizer */
spinlock_t _xmit_lock ____cacheline_aligned_in_smp;
/* cpu id of processor entered to hard_start_xmit or -1,
if nobody entered there.
*/
int xmit_lock_owner;
void *priv; /* pointer to private data */
int (*hard_start_xmit) (struct sk_buff *skb,
struct net_device *dev);
/* These may be needed for future network-power-down code. */
unsigned long trans_start; /* Time (in jiffies) of last Tx */
int watchdog_timeo; /* used by dev_watchdog() */
struct timer_list watchdog_timer;
/*
* refcnt is a very hot point, so align it on SMP
*/
/* Number of references to this device */
atomic_t refcnt ____cacheline_aligned_in_smp;
/* delayed register/unregister */
struct list_head todo_list;
/* device index hash chain */
struct hlist_node index_hlist;
struct net_device *link_watch_next;
/* register/unregister state machine */
enum { NETREG_UNINITIALIZED=0,
NETREG_REGISTERED, /* completed register_netdevice */
NETREG_UNREGISTERING, /* called unregister_netdevice */
NETREG_UNREGISTERED, /* completed unregister todo */
NETREG_RELEASED, /* called free_netdev */
} reg_state;
/* Called after device is detached from network. */
void (*uninit)(struct net_device *dev);
/* Called after last user reference disappears. */
void (*destructor)(struct net_device *dev);
/* Pointers to interface service routines. */
int (*open)(struct net_device *dev);
int (*stop)(struct net_device *dev);
#define HAVE_NETDEV_POLL
int (*hard_header) (struct sk_buff *skb,
struct net_device *dev,
unsigned short type,
void *daddr,
void *saddr,
unsigned len);
int (*rebuild_header)(struct sk_buff *skb);
#define HAVE_MULTICAST
void (*set_multicast_list)(struct net_device *dev);
#define HAVE_SET_MAC_ADDR
int (*set_mac_address)(struct net_device *dev,
void *addr);
#define HAVE_PRIVATE_IOCTL
int (*do_ioctl)(struct net_device *dev,
struct ifreq *ifr, int cmd);
#define HAVE_SET_CONFIG
int (*set_config)(struct net_device *dev,
struct ifmap *map);
#define HAVE_HEADER_CACHE
int (*hard_header_cache)(struct neighbour *neigh,
struct hh_cache *hh);
void (*header_cache_update)(struct hh_cache *hh,
struct net_device *dev,
unsigned char * haddr);
#define HAVE_CHANGE_MTU
int (*change_mtu)(struct net_device *dev, int new_mtu);
#define HAVE_TX_TIMEOUT
void (*tx_timeout) (struct net_device *dev);
void (*vlan_rx_register)(struct net_device *dev,
struct vlan_group *grp);
void (*vlan_rx_add_vid)(struct net_device *dev,
unsigned short vid);
void (*vlan_rx_kill_vid)(struct net_device *dev,
unsigned short vid);
int (*hard_header_parse)(struct sk_buff *skb,
unsigned char *haddr);
int (*neigh_setup)(struct net_device *dev, struct neigh_parms *);
#ifdef CONFIG_NETPOLL
struct netpoll_info *npinfo;
#endif
#ifdef CONFIG_NET_POLL_CONTROLLER
void (*poll_controller)(struct net_device *dev);
#endif
/* bridge stuff */
struct net_bridge_port *br_port;
struct device dev;
/* space for optional statistics and wireless sysfs groups */
struct attribute_group *sysfs_groups[3];
};
这个数据结构比较巨大。我们分几组一点一点来看
1 网络设备的通用字段
name 网络适配器的名字
*next 用于连接多个网络设备
owner moudle结构指针
ifindex 除name外的网络设备第二标识符
iflink 指定了用来发包的网络设备的索引
state 网络设备的状态可以是以下值:LINK_STATE_START,LINK_STATE_X0FF(替代以前的tbusy字段)
下列3种方法可以改变state的值:netif_stop_queue(dev);netif_start_queue(dev);
netif_wake_queue(dev);
trans_start 传输计时开始
*priv 指向其私有数据的指针
qdisc 映射当前网络服务策略
refcnt 当前网络设备引用计数
xmit_lock 发送时的锁
queue_lock 队列的锁
xmit_lock_owner 获得发送时的锁的处理器编号
2 硬件相关字段
rmem_end 接受内存尾地址
rmem-start 接受内存首地址
mem_end 发送内存尾地址
mem_start 发送内存首地址
base_addr 网络设备的基地址(见后图)
irq 中断号
if_port 端口号
3 物理层上的数据
hard_header_length 第二层包报头长度
mtu 最大传输单元
tx_queue_len 网络设备输出队列最大长度
type 网络适配器硬件类型
addr_len 第二层地址长度
dev_addr[MAX_ADDR_LEN] 第二层地址
broadcast[MAX_ADDR_LEN] 广播地址
*mc_list 指向具有多播第二层地址的线性表
mc_count dev_mc_list中的地址数量(多播地址数)
watchdpg_timeo 超时时间(从trans_start开始,经过watchdog_timeo时间后超时)
4 网络层上的数据
ip_ptr,ip6_ptr,atalk_ptr,dn_ptr,ec_ptr 指向网络适配器第三层协议信息,如ip_ptr指向in_device结构。该结构包含ip地址列表,多播ip列表,ARP协议参数等
pa_len 协议地址长度
pa_dstaddr 点对点连接中的目的地址
flags 各种不同的开关,他们都可以通过ifconfig命令来设置
Flag
Meaning
IFF_UPThe network device is activated and can send and receive packets.
IFF_BROADCAST
The device is broadcast-enabled, and the broadcast address pa_braddr is valid.
IFF_DEBUG
This flag switches the debug mode on (currently not used by any driver)。
IFF_LOOPBACK
This flag shows that this is a loopback network device.
IFF_POINTOPOINT
This is a point-to-point connection. If this switch is set, then pa_dstaddr should contain the partner's address.
IFF_NOARP
This device does not support the Address Resolution Protocol (ARP) (e.g., in point-to-point connections)。
IFF_PROMISC
This flag switches the promiscuous mode on. This means that all packets currently received in the network adapter are forwarded to the upper layers, including those not intended for this computer. This mode is of interest for tcpdump only
IFF_MULTICAST
This flag activates the receipt of multicast packets. ether_setup() activates this switch. A card that does not support multicast should delete this flag.
IFF_ALLMULTI
All multicast packets should be received. This is required when the computer is to work as multicast router. IFF_MULTICAST has to be set in addition.
IFF_PORTSEL
Setting of the output port is supported by the hardware.
IFF_AUTOMEDIA
Automatic selection of the output medium (autosensing ) is enabled.
IFF_DYNAMIC
Dynamic change of the network device's address is enabled (e.g., for dialup connections)。
5 设备驱动程序的函数
init() 搜索并初始化网络设备
uninit() 注销网络设备
destructor() 当网络设备的最后一个引用refcnt被删除时调用此函数
open () 打开网络设备
stop() 关闭网络设备
hard_start_xmit() 发送包,成功返回0,否则返回1
get_stats() 获取网络设备状态信息,这些信息以net_device_stats 结构的形式返回
get_wireless_stats() 获取无限网络设备的状态信息,这些信息以iw_statistics 结构的形式返回
set_multicast_list() 将多播MAC地址传给网络适配器
watchdog_timeo() 超时处理函数
do_ioctl() 向网络驱动程序传递网络适配器相关的ioctl()命令
set_config() 运行时改变网络适配器的配置
上面所列的方法依赖于所使用的网络适配器,也就是说如果需要他们的功能则必须由驱动程序来提供。
下面所列的方法较少依赖于适配器,不必由驱动程序相关方法实现。
hard_header() 根据源和目标第二层地址创建二层报头
rebuild_header() 重建第二层报头
hard_header_cache() 用硬报头缓存中保存的数据填充第二层报头
header_cache_update() 更改硬报头缓存中保存的第二层报头数据
hard_header_parse() 从套接字缓冲区的包数据空间读取第二层报头的发送地址
set_mac_address() 设mac地址
change_mtu() 改变mtu长度
五、 网络设备初始化
在使用网络设备之前,必须对它进行初始化和向内核注册该设备。网络设备的初始化包括以下步骤:
· 硬件初始化:分配IRQ和I/O端口等。
· 软件初始化:分配IP地址等。
· 功能初始化:QoS等
六、 网络设备与内核的沟通方式
网络设备(网卡)通过轮询和中断两种方式与内核沟通。
· 轮询(polling),由内核发起,内核周期性地检查网络设备是否有数据要处理。
· 中断(interrupt),由设备发起,设备向内核发送一个硬件中断信号。
Linux网络系统可以结合轮询和中断两方式以提高网络系统的性能。共小节重点介绍中断方式。
每个中断都会调用一个叫中断处理器的函数。当驱动程序向内核注册一个网卡时,会请求和分配一个IRQ号。接着为分配的这个IRQ注册中断处理器。注册和释放中断处理器的代码是架构相关的,不同的硬件平台有不同的代码实现。实现代码位于kernel/irq/manage.c和arch/XXX/kernel/irq.c源码文件中。XXX是不同硬件架构的名称,如我们所使用得最多的i386架构。下面是注册和释放中断处理器的函数原型。
int request_irq(unsigned int irq, irq_handler_t handler,
unsigned long irqflags, const char *devname, void *dev_id)
void free_irq(unsigned int irq, void *dev_id)
内核是通过IRQ号来找到对应的中断处理器并执行它的。为了找到中断处理器,内核把IRQ号和中断处理器函数的关联起来存储在全局表(global table)中。IRQ号和中断处理器的关联性可以是一对一,也可以是一对多。因为IRQ号是可以被多个设备所共享的。
通过中断,网卡设备可以向驱动程序传送以下信息:
· 帧的接收,这是最用的中断类型。
· 传送失败通知,如传送超时。
· DMA传送成功。
· 设备有足够的内存传送数据帧。当外出队列没有足够的内存空间存放一个最大的帧时(对于以太网卡是1535),网卡产生一个中断要求以后再传送数据,驱动程序会禁止数据的传送,。当有效内存空间多于设备需传送的最大帧(MTU)时,网卡会发送一个中断通知驱动程序重新启用数据传送。这些逻辑处理在网卡驱动程序中设计。 netif_stop_queue()函数禁止设备传送队列,netif_start_queue()函数重启设备的传送队列。这些动作一般在驱动程序的xxx_start_xmit()中处理。
系统的中断资源是有限的,不可能为每种设备提供独立的中断号,多种设备要共享有限的中断号。上面我们提到中断号是和中断处理器关联的。在中断号共享的情况下内核如何正确找到对应的中断处理器呢?内核采用一种最简单的方法,就是不管三七二一,当同一中断号的中断发生时,与该中断号关联的所有中断处理器都一起被调用。调用后再靠中断处理器中的过滤程序来筛选执行真正的中断处理。
对于使用共享中断号的设备,它的驱动程序在注册时必须先指明允许中断共享。
IRQ与中断处理器的映射关系保存在一个矢量表中。该表保存了每个IRQ的中断处理器。矢量表的大小是平台相关的,从15(i386)到超过200都有。 irqaction数据结构保存了映射表的信息。上面提到的request_irq()函数创建irqaction数据结构并通过setup_irq()把它加入到irq_des矢量表中。irq_des在 kernel/irq/handler.c中定义,平台相关的定义在arch/XXX/kernel/irq.c文件中。setup_irq()在kernel/irq/manage.c,平台相关的定义在arch/XXX/kernel/irq.c中。
七、网络设备操作层的初始化
在系统启动阶段,网络设备操作层通过net_dev_init()进行初始化。net_dev_init()的代码在net/core/dev.c文件中。这是一个以__init标识的函数,表示它是一个低层的代码。
net_dev_init()的主要初始化工作内容包括以下几点:
· 生成/proc/net目录和目录下相关的文件。
八、内核模块加载器
kmod是内核模块加载器。该加载器在系统启动时会触发/sbin/modprobe和/sbin/hotplug自动加载相应的内核模块和运行设备启动脚本。modprobe使用/etc/modprobe.conf配置文件。当该文件中有"alias eth0 3c59x"配置时就会自动加3c59x.ko模块。
九、虚拟设备
虚拟设备是在真实设备上的虚拟,虚拟设备和真实设备的对应关系可以一对多或多对一。即一个虚拟设备对应多个真实设备或多个真实设备一个虚拟设备。下面介绍网络子系统中虚拟设备的应用情况。
· Bonding,把多个真实网卡虚拟成一个虚拟网卡。对于应用来讲就相当于访问一个网络接口。
· 802.1Q,802.3以太网帧头扩展,添加了VLAN头信息。把多个真实网卡虚拟成一个虚拟网卡。
· Bridging,一个虚拟网桥,把多个真实网卡虚拟成一个虚拟网卡。
· Tunnel interfaces,实现GRE和IP-over-IP虚拟通道。把一个真实网卡虚拟成多个虚拟网卡。
· True equalizer (TEQL),类似于Bonding。
上面不是一个完整列表,随着内核的不断开发完善,新功能新应用也会不断出现。
本文参考资料:感谢以下作者的分享
http://blog.csdn.net/ylyuanlu/article/details/7707877
http://www.chinaunix.net/old_jh/4/1065188.html
更多推荐
所有评论(0)