原文出处:http://blog.chinaunix.net/uid-23392781-id-2426603.html
在(一)中分析了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)
下面简单分析一下sock_init
static int __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; }
/net/socket.c这个文件结构很清晰,也比较好分析,都说socket编程,多少也应该了解一下实现吧。
SYSCALL_DEFINE3(socket, int, family, int, type, int, protocol)
首先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)
SYSCALL_DEFINE3( socket , int , family, int , type, int , protocol) { int retval; struct socket * 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; }
至于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都干了什么。
/* * Create an inet socket. */ static int inet_create( struct net * net, struct socket * sock, int protocol) { struct sock * sk; struct inet_protosw * answer; struct inet_sock * inet; struct proto * answer_prot; unsigned char 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; }
转载于:https://blog.51cto.com/lvzg2005/1228091