Chinaunix首页 | 论坛 | 博客
  • 博客访问: 665629
  • 博文数量: 156
  • 博客积分: 4833
  • 博客等级: 上校
  • 技术积分: 1554
  • 用 户 组: 普通用户
  • 注册时间: 2007-05-21 19:36
文章分类

全部博文(156)

文章存档

2016年(2)

2013年(1)

2012年(13)

2011年(30)

2010年(46)

2009年(29)

2008年(23)

2007年(12)

分类: LINUX

2009-10-29 13:46:21

linux内核中socket的实现

 

首先来看整个与socket相关的操作提供了一个统一的接口sys_socketcall.

下面就是它的代码片段:

 

 

asmlinkage long sys_socketcall(int call, unsigned long __user *args) 

{ 

 unsigned long a[6]; 

 unsigned long a0, a1; 

 int err; 

.......................................... 

 

 a0 = a[0]; 

 a1 = a[1]; 

 

 switch (call) { 

 case SYS_SOCKET: 

 err = sys_socket(a0, a1, a[2]); 

 break; 

 case SYS_BIND: 

 err = sys_bind(a0, (struct sockaddr __user *)a1, a[2]); 

 break; 

 case SYS_CONNECT: 

 err = sys_connect(a0, (struct sockaddr __user *)a1, a[2]); 

 break; 

 case SYS_LISTEN: 

 err = sys_listen(a0, a1); 

 break; 

 case SYS_ACCEPT: 

 err = 

   do_accept(a0, (struct sockaddr __user *)a1, 

     (int __user *)a[2], 0); 

 break; 

 case SYS_GETSOCKNAME: 

 err = 

   sys_getsockname(a0, (struct sockaddr __user *)a1, 

    (int __user *)a[2]); 

 break; 

..................................... 

 return err; 

}

 

  可以看到代码比较简单,就是通过传递进来的call类型,来调用相应的socket相关的函数.

  这里你可能注意到了,那就是一般文件句柄相关的操作,比如write,read,aio,poll这些并没有看到(也就是 file_operations).这是因为socket上面其实还有一层vfs,内核把socket当做一个文件系统来处理,并实现了相应的vfs方法.因此下面我们先来了解下vfs.然后会描述下进程如何通过vfs存取句柄.

 

 vfs其实就相当于对下层的文件系统和上层应用之间的粘合层,它定义了文件系统需要实现的相关的操作,然后下层的文件系统只需要实现这些方法就可以了,也就是说在内核其他部分和上层应用看来,所有的文件系统没有任何区别.

下面的这张图就是从用户空间调用write的大体流程:

 

 

  vfs中有4种主要的数据结构:

  1 超级块对象,代表一个已安装的文件系统.super_block

  2 索引节点对象,代表一个文件.inode

  3 目录项对象,代表一个目录项.dentry

  4 文件对象,表示一个被进程打开的文件.file

  其中每种对象都包含一个操作对象.依次为super_operations,inode_operations,dentry_operations 以及file_operations.各自操作不同的层次.然后我们的文件系统只需要实现这些方法,然后注册到内核就可以了.

  接下来我们来看和vfs相应的结构:

  第一个就是file_system_type结构,这个结构表示了一个文件系统:

  

struct file_system_type { 

 const char *name; 

 int fs_flags; 

///最关键的函数,得到文件系统的超级块. 

 int (*get_sb) (struct file_system_type *, int, 

const char *, void *, struct vfsmount *); 

 void (*kill_sb) (struct super_block *); 

........................................... 

};

 

  然后是vfsmount结构,它表示了一个安装点,换句话说也就是一个文件系统实例.

 

  第三个是files_struct结构,它主要是为每个进程来维护它所打开的句柄.这里只需要注意一个就是fd_arrayfstable中的 fd的区别.当进程数比较少也就是小于NR_OPEN_DEFAULT(32),句柄就会存放在fd_array,而当句柄数超过32则就会重新分配数组,然后将fd指针指向它(然后我们通过fd就可以取得相应的file结构).

 

而且files_struct是每个进程只有一个的.

 

struct files_struct { 

 /* 

  * read mostly part 

  */ 

 atomic_t count; 

 struct fdtable *fdt; 

 struct fdtable fdtab; 

 /* 

  * written part on a separate cache line in SMP 

  */ 

 spinlock_t file_lock ____cacheline_aligned_in_smp; 

 int next_fd; 

 struct embedded_fd_set close_on_exec_init; 

 struct embedded_fd_set open_fds_init; 

///所打开的所有文件 

 struct file * fd_array[NR_OPEN_DEFAULT]; 

}; 

  

struct fdtable { 

 unsigned int max_fds; 

 struct file ** fd;   /* current fd array */ 

 fd_set *close_on_exec; 

 fd_set *open_fds; 

 struct rcu_head rcu; 

 struct fdtable *next; 

}; 

 

  还有两个一个是fs_struct,一个是namespace也都是进程相关的.这里就不一一介绍了.

 

  我这里vfs介绍只是个大概,需要详细了解的,可以去看ulkvfs相关章节和linux内核设计与实现的相关章节.

 

      

 

因此下面的图表示了进程和socket的关系:

 


 

  上面的这张图有些老了,新的内核中的inode节点中已经没有u这个联合体了,对应的是会有一个包含socketinode的一个结构体,然后我们通过inode,inode中专门有个i_mode域来判断相应的inode类型,比如socket就是S_IFSOCK.就可以直接计算出相应的 socket的地址,然后就可以存取socket.后面我们会介绍.

  内核中标售socket有两个数据结构,一个是socket,另一个是sock,其中socket是一个general BSD socket,它也就是应用程序和4层协议之间的一个接口,屏蔽掉了相关的4层协议部分.而在内核中,socket所需要使用的相关的4层协议的信息全部是保存在sock结构当中的,socketsock这两个结构都有保存对方的指针,因此可以很容易的存取对方.

  还有一个就是ops,这个域保存了所有的相关的4层协议的操作函数..

  而在sock中有一个sk_common保存了一个skc_prot,这个域保存的是相应的协议簇的操作函数的**.

  后面介绍到socket创建的时候,我们会分析proto_opsproto的区别.其实proto相当于对proto_ops的一层封装,最终会在proto中调用proto_ops.

    

/** 

 * struct socket - general BSD socket 

 * @state: socket state (%SS_CONNECTED, etc) 

 * @type: socket type (%SOCK_STREAM, etc) 

 * @flags: socket flags (%SOCK_ASYNC_NOSPACE, etc) 

 * @ops: protocol specific socket operations 

 * @fasync_list: Asynchronous wake up list 

 * @file: File back pointer for gc 

 * @sk: internal networking protocol agnostic socket representation 

 * @wait: wait queue for several uses 

 */ 

struct socket { 

 socket_state state; 

 short  type; 

 unsigned long flags; 

 const struct proto_ops *ops; 

 struct fasync_struct *fasync_list; 

 struct file *file; 

 struct sock *sk; 

 wait_queue_head_t wait; 

}; 

 

struct sock_common { 

 unsigned short skc_family; 

 volatile unsigned char skc_state; 

 unsigned char skc_reuse; 

 int  skc_bound_dev_if; 

 struct hlist_node skc_node; 

 struct hlist_node skc_bind_node; 

 atomic_t skc_refcnt; 

 unsigned int skc_hash; 

 struct proto *skc_prot; 

#ifdef CONFIG_NET_NS 

 struct net  *skc_net; 

#endif 

}; 

 

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, int flags); 

................................................... 

}; 

 

 

      

然后我们来看sock_init的实现,在这个函数中,socket注册为一个伪文件系统,并安装相应的mount:

 

///相应的mount对象 

static struct vfsmount *sock_mnt __read_mostly; 

///文件系统对象. 

static struct file_system_type sock_fs_type = { 

 .name = "sockfs", 

 .get_sb = sockfs_get_sb, 

 .kill_sb = kill_anon_super, 

}; 

 

static int __init sock_init(void) 

{ 

 /* 

 *   Initialize sock SLAB cache. 

 */ 

 

 sk_init(); 

 

 /* 

 *   Initialize skbuff SLAB cache 

 */ 

 skb_init(); 

 

///初始化一个inodecache. 

 init_inodecache(); 

///注册文件系统到内核. 

 register_filesystem(&sock_fs_type); 

///安装mount. 

 sock_mnt = kern_mount(&sock_fs_type); 

 

#ifdef CONFIG_NETFILTER 

 netfilter_init(); 

#endif 

 return 0; 

}

 

  我们知道每次创建一个socket,都是要依赖于当前的protocol family类型的(后面会分析sys_socket的源码的时候会看到).而在内核中,每种类型的protocol family都会有一个相对应的net_proto_family结构,然后将这个结构注册到内核的net_families数组中,这样我们创建 socket的时候,就可以调用这个数组来创建socket.

 

  我们先来看sock_register的源码,也就是如何将一个net_proto_family注册到相应的数组:

 

 

static const struct net_proto_family *net_families[NPROTO] __read_mostly; 

 

int sock_register(const struct net_proto_family *ops) 

{ 

 int err; 

 

 if (ops->family >= NPROTO) { 

 printk(KERN_CRIT "protocol %d >= NPROTO(%d)\n", ops->family, 

     NPROTO); 

 return -ENOBUFS; 

 } 

 

 spin_lock(&net_family_lock); 

///代码非常简单,就是根据类型,然后放到相应的位置. 

 if (net_families[ops->family]) 

 err = -EEXIST; 

 else { 

 net_families[ops->family] = ops; 

 err = 0; 

 } 

 spin_unlock(&net_family_lock); 

 

 printk(KERN_INFO "NET: Registered protocol family %d\n", ops->family); 

 return err; 

}

 

  我们知道每个协议簇和相应的套接口都对应有好多种组合,因此在协议簇的实现中保存了一个相应的结构来保存这些组合,然后后面就首先通过family然后确定到某个结构,再根据套接口的类型来得到这个结构,并赋值给sock.

 

  这里要注意我们只分析af_inet的实现,其他的协议簇都差不多:

  我们来看这个的实现:

 

///可以看到这是一个数组,每个元素都是一个链表,也就是每种类型的socket就是一个链表.而这个链表所包含的是不同4层协议的inetsw.可是在inet,现在每种类型的socket只对应一个4层协议.这里只是为了以后扩展. 

static struct list_head inetsw[SOCK_MAX]; 

 

///相应的socket的对应的信息的结构. 

struct inet_protosw { 

 struct list_head list; 

 

///需要这两个key才能定位一个inet_protosw. 

 unsigned short type;  /* This is the 2nd argument to socket(2). */ 

 unsigned short protocol; /* This is the L4 protocol number. */ 

 

///相应的基于ipv44层协议的操作**. 

 struct proto *prot; 

///相应的协议簇的操作信息. 

 const struct proto_ops *ops; 

  

 int       capability; /* Which (if any) capability do 

     * we need to use this socket 

     * interface? 

                   */ 

 char       no_check;  /* checksum on rcv/xmit/none? */ 

 unsigned char flags;   /* See INET_PROTOSW_* below. */ 

}; 

 

void inet_register_protosw(struct inet_protosw *p) 

{ 

 struct list_head *lh; 

 struct inet_protosw *answer; 

 int protocol = p->protocol; 

 struct list_head *last_perm; 

............................................. 

 answer = NULL; 

 last_perm = &inetsw[p->type]; 

///这个操作也很简单,就是将inet_protosw根据套接口类型插入到全局链表数组. 

 list_for_each(lh, &inetsw[p->type]) { 

 answer = list_entry(lh, struct inet_protosw, list); 

 

 /* Check only the non-wild match. */ 

 if (INET_PROTOSW_PERMANENT & answer->flags) { 

  if (protocol == answer->protocol) 

  break; 

  last_perm = lh; 

 } 

 

 answer = NULL; 

 } 

 if (answer) 

 goto out_permanent; 

///插入链表. 

 list_add_rcu(&p->list, last_perm); 

.............................. 

 

接下来来分析inet_init的源码.

 

///表示了所有的可能的当前协议簇和套接口类型的组合. 

static struct inet_protosw inetsw_array[] = 

{ 

 { 

 .type =    SOCK_STREAM, 

 .protocol =  IPPROTO_TCP, 

 .prot =    &tcp_prot, 

 .ops =    &inet_stream_ops, 

 .capability = -1, 

 .no_check =  0, 

 .flags =   INET_PROTOSW_PERMANENT | 

     INET_PROTOSW_ICSK, 

 }, 

 

 { 

 .type =    SOCK_DGRAM, 

 .protocol =  IPPROTO_UDP, 

 .prot =    &udp_prot, 

 .ops =    &inet_dgram_ops, 

 .capability = -1, 

 .no_check =  UDP_CSUM_DEFAULT, 

 .flags =   INET_PROTOSW_PERMANENT, 

 }, 

 

 

 { 

    .type =    SOCK_RAW, 

    .protocol =  IPPROTO_IP, /* wild card */ 

    .prot =    &raw_prot, 

    .ops =    &inet_sockraw_ops, 

    .capability = CAP_NET_RAW, 

    .no_check =  UDP_CSUM_DEFAULT, 

    .flags =   INET_PROTOSW_REUSE, 

 } 

}; 

 

///协议簇的创建函数. 

static struct net_proto_family inet_family_ops = { 

 .family = PF_INET, 

 .create = inet_create, 

 .owner = THIS_MODULE, 

}; 

 

static int __init inet_init(void) 

{ 

............................................. 

 

///注册相应的proto到全局链表中. 

 rc = proto_register(&tcp_prot, 1); 

 if (rc) 

 goto out; 

 

 rc = proto_register(&udp_prot, 1); 

 if (rc) 

 goto out_unregister_tcp_proto; 

 

 rc = proto_register(&raw_prot, 1); 

 if (rc) 

 goto out_unregister_udp_proto; 

 

///注册协议簇的操作函数(后面socket创建的时候会用到). 

 (void)sock_register(&inet_family_ops); 

 

............................................. 

 /* Register the socket-side information for inet_create. */ 

 for (r = &inetsw[0]; r < &inetsw[SOCK_MAX]; ++r) 

 INIT_LIST_HEAD(r); 

 

///inetsw_array插入到相应的数组链表. 

 for (q = inetsw_array; q < &inetsw_array[INETSW_ARRAY_LEN]; ++q) 

 inet_register_protosw(q); 

........................................... 

 

}

 

      

接下来我们来通过分析创建socket的函数sys_socket,来更加好的理解socket的实现.

 

asmlinkage long sys_socket(int family, int type, 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)); 

.................................... 

}

 

  sock_create的具体流程我们就不分析了,我们只需要知道最终他会通过传递进来的family的值,来取得相应的family中注册的 creat函数.然后会调用这个函数来完成socket的创建.而在上面的代码分析中,我们知道在af_inet,注册的create函数是 inet_create函数,因此我们来看这个函数的实现:

 

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; 

........................................................... 

///首先给socket状态赋值. 

 

 sock->state = SS_UNCONNECTED; 

 

 /* Look for the requested type/protocol pair. */ 

lookup_protocol: 

 err = -ESOCKTNOSUPPORT; 

 rcu_read_lock(); 

///通过typeprotocl的值,来查找到相应的inet_protosw结构. 

 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; 

 } 

 

 .......................................... 

 

///开始给socket赋值.这里我们可以看到最终socketops域所得到的值就是相应的协议簇的操作**(比如inet_stream_ops这些).. 

 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; 

///alloc一个sock结构,其中将刚才取得的inet_protosw中的pro 域赋值给socksk_prot域和sk_prot_creator.以及family域也被相应的赋值. 

 sk = sk_alloc(net, PF_INET, GFP_KERNEL, answer_prot); 

 if (sk == NULL) 

 goto out; 

.................................................................... 

 

///这个函数中会初始化相应的socket中的写队列,读队列以及错误队列.并将sk指针和sock连接起来.而且还将初始化相应的定时器. 

 

 sock_init_data(sock, sk); 

.......................................... 

///调用相应的初始化. 

 

 if (sk->sk_prot->init) { 

///其实也就是相对应4层协议的初始化函数,它会初始化一些协议相关的东西. 

 err = sk->sk_prot->init(sk); 

 if (err) 

  sk_common_release(sk); 

 } 

out: 

 return err; 

out_rcu_unlock: 

 rcu_read_unlock(); 

 goto out; 

}

 

 

  这里举个例子,来看一下tcp_v4_init_sock的实现,也就是tcp的初始化函数.

 

static int tcp_v4_init_sock(struct sock *sk) 

{ 

 struct inet_connection_sock *icsk = inet_csk(sk); 

 struct tcp_sock *tp = tcp_sk(sk); 

 

 skb_queue_head_init(&tp->out_of_order_queue); 

///初始化定时器,也就是tcp的那3个定时器,write,delay以及keepalive定时器. 

 tcp_init_xmit_timers(sk); 

 tcp_prequeue_init(tp); 

................................... 

///状态赋值.初始状态. 

 sk->sk_state = TCP_CLOSE; 

 .................................. 

 

 return 0; 

}

 

  上面我们看到有两个新的结构inet_connection_sock以及tcp_sock.我们接下来就来看这两个结构.

 

  inet_connection_sock也就是所有面向连接的协议的socket的相关信息.它的第一个域是inet_sock,因此我们可以很方便的进行转换.tcp_sock 相当与inet_connection_sock得一个子类,保存有所有tcp相关的socket的信息.它的第一个域就是 inet_connection_sock.

  可以看到其实tcp_socket类似于inet_sock(前面的blog有介绍),都是保存了本层的相关的信息.

  这里就不列出这两个结构了,内核中这两个结构的注释都是很详细的..

  在看sock_map_fd实现之前,我们先来看内核中socket类型的inode节点的实现:

  这里看到,我们只要拥有了inode节点,通过containof宏我们就可以计算出socket的地址,从而就可以得到整个socket的信息了.

 

struct socket_alloc { 

 struct socket socket; 

 struct inode vfs_inode; 

};

 

      

 

  而inode节点的赋值是在sock_alloc中实现的,而这个函数是在__sock_create中被调用的,也就是在init_cteate被调用之前.

 

static struct socket *sock_alloc(void) 

{ 

 struct inode *inode; 

 struct socket *sock; 

 

///新建一个inode,sock_mnt就是sock_init中被安装的mount. 

 inode = new_inode(sock_mnt->mnt_sb); 

 if (!inode) 

 return NULL; 

///然后组合inodesocket结构. 

 sock = SOCKET_I(inode); 

///设置inode类型. 

 inode->i_mode = S_IFSOCK | S_IRWXUGO; 

 inode->i_uid = current->fsuid; 

 inode->i_gid = current->fsgid; 

///sockets_in_use(也就是当前创建的socket)加一. 

 get_cpu_var(sockets_in_use)++; 

 put_cpu_var(sockets_in_use); 

 return sock; 

}

 

  然后我们来看sock_map_fd的实现.我们首先要知道,socket是没有open函数的,因此要通过vfs层的调用,必须要在create的时候,映射一个file结构,从而将句柄与这个file关联起来.

 

int sock_map_fd(struct socket *sock, int flags) 

{ 

 struct file *newfile; 

///找到一个可用的fd,并找到一个可用的file结构并返回. 

 int fd = sock_alloc_fd(&newfile, flags); 

 

 if (likely(fd >= 0)) { 

///初始化这个file结构. 

 int err = sock_attach_fd(sock, newfile, flags); 

 

 if (unlikely(err < 0)) { 

  put_filp(newfile); 

  put_unused_fd(fd); 

  return err; 

 } 

///将句柄和文件指针关联起来. 

 fd_install(fd, newfile); 

 } 

 return fd; 

} 

 

 

sock_alloc_fd实现比较简单,这里就不分析了.

就来看下sock_attach_fd的实现.:

 

  这里要注意,内核通过把socket指针赋值给fileprivate_data,这样就可以通过句柄,fdtable中得到file对象,然后轻松取得socket对象.

 

///目录项的操作** 

static struct dentry_operations sockfs_dentry_operations = { 

 .d_delete = sockfs_delete_dentry, 

 .d_dname = sockfs_dname, 

}; 

///文件的操作**.这些函数最终调用的还是socketops域中的函数.而我们上面已经提过最终他们调用sock域的proto中的函数. 

static const struct file_operations socket_file_ops = { 

 .owner = THIS_MODULE, 

 .llseek = no_llseek, 

 .aio_read = sock_aio_read, 

 .aio_write = sock_aio_write, 

 .poll = sock_poll, 

 .unlocked_ioctl = sock_ioctl, 

#ifdef CONFIG_COMPAT 

 .compat_ioctl = compat_sock_ioctl, 

#endif 

 .mmap = sock_mmap, 

 .open = sock_no_open, /* special open code to disallow open via /proc */ 

 .release = sock_close, 

 .fasync = sock_fasync, 

 .sendpage = sock_sendpage, 

 .splice_write = generic_splice_sendpage, 

 .splice_read = sock_splice_read, 

}; 

 

 

static int sock_attach_fd(struct socket *sock, struct file *file, int flags) 

{ 

 struct dentry *dentry; 

 struct qstr name = { .name = "" }; 

///根据装载点的mnt_sb(super block)root域来创建一个目录项. 

 dentry = d_alloc(sock_mnt->mnt_sb->s_root, &name); 

 if (unlikely(!dentry)) 

 return -ENOMEM; 

///sockfs的目录项操作**赋值. 

 dentry->d_op = &sockfs_dentry_operations; 

 /* 

 * We dont want to push this dentry into global dentry hash table. 

 * We pretend dentry is already hashed, by unsetting DCACHE_UNHASHED 

 * This permits a working /proc/$pid/fd/XXX on sockets 

 */ 

 dentry->d_flags &= ~DCACHE_UNHASHED; 

///inode和目录项关联起来. 

 d_instantiate(dentry, SOCK_INODE(sock)); 

 

 sock->file = file; 

///初始化文件对象,主要就是将socket_file_ops赋值给file结构的f_op. 

 init_file(file, sock_mnt, dentry, FMODE_READ | FMODE_WRITE, 

  &socket_file_ops); 

 SOCK_INODE(sock)->i_fop = &socket_file_ops; 

 file->f_flags = O_RDWR | (flags & O_NONBLOCK); 

 file->f_pos = 0; 

///sock赋值给private_data,这样我们就能通过file轻松获得socket结构(在后面会用到). 

 file->private_data = sock; 

 

 return 0; 

} 

 

 

下面就是sys_socket的流程图:

 


 

  最终来总结一下.内核中,socket是作为一个伪文件系统来实现的,它在初始化时注册到内核,而每个进程的files_struct域保存了所有的句柄,包括socket.一般的文件操作的话,内核直接调用vfs层的方法,然后会自动调用socket实现的相关方法.内核通过inode结构的 imode域就可以知道当前的句柄所关联的是不是socket类型,这时遇到socket独有的操作,就通过containof方法,来计算出 socket的地址,从而就可以进行相关的操作.

  最后我们要注意的是,内核在调用相关操作都是直接调用socketops,然后在ops域中调用相应的sock结构体中的sock_common域的skc_prot的操作集中的相对应的函数.

  举个例子,假设现在我们使用tcp协议然后调用bind方法,内核会先调用sys_bind方法:

 

asmlinkage long sys_bind(int fd, struct sockaddr __user *umyaddr, int addrlen) 

{ 

................................................ 

  if (!err) 

  err = sock->ops->bind(sock, 

      (struct sockaddr *) 

      &address, addrlen); 

................................................... 

}

  可以看到它调用的是ops域的bind方法.而这时我们的ops域是inet_stream_ops,来看它的bind方法:

    

int inet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) 

{ 

.............................................. 

 

 /* If the socket has its own bind function then use it. (RAW) */ 

 if (sk->sk_prot->bind) { 

 err = sk->sk_prot->bind(sk, uaddr, addr_len); 

 goto out; 

 } 

................................................ 

} 

  它最终调用的是sock结构的sk_prot(也就是sock_commonskc_prot)bind方法,而此时我们的skc_prot的值是tcp_prot,因此最终会调用tcp_protbind方法.

  下面就是示意图:



文件:SOCKET.zip
大小:305KB
下载:下载

 


 

  

阅读(948) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~