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()函数
- /*
- * Perform the socket system call. we locate the appropriate
- * family, then create a fresh socket.
- */
- static int sock_socket(int family, int type, int protocol)
- {
- int i, fd;
- struct socket *sock;
- struct proto_ops *ops;
- /* Locate the correct protocol family. */
- for (i = 0; i < NPROTO; ++i) //查找对应的协议族
- {
- if (pops[i] == NULL) continue;
- if (pops[i]->family == family)
- break;
- }
- if (i == NPROTO) //查找未果,返回错误
- {
- return -EINVAL;
- }
- ops = pops[i];//指针指向该协议族的原型操作函数集
- /*
- * Check that this is a type that we know how to manipulate and
- * the protocol makes sense here. The family can still reject the
- * protocol later.
- */
- if ((type != SOCK_STREAM && type != SOCK_DGRAM &&
- type != SOCK_SEQPACKET && type != SOCK_RAW &&
- type != SOCK_PACKET) || protocol < 0)
- return(-EINVAL);
- /*
- * Allocate the socket and allow the family to set things up. if
- * the protocol is 0, the family is instructed to select an appropriate
- * default.
- */
- if (!(sock = sock_alloc())) //获取一个socket,已经完成了socket部分初始化设置
- {
- printk("NET: sock_socket: no more sockets\n");
- return(-ENOSR); /* Was: EAGAIN, but we are out of
- system resources! */
- }
- sock->type = type;
- sock->ops = ops;
- if ((i = sock->ops->create(sock, protocol)) < 0) //调用INET层函数,inet_create()函数,创建inet层的socket,即sock结构
- {
- sock_release(sock);
- return(i);
- }
- if ((fd = get_fd(SOCK_INODE(sock))) < 0) //根据sock结构中的inode,分配文件描述符
- {
- sock_release(sock);
- return(-EINVAL);
- }
- return(fd);
- }
该函数的大体功能:
1、分配socket,sock结构,用于BSD和INET层的socket
2、分配inode和file结构,用于文件操作
3、返回文件操作描述符,用于应用程序的使用
其中初始化分配一个socket的方法如下:
- /*
- * Allocate a socket.
- */
- struct socket *sock_alloc(void)
- {
- struct inode * inode;
- struct socket * sock;
- inode = get_empty_inode();//获一个空的文件结点
- if (!inode)
- return NULL;
- //文件结点相应字段赋值
- inode->i_mode = S_IFSOCK;
- inode->i_sock = 1;
- inode->i_uid = current->uid;
- inode->i_gid = current->gid;
- sock = &inode->u.socket_i;//给sicket结构指针赋值,可以看到inode和socket一一对应
- sock->state = SS_UNCONNECTED;
- sock->flags = 0;
- sock->ops = NULL;
- sock->data = NULL;
- sock->conn = NULL;
- sock->iconn = NULL;
- sock->next = NULL;
- sock->wait = &inode->i_wait;
- sock->inode = inode; /* "backlink": we could use pointer arithmetic instead */
- sock->fasync_list = NULL;
- sockets_in_use++;
- return sock;
- }
执行完,然后调用INET层的inet_create()函数
- /*
- * Create an inet socket.
- *
- * FIXME: Gcc would generate much better code if we set the parameters
- * up in in-memory structure order. Gcc68K even more so
- */
- //创建inet socket,即sock结构
- static int inet_create(struct socket *sock, int protocol)
- {
- struct sock *sk;
- struct proto *prot;
- int err;
- sk = (struct sock *) kmalloc(sizeof(*sk), GFP_KERNEL);//分配空间
- if (sk == NULL)
- return(-ENOBUFS);
- sk->num = 0;
- sk->reuse = 0;
- switch(sock->type)
- {
- case SOCK_STREAM:
- case SOCK_SEQPACKET:
- .................
- case SOCK_DGRAM:
- if (protocol && protocol != IPPROTO_UDP)
- {
- kfree_s((void *)sk, sizeof(*sk));
- return(-EPROTONOSUPPORT);
- }
- protocol = IPPROTO_UDP;
- sk->no_check = UDP_NO_CHECK;
- prot=&udp_prot;//原型指针指向UDP的原型定义
- break;
- case SOCK_RAW:
- .........................
- break;
- case SOCK_PACKET:
- ..........................
- break;
- default:
- kfree_s((void *)sk, sizeof(*sk));
- return(-ESOCKTNOSUPPORT);
- }
- sk->socket = sock;//可以看出sock和socket的对应关系
- .................
- sk->type = sock->type;
- sk->stamp.tv_sec=0;
- sk->protocol = protocol;
- sk->wmem_alloc = 0;
- sk->rmem_alloc = 0;
- sk->sndbuf = SK_WMEM_MAX;
- sk->rcvbuf = SK_RMEM_MAX;
- ......................................//sock的初始化
- /* this is how many unacked bytes we will accept for this socket. */
- sk->max_unacked = 2048; /* needs to be at most 2 full packets. */
- /* how many packets we should send before forcing an ack.
- if this is set to zero it is the same as sk->delay_acks = 0 */
- sk->max_ack_backlog = 0;
- sk->inuse = 0;
- sk->delay_acks = 0;
- skb_queue_head_init(&sk->write_queue);
- skb_queue_head_init(&sk->receive_queue);
- sk->mtu = 576;
- sk->prot = prot;
- sk->sleep = sock->wait;
- sk->daddr = 0;//远端地址
- sk->saddr = 0 /* ip_my_addr() */;//本地地址
- sk->err = 0;
- sk->next = NULL;
- sk->pair = NULL;
- sk->send_tail = NULL;//发送链表尾
- sk->send_head = NULL;//发送链表头
- ..............................
- skb_queue_head_init(&sk->back_log);//初始化双链表
- ..................................
- sk->ip_tos=0;
- sk->ip_ttl=64;
- ...................................
- if (sk->num) //本地端口号不空
- {
- /*
- * It assumes that any protocol which allows
- * the user to assign a number at socket
- * creation time automatically
- * shares.
- */
- put_sock(sk->num, sk);//根据端口号将sock结构加入sock表中
- sk->dummy_th.source = ntohs(sk->num);
- }
- if (sk->prot->init) //UDP的初始化函数为空
- {
- err = sk->prot->init(sk);
- if (err != 0)
- {
- destroy_sock(sk);
- return(err);
- }
- }
- return(0);
- }
返回文件描述的操作符
- /*
- * Obtains the first available file descriptor and sets it up for use.
- */
- //根据文件inode指针创建文件结构,并返回文件操作的操作符,用于应用程序的使用
- static int get_fd(struct inode *inode)
- {
- int fd;
- struct file *file;
- /*
- * Find a file descriptor suitable for return to the user.
- */
- file = get_empty_filp();
- if (!file)
- return(-1);
- for (fd = 0; fd < NR_OPEN; ++fd)
- if (!current->files->fd[fd])
- break;
- if (fd == NR_OPEN)
- {
- file->f_count = 0;
- return(-1);
- }
- FD_CLR(fd, ¤t->files->close_on_exec);
- current->files->fd[fd] = file;
- file->f_op = &socket_file_ops;
- file->f_mode = 3;
- file->f_flags = O_RDWR;
- file->f_count = 1;
- file->f_inode = inode;
- if (inode)
- inode->i_count++;
- file->f_pos = 0;
- return(fd);
- }
下面开始正式看发送数据的最顶层函数--sock_write()函数
- /*
- * Write data to a socket. We verify that the user area ubuf..ubuf+size-1 is
- * readable by the user process.
- */
- static int sock_write(struct inode *inode, struct file *file, char *ubuf, int size)
- {
- struct socket *sock;
- int err;
- if (!(sock = socki_lookup(inode))) //返回inode结构的对应的socket结构
- {
- printk("NET: sock_write: can't find socket for inode!\n");
- return(-EBADF);
- }
- if (sock->flags & SO_ACCEPTCON)
- return(-EINVAL);
- if(size<0)
- return -EINVAL;
- if(size==0)
- return 0;
- if ((err=verify_area(VERIFY_READ,ubuf,size))<0)
- return err;
- return(sock->ops->write(sock, ubuf, size,(file->f_flags & O_NONBLOCK)));//调用inet_write()函数
- }
inet_write()函数
- static int inet_write(struct socket *sock, char *ubuf, int size, int noblock)
- {
- return inet_send(sock,ubuf,size,noblock,0);
- }
inet_send()函数
- static int inet_send(struct socket *sock, void *ubuf, int size, int noblock,
- unsigned flags)
- {
- struct sock *sk = (struct sock *) sock->data;//从socket结构中取出sock指针
- if (sk->shutdown & SEND_SHUTDOWN)
- {
- send_sig(SIGPIPE, current, 1);
- return(-EPIPE);
- }
- if(sk->err)
- return inet_error(sk);
- /* We may need to bind the socket. */
- if(inet_autobind(sk)!=0)//自动分配本地端口号,并将sk根据端口号加入sock表中
- return(-EAGAIN);
- return(sk->prot->write(sk, (unsigned char *) ubuf, size, noblock, flags));//调用udp_write()函数
- }
这样系统就会调用传输层(还是以UDP为例)的函数udp_write()来发送数据,这样数据就从应用层到了传输层。下篇分析传输层向网络层的数据传输。
Linux内核--网络栈实现分析(八)--应用层发送数据(下)的更多相关文章
- Linux内核--网络栈实现分析(七)--数据包的传递过程(下)
本文分析基于Linux Kernel 1.2.13 原创作品,转载请标明http://blog.csdn.net/yming0221/article/details/7545855 更多请查看专栏,地 ...
- Linux内核--网络栈实现分析(二)--数据包的传递过程--转
转载地址http://blog.csdn.net/yming0221/article/details/7492423 作者:闫明 本文分析基于Linux Kernel 1.2.13 注:标题中的”(上 ...
- Linux内核--网络栈实现分析(二)--数据包的传递过程(上)
本文分析基于Linux Kernel 1.2.13 原创作品,转载请标明http://blog.csdn.net/yming0221/article/details/7492423 更多请看专栏,地址 ...
- Linux内核--网络栈实现分析(十一)--驱动程序层(下)
本文分析基于Linux Kernel 1.2.13 原创作品,转载请标明http://blog.csdn.net/yming0221/article/details/7555870 更多请查看专栏,地 ...
- Linux内核--网络栈实现分析(一)--网络栈初始化
本文分析基于内核Linux Kernel 1.2.13 原创作品,转载请标明http://blog.csdn.net/yming0221/article/details/7488828 更多请看专栏, ...
- Linux内核--网络栈实现分析(一)--网络栈初始化--转
转载地址 http://blog.csdn.net/yming0221/article/details/7488828 作者:闫明 本文分析基于内核Linux Kernel 1.2.13 以后的系列博 ...
- Linux内核--网络栈实现分析(三)--驱动程序层+链路层(上)
本文分析基于Linux Kernel 1.2.13 原创作品,转载请标明http://blog.csdn.net/yming0221/article/details/7497260 更多请看专栏,地址 ...
- Linux内核--网络栈实现分析(六)--应用层获取数据包(上)
本文分析基于内核Linux 1.2.13 原创作品,转载请标明http://blog.csdn.net/yming0221/article/details/7541907 更多请看专栏,地址http: ...
- Linux内核--网络栈实现分析(四)--网络层之IP协议(上)
本文分析基于Linux Kernel 1.2.13 原创作品,转载请标明http://blog.csdn.net/yming0221/article/details/7514017 更多请看专栏,地址 ...
随机推荐
- iOS8以后自动计算cell的高度
前提: 1.iOS系统>=8 2.cell中的每个控件布局固定,不含一些动态的模块,但是可以含有label的变化 可以采用tableView自动计算cell的高度 首先设置tableView的属 ...
- 使用HttpClient 4.3.4 自动登录并抓取中国联通用户基本信息和账单数据,GET/POST/Cookie
一.什么是HttpClient? HTTP 协议可能是现在 Internet 上使用得最多.最重要的协议了,越来越多的 Java 应用程序需要直接通过 HTTP 协议来访问网络资源.虽然在 JDK 的 ...
- JS之BOM、客户端检测和DOM
这是第八章到第十二章的内容,粗略浏览过一遍. 一.BOM 浏览器对象模型.包括了window.location.navigator.screen和history对象. window:核心对象 1.JS ...
- java多线程详解(8)-volatile,Atomic比较
在变成过程中我们需要保证变量的线程安全,在java中除了使用锁机制或者Threadlocal等保证线程安全,还提供了 java.util.concurrent.atomic.Atomic*(如Atom ...
- discuz!安装遇到问题的解决方案
正常的安装步骤好多地方都有写过了,我安装的时候遇到问题百度翻了个遍也没有找到,现在问题已经解决了,发出了分享一下! 进入第三步创建数据库的时候提示:由于目标计算机积极拒绝,无法连接. 打开phpmya ...
- DBlink与同义词
DBLink就是数据库链接,而同义词就已经具体到某个用户下的表了 原文链接:http://www.linuxidc.com/Linux/2013-01/77579.htm 这里所需要的信息: 从MM库 ...
- MySQL 性能优化的最佳20多条经验分享
当我们去设计数据库表结构,对操作数据库时(尤其是查表时的SQL语句),我们都需要注意数据操作的性能.这里,我们不会讲过多的SQL语句的优化,而只是针对MySQL这一Web应用最多的数据库.希望下面的这 ...
- VC++修改电脑系统时间
我们可以通过代码,对系统时间进行获取和修改. 我将介绍两种方法.第一种,通过SYSTEMTIME 代码实现: #include <windows.h> #include <iostr ...
- CSS3制作各种形状图像
圆形-椭圆形-三角形-倒三角形=左三角形-右三角形-菱形-平行四边形- 星形-六角星形-五边形-六边形-八角形-心形-蛋形-无穷符号-消息提示框 不废话直接 html界面(亲测的) ------转自百 ...
- 未定义标识符string
“未定义标识符string” 解决方法: 头文件加上 #include <iostream>using namespace std; string是标准库的,要加std::string, ...