Linux IPv4代码分析系列(2)
2013-06-24 08:48
501 查看
原文出处:http://blog.chinaunix.net/uid-23392781-id-2426603.html
在(一)中分析了inet_init的初始化部分,但在这之前还是有一部分的初始化需要说明一下,这个就是/net/socket.c/sock_init()。它的初始化在inet_init之前,详解对应函数的定义。
下面简单分析一下sock_init
/net/socket.c这个文件结构很清晰,也比较好分析,都说socket编程,多少也应该了解一下实现吧。SYSCALL_DEFINE3(socket, int, family, int, type, int, protocol)
首先SYSCALL_DEFINE3这个宏定义了socket系统调用的实现。而EXPORT_SYMBOL修饰的函数是供内核其他模块使用的。
SYSCALL_DEFINE3(socket, int, family, int, type, int, protocol)
至于sys_bind, sys_listen, sys_connect, sys_send, sys_recv等相关函数这里并没有具体实现,因为socket类型有多种,这里基于可扩展性考虑,根据socket传入参数来定位socket类型,进而调用对应的函数指针。sys_accept在这里的实现类似sys_socket。socket类型的注册和卸载见这两个函数,int sock_register(const struct net_proto_family *ops)
void sock_unregister(int family)
另外还有一些供kernel调用的函数见kernel_****,开头的函数。由于目前只考虑ipv4的实现,那么在net/ipv4/下,调用sock_register的地方是在(一)中分析的inet_init函数里调用的。那么我们在用户进程中调用socket函数时,kernel调用了/net/ipv4/af_inet.c/inet_create函数,看看inet_create都干了什么。
在(一)中分析了inet_init的初始化部分,但在这之前还是有一部分的初始化需要说明一下,这个就是/net/socket.c/sock_init()。它的初始化在inet_init之前,详解对应函数的定义。
core_initcall(sock_init);/* early initcall */ fs_initcall(inet_init); #define core_initcall(fn) __define_initcall("1",fn,1) #define fs_initcall(fn) __define_initcall("5",fn,5) |
staticint __init sock_init(void) { /* * Initialize sock SLAB cache. */ sk_init(); /* * Initialize skbuff SLAB cache */ skb_init();//这个函数的初始化比较有意思,因为它的内部申请了两个内存头节点。为了说明这个用处,给出一个链接,一句话还是不能说清楚的,内存管理的确比较复杂。 https://www.ibm.com/developerworks/cn/linux/l-linux-slab-allocator/ /* * Initialize the protocols module. */ init_inodecache();//同上 register_filesystem(&sock_fs_type); sock_mnt = kern_mount(&sock_fs_type);//这里添加了socket文件系统的支持,可以cat /proc/filesystems查看当前支持多少种文件系统,其中的”nodev sockfs”就是要注册的文件系统。注册socket文件系统的好处就是可以像操作文件一样进行发送接收数据。看看这个文件: srw-rw---- 1 root admin 0 Apr 20 03:46 networkd_uds /* The real protocol initialization is performed in later initcalls. */ //下面这个功能就是大名鼎鼎的netfilter了,虽然是可选的组件,但现在的内核默认都是编译到内核中了,见arch/xx/xx_defconfig文件 CONFIG_NETFILTER=y Netfilter功能强大, 支持L2,L3,L4层的包过滤,数据包修改,NAT,转发策略,拦截策略。另外可以自己写module实现很多其他的功能,比如xt_limit module能够实现简单的流量控制等功能。好东西啊。 #ifdef CONFIG_NETFILTER netfilter_init(); #endif return 0; } |
首先SYSCALL_DEFINE3这个宏定义了socket系统调用的实现。而EXPORT_SYMBOL修饰的函数是供内核其他模块使用的。
SYSCALL_DEFINE3通过跟踪不难发现最终定义了sys_socket系统调用,并加入系统调用数组sys_call_table中,linux的宏定义还是比较烦的,估计玩过连连看的兄弟应该能很快的找到定义:) a)#define SYSCALL_DEFINE3(name,...) SYSCALL_DEFINEx(3, _##name,__VA_ARGS__) b)#define SYSCALL_DEFINEx(x, sname,...) \ __SYSCALL_DEFINEx(x, sname,__VA_ARGS__) c)asmlinkage long sys##name(__SC_DECL##x(__VA_ARGS__)) 再看include/asm-generic/unistd.h #define __NR_socket 198 __SYSCALL(__NR_socket, sys_socket) |
SYSCALL_DEFINE3(socket,int, family,int, type,int, protocol) { int retval; structsocket*sock; int flags; /* Check the SOCK_* constants for consistency. */ BUILD_BUG_ON(SOCK_CLOEXEC != O_CLOEXEC); BUILD_BUG_ON((SOCK_MAX | SOCK_TYPE_MASK)!= SOCK_TYPE_MASK); BUILD_BUG_ON(SOCK_CLOEXEC & SOCK_TYPE_MASK); BUILD_BUG_ON(SOCK_NONBLOCK & SOCK_TYPE_MASK); flags = type &~SOCK_TYPE_MASK; if(flags &~(SOCK_CLOEXEC | SOCK_NONBLOCK)) return-EINVAL; type &= SOCK_TYPE_MASK; if(SOCK_NONBLOCK != O_NONBLOCK &&(flags & SOCK_NONBLOCK)) flags =(flags &~SOCK_NONBLOCK)| O_NONBLOCK; //sock_create->sock_alloc->new_inode 通过调用关系可以看出,这个函数的本质是申请一个inode节点;同时还需要注意的是,在__sock_create函数中调用了函数指针int (*create)(struct net *net, struct socket *sock, int protocol),用于各类型socket初始化及函数指针挂接。 retval = sock_create(family, type, protocol,&sock); if(retval < 0) goto out; retval = sock_map_fd(sock, flags &(O_CLOEXEC | O_NONBLOCK)); //在当前进程中找到一个空闲的fd,把fd和inode关联起来。 if(retval < 0) goto out_release; out: /* It may be already another descriptor 8) Not kernel problem. */ return retval; out_release: sock_release(sock); return retval; } |
void sock_unregister(int family)
另外还有一些供kernel调用的函数见kernel_****,开头的函数。由于目前只考虑ipv4的实现,那么在net/ipv4/下,调用sock_register的地方是在(一)中分析的inet_init函数里调用的。那么我们在用户进程中调用socket函数时,kernel调用了/net/ipv4/af_inet.c/inet_create函数,看看inet_create都干了什么。
/* * Create an inet socket. */ staticint inet_create(struct net *net,structsocket*sock,int protocol) { struct sock *sk; struct inet_protosw *answer; struct inet_sock *inet; struct proto *answer_prot; unsignedchar answer_flags; char answer_no_check; int try_loading_module = 0; int err; if(unlikely(!inet_ehash_secret)) if(sock->type !=SOCK_RAW&& sock->type !=SOCK_DGRAM) build_ehash_secret(); sock->state = SS_UNCONNECTED;//设置socket状态 /* Look for the requested type/protocol pair. */ lookup_protocol: err =-ESOCKTNOSUPPORT; rcu_read_lock(); //下面这部分片段定位到inet的socket子类型,TCP/UDP/IP list_for_each_entry_rcu(answer,&inetsw[sock->type],list){ err = 0; /* Check the non-wild match. */ if(protocol == answer->protocol){ if(protocol !=IPPROTO_IP) break; }else{ /* Check for the two wild cases. */ if(IPPROTO_IP== protocol){ protocol = answer->protocol; break; } if(IPPROTO_IP== answer->protocol) break; } err =-EPROTONOSUPPORT; } if(unlikely(err)){//这里面有一个自动加载的功能,如果功能模块在编译时指定为独立模块,kernel会按照下面的命名规则找到对应模块,并加载到内核当中。 if(try_loading_module < 2){ rcu_read_unlock(); /* * Be more specific, e.g. net-pf-2-proto-132-type-1 * (net-pf-PF_INET-proto-IPPROTO_SCTP-type-SOCK_STREAM) */ if(++try_loading_module == 1) request_module("net-pf-%d-proto-%d-type-%d", PF_INET, protocol, sock->type); /* * Fall back to generic, e.g. net-pf-2-proto-132 * (net-pf-PF_INET-proto-IPPROTO_SCTP) */ else request_module("net-pf-%d-proto-%d", PF_INET, protocol); goto lookup_protocol; }else goto out_rcu_unlock; } err =-EPERM; if(answer->capability > 0 &&!capable(answer->capability)) goto out_rcu_unlock; err =-EAFNOSUPPORT; if(!inet_netns_ok(net, protocol)) goto out_rcu_unlock; sock->ops = answer->ops; //这里挂接的函数指针。 answer_prot = answer->prot; answer_no_check = answer->no_check; answer_flags = answer->flags; rcu_read_unlock(); WARN_ON(answer_prot->slab ==NULL); err =-ENOBUFS; //为sock结构分配内存,并初始化里面的各字段。每个字段的含义没有必要都要搞清楚 sk = sk_alloc(net,PF_INET, GFP_KERNEL, answer_prot); if(sk ==NULL) goto out; err = 0; sk->sk_no_check = answer_no_check; if(INET_PROTOSW_REUSE & answer_flags) sk->sk_reuse = 1; inet = inet_sk(sk); inet->is_icsk =(INET_PROTOSW_ICSK & answer_flags)!= 0; if(SOCK_RAW== sock->type){ inet->num = protocol; if(IPPROTO_RAW== protocol) inet->hdrincl = 1; } if(ipv4_config.no_pmtu_disc) inet->pmtudisc = IP_PMTUDISC_DONT; else inet->pmtudisc = IP_PMTUDISC_WANT; inet->id = 0; sock_init_data(sock, sk); sk->sk_destruct = inet_sock_destruct; sk->sk_protocol = protocol; sk->sk_backlog_rcv = sk->sk_prot->backlog_rcv; inet->uc_ttl =-1; inet->mc_loop = 1; inet->mc_ttl = 1; inet->mc_all = 1; inet->mc_index = 0; inet->mc_list =NULL; sk_refcnt_debug_inc(sk); if(inet->num){ /* It assumes that any protocol which allows * the user to assign a number at socket * creation time automatically * shares. */ inet->sport =htons(inet->num); /* Add to protocol hash chains. */ sk->sk_prot->hash(sk); } if(sk->sk_prot->init){ err = sk->sk_prot->init(sk); if(err) sk_common_release(sk); } out: return err; out_rcu_unlock: rcu_read_unlock(); goto out; } |
相关文章推荐
- Linux IPv4代码分析系列(1)
- Linux IPv4代码分析系列(2)
- linux驱动由浅入深系列:usb子系统之四(android平台鼠标驱动代码分析)
- linux驱动由浅入深系列:高通sensor架构实例分析之二(驱动代码结构)
- 【多维分析系列】代码与未来
- Linux0.11内核--启动引导代码分析setup.s
- Linux ASoC音频驱动架构 及 Machine驱动代码分析
- Linux-0.01 引导代码分析-head.s
- Linux-0.11内核源码分析系列:进程调度sleep_on()函数分析
- Linux启动代码分析
- 嵌入式Linux内核移植相关代码分析
- linux设备模型--代码分析
- ARM-Linux代码的执行效率分析
- linux i2c子系统代码分析3 ---操作函数i2c_add_adapter i2c_add_numbered_adapter介绍
- 基于Linux的消息队列及多线程编程实现的聊天室(二)代码分析
- Linux下对NS2项目的仿真分析全过程代码(从tcl脚本-awk文本处理-gnuplot)
- Linux-kernel 网桥代码分析
- Linux网络层 ip_rcv()函数代码分析(__pskb_pull_tail)
- Linux平台代码覆盖率测试工具GCOV相关文件分析