zoukankan      html  css  js  c++  java
  • Linux内核网络栈实现分析(八)应用层发送数据(下)

    本文分析基于Linux Kernel 1.2.13

    原创作品,转载请标明http://blog.csdn.net/yming0221/article/details/7547826

    更多请查看专栏,地址http://blog.csdn.net/column/details/linux-kernel-net.html

    作者:闫明

    注:标题中的”(上)“,”(下)“表示分析过程基于数据包的传递方向:”(上)“表示分析是从底层向上分析、”(下)“表示分析是从上向下分析。


    下面是发送数据的流程:


    应用层发送数据包的入口函数是BSD socket层的sock_write()函数,在分析该函数之前,先分析下socket的创建,系统调用sys_socket()对应的BSD socket层函数为sock_socket()

    sock_socket()函数

    1. /* 
    2.  *  Perform the socket system call. we locate the appropriate 
    3.  *  family, then create a fresh socket. 
    4.  */  
    5.   
    6. static int sock_socket(int family, int type, int protocol)  
    7. {  
    8.     int i, fd;  
    9.     struct socket *sock;  
    10.     struct proto_ops *ops;  
    11.   
    12.     /* Locate the correct protocol family. */  
    13.     for (i = 0; i < NPROTO; ++i) //查找对应的协议族  
    14.     {  
    15.         if (pops[i] == NULL) continue;  
    16.         if (pops[i]->family == family)   
    17.             break;  
    18.     }  
    19.   
    20.     if (i == NPROTO) //查找未果,返回错误  
    21.     {  
    22.         return -EINVAL;  
    23.     }  
    24.   
    25.     ops = pops[i];//指针指向该协议族的原型操作函数集  
    26.   
    27. /* 
    28.  *  Check that this is a type that we know how to manipulate and 
    29.  *  the protocol makes sense here. The family can still reject the 
    30.  *  protocol later. 
    31.  */  
    32.     
    33.     if ((type != SOCK_STREAM && type != SOCK_DGRAM &&  
    34.         type != SOCK_SEQPACKET && type != SOCK_RAW &&  
    35.         type != SOCK_PACKET) || protocol < 0)  
    36.             return(-EINVAL);  
    37.   
    38. /* 
    39.  *  Allocate the socket and allow the family to set things up. if 
    40.  *  the protocol is 0, the family is instructed to select an appropriate 
    41.  *  default. 
    42.  */  
    43.   
    44.     if (!(sock = sock_alloc())) //获取一个socket,已经完成了socket部分初始化设置  
    45.     {  
    46.         printk("NET: sock_socket: no more sockets\n");  
    47.         return(-ENOSR); /* Was: EAGAIN, but we are out of 
    48.                    system resources! */  
    49.     }  
    50.   
    51.     sock->type = type;  
    52.     sock->ops = ops;  
    53.     if ((i = sock->ops->create(sock, protocol)) < 0) //调用INET层函数,inet_create()函数,创建inet层的socket,即sock结构  
    54.     {  
    55.         sock_release(sock);  
    56.         return(i);  
    57.     }  
    58.   
    59.     if ((fd = get_fd(SOCK_INODE(sock))) < 0) //根据sock结构中的inode,分配文件描述符  
    60.     {  
    61.         sock_release(sock);  
    62.         return(-EINVAL);  
    63.     }  
    64.   
    65.     return(fd);  
    66. }  

    该函数的大体功能:

    1、分配socket,sock结构,用于BSD和INET层的socket

    2、分配inode和file结构,用于文件操作

    3、返回文件操作描述符,用于应用程序的使用

    其中初始化分配一个socket的方法如下:

    1. /* 
    2.  *  Allocate a socket. 
    3.  */  
    4.   
    5. struct socket *sock_alloc(void)  
    6. {  
    7.     struct inode * inode;  
    8.     struct socket * sock;  
    9.   
    10.     inode = get_empty_inode();//获一个空的文件结点  
    11.     if (!inode)  
    12.         return NULL;  
    13.     //文件结点相应字段赋值  
    14.     inode->i_mode = S_IFSOCK;  
    15.     inode->i_sock = 1;  
    16.     inode->i_uid = current->uid;  
    17.     inode->i_gid = current->gid;  
    18.   
    19.     sock = &inode->u.socket_i;//给sicket结构指针赋值,可以看到inode和socket一一对应  
    20.     sock->state = SS_UNCONNECTED;  
    21.     sock->flags = 0;  
    22.     sock->ops = NULL;  
    23.     sock->data = NULL;  
    24.     sock->conn = NULL;  
    25.     sock->iconn = NULL;  
    26.     sock->next = NULL;  
    27.     sock->wait = &inode->i_wait;  
    28.     sock->inode = inode;     /* "backlink": we could use pointer arithmetic instead */  
    29.     sock->fasync_list = NULL;  
    30.     sockets_in_use++;  
    31.     return sock;  
    32. }  

    执行完,然后调用INET层的inet_create()函数

    1. /* 
    2.  *  Create an inet socket. 
    3.  * 
    4.  *  FIXME: Gcc would generate much better code if we set the parameters 
    5.  *  up in in-memory structure order. Gcc68K even more so 
    6.  */  
    7. //创建inet socket,即sock结构  
    8. static int inet_create(struct socket *sock, int protocol)  
    9. {  
    10.     struct sock *sk;  
    11.     struct proto *prot;  
    12.     int err;  
    13.   
    14.     sk = (struct sock *) kmalloc(sizeof(*sk), GFP_KERNEL);//分配空间  
    15.     if (sk == NULL)   
    16.         return(-ENOBUFS);  
    17.     sk->num = 0;  
    18.     sk->reuse = 0;  
    19.     switch(sock->type)   
    20.     {  
    21.         case SOCK_STREAM:  
    22.         case SOCK_SEQPACKET:  
    23.             .................  
    24.   
    25.         case SOCK_DGRAM:  
    26.             if (protocol && protocol != IPPROTO_UDP)   
    27.             {  
    28.                 kfree_s((void *)sk, sizeof(*sk));  
    29.                 return(-EPROTONOSUPPORT);  
    30.             }  
    31.             protocol = IPPROTO_UDP;  
    32.             sk->no_check = UDP_NO_CHECK;  
    33.             prot=&udp_prot;//原型指针指向UDP的原型定义  
    34.             break;  
    35.         
    36.         case SOCK_RAW:  
    37.             .........................  
    38.             break;  
    39.   
    40.         case SOCK_PACKET:  
    41.             ..........................  
    42.             break;  
    43.   
    44.         default:  
    45.             kfree_s((void *)sk, sizeof(*sk));  
    46.             return(-ESOCKTNOSUPPORT);  
    47.     }  
    48.     sk->socket = sock;//可以看出sock和socket的对应关系  
    49. .................  
    50.     sk->type = sock->type;  
    51.     sk->stamp.tv_sec=0;  
    52.     sk->protocol = protocol;  
    53.     sk->wmem_alloc = 0;  
    54.     sk->rmem_alloc = 0;  
    55.     sk->sndbuf = SK_WMEM_MAX;  
    56.     sk->rcvbuf = SK_RMEM_MAX;  
    57.     ......................................//sock的初始化  
    58.   
    59.     /* this is how many unacked bytes we will accept for this socket.  */  
    60.     sk->max_unacked = 2048; /* needs to be at most 2 full packets. */  
    61.   
    62.     /* how many packets we should send before forcing an ack.  
    63.        if this is set to zero it is the same as sk->delay_acks = 0 */  
    64.     sk->max_ack_backlog = 0;  
    65.     sk->inuse = 0;  
    66.     sk->delay_acks = 0;  
    67.     skb_queue_head_init(&sk->write_queue);  
    68.     skb_queue_head_init(&sk->receive_queue);  
    69.     sk->mtu = 576;  
    70.     sk->prot = prot;  
    71.     sk->sleep = sock->wait;  
    72.     sk->daddr = 0;//远端地址  
    73.     sk->saddr = 0 /* ip_my_addr() */;//本地地址  
    74.     sk->err = 0;  
    75.     sk->next = NULL;  
    76.     sk->pair = NULL;  
    77.     sk->send_tail = NULL;//发送链表尾  
    78.     sk->send_head = NULL;//发送链表头  
    79.     ..............................  
    80.     skb_queue_head_init(&sk->back_log);//初始化双链表  
    81.     ..................................  
    82.     sk->ip_tos=0;  
    83.     sk->ip_ttl=64;  
    84. ...................................  
    85.       
    86.   
    87.     if (sk->num) //本地端口号不空  
    88.     {  
    89.     /* 
    90.      * It assumes that any protocol which allows 
    91.      * the user to assign a number at socket 
    92.      * creation time automatically 
    93.      * shares. 
    94.      */  
    95.         put_sock(sk->num, sk);//根据端口号将sock结构加入sock表中  
    96.         sk->dummy_th.source = ntohs(sk->num);  
    97.     }  
    98.   
    99.     if (sk->prot->init) //UDP的初始化函数为空  
    100.     {  
    101.         err = sk->prot->init(sk);  
    102.         if (err != 0)   
    103.         {  
    104.             destroy_sock(sk);  
    105.             return(err);  
    106.         }  
    107.     }  
    108.     return(0);  
    109. }  

    返回文件描述的操作符

    1. /* 
    2.  *  Obtains the first available file descriptor and sets it up for use.  
    3.  */  
    4. //根据文件inode指针创建文件结构,并返回文件操作的操作符,用于应用程序的使用  
    5. static int get_fd(struct inode *inode)  
    6. {  
    7.     int fd;  
    8.     struct file *file;  
    9.   
    10.     /* 
    11.      *  Find a file descriptor suitable for return to the user.  
    12.      */  
    13.   
    14.     file = get_empty_filp();  
    15.     if (!file)   
    16.         return(-1);  
    17.   
    18.     for (fd = 0; fd < NR_OPEN; ++fd)  
    19.         if (!current->files->fd[fd])   
    20.             break;  
    21.     if (fd == NR_OPEN)   
    22.     {  
    23.         file->f_count = 0;  
    24.         return(-1);  
    25.     }  
    26.   
    27.     FD_CLR(fd, &current->files->close_on_exec);  
    28.         current->files->fd[fd] = file;  
    29.     file->f_op = &socket_file_ops;  
    30.     file->f_mode = 3;  
    31.     file->f_flags = O_RDWR;  
    32.     file->f_count = 1;  
    33.     file->f_inode = inode;  
    34.     if (inode)   
    35.         inode->i_count++;  
    36.     file->f_pos = 0;  
    37.     return(fd);  
    38. }  

    下面开始正式看发送数据的最顶层函数--sock_write()函数

    1. /* 
    2.  *  Write data to a socket. We verify that the user area ubuf..ubuf+size-1 is 
    3.  *  readable by the user process. 
    4.  */  
    5.   
    6. static int sock_write(struct inode *inode, struct file *file, char *ubuf, int size)  
    7. {  
    8.     struct socket *sock;  
    9.     int err;  
    10.       
    11.     if (!(sock = socki_lookup(inode))) //返回inode结构的对应的socket结构  
    12.     {  
    13.         printk("NET: sock_write: can't find socket for inode!\n");  
    14.         return(-EBADF);  
    15.     }  
    16.   
    17.     if (sock->flags & SO_ACCEPTCON)   
    18.         return(-EINVAL);  
    19.       
    20.     if(size<0)  
    21.         return -EINVAL;  
    22.     if(size==0)  
    23.         return 0;  
    24.           
    25.     if ((err=verify_area(VERIFY_READ,ubuf,size))<0)  
    26.         return err;  
    27.     return(sock->ops->write(sock, ubuf, size,(file->f_flags & O_NONBLOCK)));//调用inet_write()函数  
    28. }  

    inet_write()函数

    1. static int inet_write(struct socket *sock, char *ubuf, int size, int noblock)  
    2. {  
    3.     return inet_send(sock,ubuf,size,noblock,0);  
    4. }  

    inet_send()函数

    1. static int inet_send(struct socket *sock, void *ubuf, int size, int noblock,   
    2.            unsigned flags)  
    3. {  
    4.     struct sock *sk = (struct sock *) sock->data;//从socket结构中取出sock指针  
    5.     if (sk->shutdown & SEND_SHUTDOWN)   
    6.     {  
    7.         send_sig(SIGPIPE, current, 1);  
    8.         return(-EPIPE);  
    9.     }  
    10.     if(sk->err)  
    11.         return inet_error(sk);  
    12.     /* We may need to bind the socket. */  
    13.     if(inet_autobind(sk)!=0)//自动分配本地端口号,并将sk根据端口号加入sock表中  
    14.         return(-EAGAIN);  
    15.     return(sk->prot->write(sk, (unsigned char *) ubuf, size, noblock, flags));//调用udp_write()函数  
    16. }  


    这样系统就会调用传输层(还是以UDP为例)的函数udp_write()来发送数据,这样数据就从应用层到了传输层。下篇分析传输层向网络层的数据传输。
  • 相关阅读:
    float
    老师的通病
    无题
    BufferedReader
    剩余定理
    ActionScript 多图加载 按图顺序索引
    C++ Socket 编程
    设计高可用和高负载的网站系统
    提高网站速度的最佳实践【翻译】
    把哈希表存储到数据库中
  • 原文地址:https://www.cnblogs.com/hehehaha/p/6332912.html
Copyright © 2011-2022 走看看