linux 协议栈

来源:互联网 发布:修改js中的字体颜色 编辑:程序博客网 时间:2024/05/02 01:13

http://www.cnblogs.com/hustcat/archive/2009/09/17/1568738.html

http://www.cnblogs.com/hustcat/archive/2009/09/17/1568765.html

Linux网络协议栈(一)——Socket入门(1)

1、TCP/IP参考模型
为了实现各种网络的互连,国际标准化组织(ISO)制定了开放式系统互连(OSI)参考模型。尽管OSI的体系结构从理论上讲是比较完整的,但实际上,完全符合OSI各层协议的商用产品却很少进入市场。而使用TCP/IP 协议的产品却大量涌入市场,几乎所有的工作站都配有TCP/IP协议,使得TCP/IP 成为计算机网络的实际的国际标准。


2、套接字(socket) 
socket是操作系统的重要组成部分之一,它是网络应用程序的基础。从层次上来说,它位于应用层,是操作系统为应用程序员提供的API,通过它,应用程序可以访问传输层协议。
1、socket 位于传输层协议之上,屏蔽了不同网络协议之间的差异;
2、socket是网络编程的入口,它提供了大量的系统调用,构成了网络程序的主体;
3、在Linux系统中,socket属于文件系统的一部分,网络通信可以被看作是对文件的读取,使得我们对网络的控制和对文件的控制一样方便。

2.1、套接字地址
在传输层上,通信端点可由Internet上3个参数描述:所用的协议、IP地址和端口号。这些内容由sockaddr描述:
复制代码
//usr/include/sys/socket.h
typedef unsigned short    sa_family_t;
//通用socket地址
struct sockaddr {
    sa_family_t    sa_family;    
/* address family, AF_xxx,协议簇*/
    
char        sa_data[14];    /* 14 bytes of protocol address    */
};

//usr/include/netinet/in.h
//INET地址簇的socket地址
struct in_addr {
                    __u32 s_addr;
};
struct sockaddr_in   {
   sa_family_t            sin_family;      
/* Address family: AF_INET */
   unsigned 
short int     sin_port;        /* Port number,端口*/
   
struct in_addr         sin_addr;        /* Internet address,IP地址*/

   
/* Pad to size of 'struct sockaddr' . */
   unsigned 
char sin_zero[sizeof (struct sockaddr) -
                          
sizeof (sa_family_t) -
                          
sizeof (uint16_t) -
                          
sizeof (struct in_addr)];
};
复制代码
Linux 支持的套接字地址族:

套接字地址族

描述

UNIX

UNIX 域套接字

INET

通过 TCP/IP 协议支持的 Internet 地址族

AX25

Amater radio X25

APPLETALK

Appletalk DDP

IPX

Novell IPX

X25

X25

Linux 所支持的BSD套接字类型:

       BSD 套接字类型

 描述

流(stream

这种套接字提供了可靠的双向顺序数据流,可保证数据不会在传输过程中丢失、破坏或重复出现。流套接字通过 INET地址族的 TCP 协议实现。

数据报(datagram

 这种套接字也提供双向的数据传输,但是并不对数据的传输提供担保,也就是说,数据可能会以错误的顺序传递,甚至丢失或破坏。这种类型的套接字通过 INET 地址族的 UDP协议实现。

原始(raw

 利用这种类型的套接字,进程可以直接访问底层协议(因此称为原始)。例如,可在某个以太网设备上打开原始套接字,然后获取原始的 IP 数据传输信息。

可靠发送的消息

 和数据报套接字类似,但保证数据被正确传输到目的端。

顺序数据包

 和流套接字类似,但数据包大小是固定的。

数据包(packet

 这并不是标准的 BSD 套接字类型,它是 Linux 专有的BSD 套接字扩展,可允许进程直接在设备级访问数据包。

  2.2、套接字操作
套接字(更确切的说是BSD套接字)为应用程序提供了基本的API,这些API是编写网络应用程序的基础。

3、套接字的实现
套接字最先是在UNIX的BSD版本实现的,所以也叫做BSD套接字,它隐藏了各个协议之间的差异,并向上提供统一的接口。Linux中实现套接字的基本结构:

3.1、BSD套接字
3.1.1、核心数据结构
为了实现BSD套接字,内核提供一个重要的数据结构struct socket,它的定义如下:

复制代码
 //BSD套接字(include/linux/net.h)
struct socket {
    socket_state        state;  
//套接字状态
    unsigned long        flags;
    
struct proto_ops    *ops; //操作函数集
    struct fasync_struct    *fasync_list;
    
struct file        *file;//每个BSD套接字都有一个inode结点,通过文件对象与其关联起来  
    struct sock        *sk; //socket内部结构,与具体的协议簇(比如PF_INET)相关
    wait_queue_head_t    wait;
    
short            type;    //套接字类型:如SOCK_STREAM, SOCK_DGRAM, SOCK_RAW, SOCK_RDM, SOCK_SEQPACKET, and SOCK_PACKET
    unsigned char        passcred; 
};

//BSD套接字操作函数集
struct 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);
};
//BSD套接字状态
typedef enum {
    SS_FREE 
= 0,            /* not allocated        */
    SS_UNCONNECTED,            
/* unconnected to any socket    */
    SS_CONNECTING,            
/* in process of connecting    */
    SS_CONNECTED,            
/* connected to socket        */
    SS_DISCONNECTING        
/* in process of disconnecting    */
} socket_state; 
复制代码
3.1.2、BSD套接字初始化
复制代码
//net/socket.c
//BSD套接字的初始化
void __init sock_init(void)
{
    
int i;

    
/*
     *    Initialize all address (protocol) families. 
     
*/
     
    
for (i = 0; i < NPROTO; i++
        net_families[i] 
= NULL; //协议簇数组初始化

    
/*
     *    Initialize sock SLAB cache.
     
*/
     
//分配sock缓存
    sk_init();

#ifdef SLAB_SKB
    
/*
     *    Initialize skbuff SLAB cache 
     
*/
    skb_init();
#endif

    
/*
     *    Initialize the protocols module. 
     
*/

    init_inodecache();

    
//注册sockfs文件系统
    register_filesystem(&sock_fs_type);
    
//安装sockfs
    sock_mnt = kern_mount(&sock_fs_type);
    
/* The real protocol initialization is performed when
     *  do_initcalls is run.  
     
*/

#ifdef CONFIG_NETFILTER
    netfilter_init();
#endif
}


//net/socket.c
//sockfs文件系统的安装点
static struct vfsmount *sock_mnt;
//sockfs文件系统类型
static struct file_system_type sock_fs_type = {
    .name 
=        "sockfs",
    .get_sb 
=    sockfs_get_sb,
    .kill_sb 
=    kill_anon_super,
}; 
//地址簇及协议信息
static struct net_proto_family *net_families[NPROTO]; 
复制代码
sock_init在系统初始化的被调用:

3.1.3、BSD套接字的系统调用
实际上,Linux内核只提供了一个与套接字相关的系统调用,即sys_socketcall,应用程序的所有套接字调用都会映射到这个系统调用上。
复制代码
//BSD套接字调用入口(net/socket.c)
asmlinkage long sys_socketcall(int call, unsigned long __user *args)
{
    unsigned 
long a[6];
    unsigned 
long a0,a1;
    
int err;

    
if(call<1||call>SYS_RECVMSG)
        
return -EINVAL;

    
/* copy_from_user should be SMP safe. */
    
if (copy_from_user(a, args, nargs[call]))//从用户区拷贝参数
        return -EFAULT;
        
    a0
=a[0];
    a1
=a[1];
    
    
switch(call)  //调用相应的函数
    {
        
case SYS_SOCKET:
            err 
= sys_socket(a0,a1,a[2]);
            
break;
        
case SYS_BIND:
            err 
= sys_bind(a0,(struct sockaddr __user *)a1, a[2]);
            
break;
        
case SYS_CONNECT:
            err 
= sys_connect(a0, (struct sockaddr __user *)a1, a[2]);
            
break;
        
case SYS_LISTEN:
            err 
= sys_listen(a0,a1);
            
break;
        
case SYS_ACCEPT:
            err 
= sys_accept(a0,(struct sockaddr __user *)a1, (int __user *)a[2]);
            
break;
        
case SYS_GETSOCKNAME:
            err 
= sys_getsockname(a0,(struct sockaddr __user *)a1, (int __user *)a[2]);
            
break;
        
case SYS_GETPEERNAME:
            err 
= sys_getpeername(a0, (struct sockaddr __user *)a1, (int __user *)a[2]);
            
break;
        
case SYS_SOCKETPAIR:
            err 
= sys_socketpair(a0,a1, a[2], (int __user *)a[3]);
            
break;
        
case SYS_SEND:
            err 
= sys_send(a0, (void __user *)a1, a[2], a[3]);
            
break;
        
case SYS_SENDTO:
            err 
= sys_sendto(a0,(void __user *)a1, a[2], a[3],
                     (
struct sockaddr __user *)a[4], a[5]);
            
break;
        
case SYS_RECV:
            err 
= sys_recv(a0, (void __user *)a1, a[2], a[3]);
            
break;
        
case SYS_RECVFROM:
            err 
= sys_recvfrom(a0, (void __user *)a1, a[2], a[3],
                       (
struct sockaddr __user *)a[4], (int __user *)a[5]);
            
break;
        
case SYS_SHUTDOWN:
            err 
= sys_shutdown(a0,a1);
            
break;
        
case SYS_SETSOCKOPT:
            err 
= sys_setsockopt(a0, a1, a[2], (char __user *)a[3], a[4]);
            
break;
        
case SYS_GETSOCKOPT:
            err 
= sys_getsockopt(a0, a1, a[2], (char __user *)a[3], (int __user *)a[4]);
            
break;
        
case SYS_SENDMSG:
            err 
= sys_sendmsg(a0, (struct msghdr __user *) a1, a[2]);
            
break;
        
case SYS_RECVMSG:
            err 
= sys_recvmsg(a0, (struct msghdr __user *) a1, a[2]);
            
break;
        
default:
            err 
= -EINVAL;
            
break;
    }
    
return err;
}

//include/asm/unistd.h
#define __NR_socketcall        102  //系统调用号
复制代码
下面来看一下sys_socket的实现:
Code
3.2、INET套接字
INET套接字就是支持 Internet 地址族的套接字,它位于TCP协议之上, BSD套接字之下,如下:

3.2.1、数据结构
Code
inet_init()函数:
Code
sock_register()函数:
Code
inet_create()函数
Code

0 0
原创粉丝点击