linux 协议栈

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值