socket创建-内核中
来源:互联网 发布:华硕笔记本优化 编辑:程序博客网 时间:2024/06/01 10:11
asmlinkage long sys_socket(int family,int type,int protocol){ int retval; struct socket*sock; retval=sock_create(family,type,protocol,&sock); if(retval<0)goto out; retval=sock_map_fd(sock); if(retval<0)goto out_release;out: return retval;out_release: sock_release(sock); return retval;}static int __sock_create(int family,int type,int protocol,struct socket**res,int kern){ int err; struct socket*sock; const struct net_proto_family*pf; if(family<0||family>=NRROTO)return -EAFNOSUPPROT; if(type<0||type>=SOCK_MAX)return -EINVAL; .....; sock=sock_alloc(); /*此函数不止是生成socket,还有相关的i节点*/ sock->type=type; ...; pf=rcu_dererence(net_families[family]); err=pf->create(sock,protocol); ...; *res=sock; return 0; ...;}static struct file_system_type sockfs_type={ .name="sockfs", .get_sb=sockfs_get_sb, .kill_sb=kill_anon_super,}; /* 每一种文件系统都是用file_systme_type结构表示, 这里的get_sb函数是从磁盘中读取该文件系统的超级块,然后进行处理 kill_sb即销毁sb 注意:一个超级块就代表一个文件系统 */static struct super_operations sockfs_ops={ .alloc_inode=sock_alloc_inode, .destroy_inode=sock_destroy_inode, .statfs=simple_statfs,}; /*对于每个文件系统,都要有操作超级块的函数集,这个就是, 其中alloc_inode函数用于:根据给定的超级块,分配i节点, 即如果在socket的文件系统中,要生成一个i节点,则需要调用这里 的alloc_inode函数,即sock_alloc_inode 下面会用到 */static struct socket* sock_alloc(void){ struct inode *inode; struct socket *sock; inode=new_inode(sock_mnt->mnt_sb); /*在此中,不止是生成i节点,还生成了socket*/ /*这个涉及到套接子的文件系统,这里说明一下*/ if(!inode)return NULL; sock=SOCKET_I(inode); /*这是个根据inode节点来找到socket结构的内联函数 * 在普通的i节点中,只用i节点这么一个结构, * 但在socket文件系统中,由于socket和inode的紧密关系, * 把他们放入到了一个结构中:struct socket_alloc * struct socket_alloc{ * struct socket socket; * struct inode vfs_inode; * } * 这里的new_inode就是根据其超级块的类型来找到合适的alloc_inode函数 * 故下面主要是socket文件系统中的alloc_inode函数,即sock_allo_inode * 它就是来生成socket文件系统中的“inode节点”,即socket_alloc结构 */ ....; return sock;}static struct inode *sock_alloc_inode(struct super_block*sb){ struct socket_alloc*ei; ei=kmem_cache_alloc(sock_inode_cachep,GFP_KERNEL);/*slab知识*/ init_waitqueue_head(&ei->socket.wait); ei->socket.fasyn_list=NULL; ...; ei->socket.sk=NULL; ei->socket.ops=NULL; ei->socket.file=NULL; ...; return &ei->vfs_inode;}/*到此,已经完成__sock_create()中的sock_alloc部分, * 下面是_sock_create()中的pf->create部分, * 这部分主要是来完成socket中sock结构的生成,以及一些初始化的工作 * * 在描述pf->create之前,还要熟知的知识,设计到协议栈的构建: * * pf是net_family_ops结构,,这个结构就代表了一个协议族,那先看看 * ipv4协议族即AF_INET是怎样的 * * static struct net_proto_family inet_family_ops={ * .family=AF_INET; * .create=inet_create; * .woner=THIS_MODULE; * } * * 在上文中的函数,它按照family的值去 * 搜索net_families数组, pf=rcu_dererence(net_families[family]); * * 而net_families是个全局数组,放置的是协议族,通过链表将各个协议族 * 连接起来。 * * 故从net_families数组中根据family的值找到了ipv4的协议族,那么 * pf->create函数就是inet_create 函数辣。。 */static int inet_create(struct socket*sock,int protocol){ struct sock*sk;/*前面说过,这个函数的主要目的是生成结构sock*/ struct list_head*p; struct inet_protosw*answer; struct inet_sock*inet; /*sock结构是通用结构,而inet_sock是专用sock结构*/ struct proto *anwer_prot; ...; list_for_each_rcu(p,&inetsw[sock->type]){answer=list_entry(p,struct inet_protosw,list);if(protocol==answer->protocol){ if(protocol!=IPPROTO_IP)break;}else{ if(IPPOROTO_IP==protocol) {protocol=answer->protocol;break; } if(IPPROTO_IP==anwswer->protocol)break;}err=-EPROTONOSUPPROT;answer=NULL; } ....; sock->ops=answer->ops; /*把proto_ops操作集赋予个sock->ops结构*/ .....; sk=sk_alloc(PF_INET,GFP_KERNEL,answer_proto,1);/*生成sock结构*/ ...; inet=inet_sk(sk);/*把sock结构转为专用的inet_sock结构*/ ...; sk->sk_prot->hash(sk); /*把prot操作集赋予sock->sk_prot??*/ ....;} /* 上面的函数有些补充知识 * * 在套接口层与传输层结合的地方,有一个重要的数据结构proto_ops * 它当中是一组与套接口系统调用想对应的传输层函数指针,因此整个proto_ops * 可以看作是一张套接口调用到传输层的跳转表(在这些函数中,可能会进一步的 * 通过proto跳转,下面说),进入到具体的传输层或网络层的处理。 * 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); * ............; * * 一些列的函数指针 * ............; * } * 既然proto_ops结构完成从协议无关层的套接口层到协议相关的传输层的转接 * proto结构(这里还没说,暂且放在这)完成从传输层到网络层的转接,那么可 * 知,每个传输层的协议都需要定义一个特定的protos_ops结构实例和proto结构 * 实例。 * * 在ipv4协议族中,一个传输层协议对应一个inet_protosw结构,而这个结构中 * 就包含了proto_ops和proto结构。 * * 协议族中的所有inet_protosw结构都定义在全局数组inetsw_array[]中, * * inetsw_array[]={ * { type=SOCK_STREAM; * protocol=IPPROTO_TCP; * prot=&tcp_prot; 这里就是proto结构了 * ops=&inet_stream_ops; 这里就proto_ops结构了 * } ,这就是一个inet_protosw结构了 * { type=SOCK_DGRAM; * protocol=IPPROTO_UDP; * prot=&udp_prot; * ops=&inet_dgram_ops; * } , * ..... * } * * 当创建socket的时候,就在这个数组中搜寻匹配的inet_protosw结构 , * 然后将其proto_ops结构存储在socket的ops中,将proto结构放在 * socket的sock结构的sk_prot中。 * * * 这样inet_create 函数创建了sock结构,且 * 把socket的proto_ops和sock的prot都赋予了各自的操作集 * * 因为以后所有针对socket的函数都需要调用这些操作集函数,所以 * 在生成socket的时候,就要一切都准备好* *//*前面讲述了sys_socket的sock_create部分,即socket和inode节点的生成以及一些 * 初始化工作,那么socket和文件描述符的一一对应关系则是在 * sock_map_fd(socket) */int sock_map_fd(struct socket*sock){ /*无论何时,变量的地址是不会变的,变的 * 只是变量中存放的值,,若想改变这个变量的值 * 则只需传递变量的地址即可了,指针变量也是一样 */ struct file*newfile; int fd=sock_alloc_fd(&newfile); /*传递指针的地址可以改变指针的指向 若只是传递指针,则不会改变指针的指向 */ if(fd>=0){int err=sock_attack_fd(sock,newfile);if(err<0){ put_filp(newfile); put_unused_fd(fd);}fd_install(fd,newfile);/*将文件描述符结构实例file增加到已经打开的 文件列表中,完成进程和文件的关联(fs/open.c)*/ }} static int sock_alloc_fd(struct file**filep){ int fd; fd=get_unused_fd(); if(likely(fd>=0)){struct file*file=get_empty_filp();*filep=file;if(unlikely(!file)){ put_unused_fd(fd); return -ENFILE;} } else*filep=NULL; return fd;} int sock_attack_fd(struct socket*sock,struct file*file){ .....; sock->file=file; file->f_op=&socket_file_ops; /*每个文件对象file都有其操作集,这里既然是socket文件系统的文件对象 * 那么它也有操作集,初始化为socket_file_ops, * 这给socket提供了另一种读取文件的方法 */ SOCK_INODE(sock)->i_fops=&socket_file_ops;/*和上面一样,只是把inode的 操作集也初始化成一样 */ file->private=sock; ...; return 0;}/*至此,socket的创建就完成了,总结来看,它完成了这么几个事情* * * 1,生成了socket结构和sock结构 * 2,初始化了socket结构的操作集proto_ops和sock的操作集proto * 3,建立了socket和文件描述符的关系(通过inode节点). * 4, 建立了和socket关联的文件描述符的操作集f_ops。 *//*这里只是分析到套接口层,而套接口层继续调用额传输层的功能完成各种 */asmlinkage long sys_sendto(int fd,void __user *buff,size_t len, unsigned flags,struct sockaddr __user*addr,int addr_len){ /*在socket的io中,最后都利用了sock_sendmsg函数*/ struct socket *sock; char address[MAX_SOCK_ADDR]; int err; struct msghdr msg; /*在io中,都是利用这个结构来传递用户太和内核太的消息的, * 这个结构能够描述更多完整的信息,包括数据块、控制块的信息 * struct msghdr * { * void *msg_name; //socket name * int msg_namelne; // length of name * struct iovec*msg_iov;//Data blocks * * struct iovec{ * void __user*iov_base;数据块的基址 * __kernel_size_t iov_len;一个数据块的长度 * } * //这个结构就是存储消息的数据结构, * 通常在构造msghdr的时候,都是先构造出iovec结构 * __kernel_size_t msg_iovlen;//Number of blocks * * void *msg_control; * __kernel_size_t msg_controllen; * unsigned msg_flags; * } */ struct iovec iov; struct file*sock_file; int fput_needed; sock_file=fget_light(fd,&fput_needed);//fd-->file; if(!sock_file) return -EBADF; sock=sock_from_file(sock_file,&err); if(!sock) goto out_put; iov.iov_base=buff;/*先构造iovec结构,这里把用户空间的buff 地址赋值给iovec结构的基址 */ iov.iov_len=len; msg.msg_name=NULL; msg.msg_iov=&iov;/*这里只生成了一个数据块*/ msg.msg_iovlen=1; msg.msg_control=NULL; msg.msg_controllen=0; msg.msg_namelen=0; if(addr){//判断这个就是为了是send调用的时候可以兼容,下面一个函数 err=move_addr_to_kernel(addr,addr_len,address); /*如果目的地址有效,则需要复制到内核中,组装完成msg结构*/ if(err<0) goto out_put; msg.msg_name=address;/*为何不直接把addr赋值到这里呢? 因为直接从用户空间赋值到内核空间是不安全的, 恶意的用户可能会是内核creash,所以 move_addr_to_kernel这个函数进行了严格的 审查 */ msg.msg_namelen=addr_len; } if(sock->file->f_flags& O_NONBLOCK) flags|=MSG_DONTWAIT; /*如果套接子设置了不阻塞选项(从中可以看出设置的选项都是作用到 * 其关联的file文件的flags上面) * ,那么也给相应的msg设置flags,不过需要把文件系统的标志(O_NONBLOCK) * 转为网络系统的标志(MSG_DONTWAIT) */ msg.msg_flags=flags; err=sock_sendmsg(sock,&msg,len); //最后调用sock_sendmsg发送到 //内核缓冲区,注意不是发送到 //对方,这里只是把数据复制到 //内核,具体的发送到对方还需要传输层层 //和网络层以及链路曾的参与 // //故需要追踪到传输层 out_put: fput_light(sock_file,fput_needed); return err;}asmlinkage long sys_send(int fd,void __user*buff,size_t len,unsigned flags){ return sys_sendto(fd,buff,len,flags,NULL,0); /*send只是简单地调用上面的函数,区别在于他们有指明目的地址, * 不过,发送出去的数据还是要指明目的地址的,还是要设置msg的目的地址的 * 那么这个地址在哪里呢?connect已经把它完成了,看看connect。 */ asmlinkage long sys_connect(int fd,struct sockaddr __user* uservaddr, int addrlen) { struct socket *sock; char address[MAX_SOCK_ADDR]; int err,fput_needed; sock=sockfd_lookup_light(fd,&err,&fput_needed); if(!sock) goto out; err=move_addr_to_kernel(uservaddr,addrlen,address); if(err<0) goto out_put; //安全检查 err=sock->ops->connect(sock,(struct sockaddr*)address,addrlen, sock->file->f_flags); /*最终调用了ops操作集*/out_put: fput_light(sock->file,fput_needed);out: return err; }}/*下面看看ops中的connect,这里假设sock是stream,则ops->connect为 * inet_stream_connect */int inet_stream_connect(struct socket*sock,struct sockaddr*uaddr, int addr_len,int flags){ struct sock*sk=sock->sk; int err; long timeo; lock_sock(sk); ...; switch(sock->state){ /*在创建socket的时候,把它的state设置为了SS_UNCONNECTED*/ case SS_UNCONNECTED: err=-EISONN; if(sk->sk_state!=TCP_CLOSE) goto out; err=sk->sk_prot->connect(sk,uaddr,addr_len); /*最后调用了传输层sock的操作集connect*/ /*而传输层的操作集中connect操作是tcp_v4_connect*/ /*而这个函数调用了很多路由方面的函数,先放放吧*/ if(err<0) goto out; sock->state=SS_CONNCTING; err=-EINPROGRESS; break; .....; } timeo=sock ....;}
0 0
- socket创建-内核中
- 内核中accept连接时创建socket结构错误导致的内存泄露
- linux内核中socket的创建过程源码分析(详细分析)
- linux内核中socket的创建过程源码分析(总结性质)
- Linux内核创建socket的过程(1)
- Linux内核创建socket的过程(1)
- Linux内核创建socket的过程
- Linux内核Socket实现之------Socket创建(2)
- Linux内核中创建线程
- linux内核中socket的实现
- linux内核中socket的实现
- Linux内核网络协议栈3-创建socket(1)
- Linux内核网络协议栈4-创建socket(2)
- linux内核网络协议分析1-----socket创建
- 解决校园网中无法创建socket问题
- 4-socket的实践到内核--追踪socket的创建
- 4-socket的实践到内核--追踪socket的创建 .
- 应用层创建socket,内核模块通过该socket发送数据包
- KM模板
- 和戮蓝萍酱偶眯胁喊廖仿收槐舅殉
- 路涯倬被殴硬沾厮屹财毫驮寥桨宦
- 假撩复问恳撞豪伟戎媳文部灿枷狙
- 瓤俏吃傧缸制甲棵欠阅廊奶逗峡腊
- socket创建-内核中
- 谴猩碌抢仲亟朗凰凰布遗枚粤杉俑
- 谔仔盘侥栽郊尚导魏狼狼毖缚滋畔
- C++实现的贪食蛇游戏
- 咆副忻值菊慌菩苏猛腺坎偬郧了苏
- struct 求大小
- malloc()参数为0的情况
- 非仔用兔遗稼导氛枚宦柑肪澈柑炭
- 菲鞠亓本孜该亚硬独谙讨嘎既斩烧