下面通过学习linux 1.2.13源码进一步理解socket通信机制。对该版本源码的学习主要参考《Linux内核网络栈源代码情景分析》(曹桂平 编著)。

  要理解socket的本质,就要理解当调用socket函数时,该函数到底创建了什么?返回了什么?

  1. int socket(int family, int type, int protocol);

  socket 函数为用户层函数,该函数对应的内核函数为sock_socket(socket.c文件),源码如下:

  1. static int sock_socket(int family, int type, int protocol)
  2. {
  3. int i, fd;
  4. struct socket *sock;
  5. struct proto_ops *ops;
  6.  
  7. /* Locate the correct protocol family. */
  8. for (i = ; i < NPROTO; ++i)
  9. {
  10. if (pops[i] == NULL) continue;
  11. if (pops[i]->family == family)
  12. break;
  13. }
  14.  
  15. if (i == NPROTO)
  16. {
  17. return -EINVAL;
  18. }
  19.  
  20. ops = pops[i];
  21.  
  22. /*
  23. * Check that this is a type that we know how to manipulate and
  24. * the protocol makes sense here. The family can still reject the
  25. * protocol later.
  26. */
  27.  
  28. if ((type != SOCK_STREAM && type != SOCK_DGRAM &&
  29. type != SOCK_SEQPACKET && type != SOCK_RAW &&
  30. type != SOCK_PACKET) || protocol < )
  31. return(-EINVAL);
  32.  
  33. /*
  34. * Allocate the socket and allow the family to set things up. if
  35. * the protocol is 0, the family is instructed to select an appropriate
  36. * default.
  37. */
  38.  
  39. if (!(sock = sock_alloc()))
  40. {
  41. printk("NET: sock_socket: no more sockets\n");
  42. return(-ENOSR); /* Was: EAGAIN, but we are out of
  43. system resources! */
  44. }
  45.  
  46. sock->type = type;
  47. sock->ops = ops;
  48. if ((i = sock->ops->create(sock, protocol)) < )
  49. {
  50. sock_release(sock);
  51. return(i);
  52. }
  53.  
  54. if ((fd = get_fd(SOCK_INODE(sock))) < )
  55. {
  56. sock_release(sock);
  57. return(-EINVAL);
  58. }
  59.  
  60. return(fd);
  61. }

   sock_socket 函数完成如下工作:

(1)分配socket、sock结构,这两个结构在网络栈的不同层次表示一个套接字连接。

(2)分配inode、file结构用于普通文件操作。

(3)分配一个文件描述符并返回给应用程序作为以后的操作句柄。

  sock_alloc 函数用于分配一个inode节点,并返回该节点的socket指针

  1. struct socket *sock_alloc(void)
  2. {
  3. struct inode * inode;
  4. struct socket * sock;
  5.  
  6. inode = get_empty_inode();
  7. if (!inode)
  8. return NULL;
  9.  
  10. inode->i_mode = S_IFSOCK;
  11. inode->i_sock = ;
  12. inode->i_uid = current->uid;
  13. inode->i_gid = current->gid;
  14.  
  15. sock = &inode->u.socket_i;
  16. sock->state = SS_UNCONNECTED;
  17. sock->flags = ;
  18. sock->ops = NULL;
  19. sock->data = NULL;
  20. sock->conn = NULL;
  21. sock->iconn = NULL;
  22. sock->next = NULL;
  23. sock->wait = &inode->i_wait;
  24. sock->inode = inode; /* "backlink": we could use pointer arithmetic instead */
  25. sock->fasync_list = NULL;
  26. sockets_in_use++;
  27. return sock;
  28. }

inode的定义如下

  1. /* include/fs.h */
  2. struct inode {
  3. dev_t i_dev;
  4. unsigned long i_ino;
  5. umode_t i_mode;
  6. nlink_t i_nlink;
  7. uid_t i_uid;
  8. gid_t i_gid;
  9. dev_t i_rdev;
  10. off_t i_size;
  11. time_t i_atime;
  12. time_t i_mtime;
  13. time_t i_ctime;
  14. unsigned long i_blksize;
  15. unsigned long i_blocks;
  16. unsigned long i_version;
  17. struct semaphore i_sem;
  18. struct inode_operations * i_op;
  19. struct super_block * i_sb;
  20. struct wait_queue * i_wait;
  21. struct file_lock * i_flock;
  22. struct vm_area_struct * i_mmap;
  23. struct inode * i_next, * i_prev;
  24. struct inode * i_hash_next, * i_hash_prev;
  25. struct inode * i_bound_to, * i_bound_by;
  26. struct inode * i_mount;
  27. unsigned short i_count;
  28. unsigned short i_wcount;
  29. unsigned short i_flags;
  30. unsigned char i_lock;
  31. unsigned char i_dirt;
  32. unsigned char i_pipe;
  33. unsigned char i_sock;
  34. unsigned char i_seek;
  35. unsigned char i_update;
  36. union {
  37. struct pipe_inode_info pipe_i;
  38. struct minix_inode_info minix_i;
  39. struct ext_inode_info ext_i;
  40. struct ext2_inode_info ext2_i;
  41. struct hpfs_inode_info hpfs_i;
  42. struct msdos_inode_info msdos_i;
  43. struct umsdos_inode_info umsdos_i;
  44. struct iso_inode_info isofs_i;
  45. struct nfs_inode_info nfs_i;
  46. struct xiafs_inode_info xiafs_i;
  47. struct sysv_inode_info sysv_i;
  48. struct socket socket_i;
  49. void * generic_ip;
  50. } u;
  51. };

  inode 结构是文件系统的一个结构体,该结构体中的成员变量u指明了该inode结构具体的文件类型,当inode是用于socket通信时,u的值就为socket_i。sock_alloc 的作用就是创建inode结构体,然后返回socket_i的地址。至于具体如何分配inode涉及到文件系统方面的知识,这里暂不讨论。

  当协议族为AF_INET时,ops->create 将调用inet_create(struct socket*sock, int protocol)函数。该函数将创建一个sock结构体并使得socket的data指针指向该sock结构体。

  1. static int inet_create(struct socket *sock, int protocol)
  2. {
  3. struct sock *sk;
  4. struct proto *prot;
  5. int err;
  6.  
  7. sk = (struct sock *) kmalloc(sizeof(*sk), GFP_KERNEL);
  8. if (sk == NULL)
  9. return(-ENOBUFS);
  10. sk->num = ;
  11. sk->reuse = ;
  12. switch(sock->type)
  13. {
  14. case SOCK_STREAM:
  15. case SOCK_SEQPACKET:
  16. if (protocol && protocol != IPPROTO_TCP)
  17. {
  18. kfree_s((void *)sk, sizeof(*sk));
  19. return(-EPROTONOSUPPORT);
  20. }
  21. protocol = IPPROTO_TCP;
  22. sk->no_check = TCP_NO_CHECK;
  23. prot = &tcp_prot;
  24. break;
  25.  
  26. case SOCK_DGRAM:
  27. if (protocol && protocol != IPPROTO_UDP)
  28. {
  29. kfree_s((void *)sk, sizeof(*sk));
  30. return(-EPROTONOSUPPORT);
  31. }
  32. protocol = IPPROTO_UDP;
  33. sk->no_check = UDP_NO_CHECK;
  34. prot=&udp_prot;
  35. break;
  36.  
  37. case SOCK_RAW:
  38. if (!suser())
  39. {
  40. kfree_s((void *)sk, sizeof(*sk));
  41. return(-EPERM);
  42. }
  43. if (!protocol)
  44. {
  45. kfree_s((void *)sk, sizeof(*sk));
  46. return(-EPROTONOSUPPORT);
  47. }
  48. prot = &raw_prot;
  49. sk->reuse = ;
  50. sk->no_check = ; /*
  51. * Doesn't matter no checksum is
  52. * performed anyway.
  53. */
  54. sk->num = protocol;
  55. break;
  56.  
  57. case SOCK_PACKET:
  58. if (!suser())
  59. {
  60. kfree_s((void *)sk, sizeof(*sk));
  61. return(-EPERM);
  62. }
  63. if (!protocol)
  64. {
  65. kfree_s((void *)sk, sizeof(*sk));
  66. return(-EPROTONOSUPPORT);
  67. }
  68. prot = &packet_prot;
  69. sk->reuse = ;
  70. sk->no_check = ; /* Doesn't matter no checksum is
  71. * performed anyway.
  72. */
  73. sk->num = protocol;
  74. break;
  75.  
  76. default:
  77. kfree_s((void *)sk, sizeof(*sk));
  78. return(-ESOCKTNOSUPPORT);
  79. }
  80. sk->socket = sock;
  81. #ifdef CONFIG_TCP_NAGLE_OFF
  82. sk->nonagle = ;
  83. #else
  84. sk->nonagle = ;
  85. #endif
  86. sk->type = sock->type;
  87. sk->stamp.tv_sec=;
  88. sk->protocol = protocol;
  89. ......
  90. sk->timer.function = &net_timer;
  91. skb_queue_head_init(&sk->back_log);
  92. sk->blog = ;
  93. sock->data =(void *) sk; //socket 指向 sock
  94. sk->dummy_th.doff = sizeof(sk->dummy_th)/;
  95. ......
  96. if (sk->prot->init)
  97. {
  98. err = sk->prot->init(sk);
  99. if (err != )
  100. {
  101. destroy_sock(sk);
  102. return(err);
  103. }
  104. }
  105. return();
  106. }

  最后调用get_fd 返回一个文件描述符给上层应用。

  1. /* socket.c */
  2. static int get_fd(struct inode *inode)
  3. {
  4. int fd;
  5. struct file *file;
  6.  
  7. /*
  8. * Find a file descriptor suitable for return to the user.
  9. */
  10.  
  11. file = get_empty_filp(); // 获取一个闲置的file结构
  12. if (!file)
  13. return(-);
  14.  
  15. for (fd = ; fd < NR_OPEN; ++fd)
  16. if (!current->files->fd[fd])
  17. break;
  18. if (fd == NR_OPEN)
  19. {
  20. file->f_count = ;
  21. return(-);
  22. }
  23.  
  24. FD_CLR(fd, &current->files->close_on_exec);
  25. current->files->fd[fd] = file;
  26. file->f_op = &socket_file_ops; // socket 文件操作
  27. file->f_mode = ;
  28. file->f_flags = O_RDWR;
  29. file->f_count = ;
  30. file->f_inode = inode;
  31. if (inode)
  32. inode->i_count++;
  33. file->f_pos = ;
  34. return(fd);
  35. }

  get_fd 用于为网络套接字分配一个文件描述符,分配描述符的同时需要一个file结构,每个file结构都需要一个inode结构对应。内核维护一个file结构数据,get_empty_filp 函数即通过检查该数组,获取一个闲置的成员。f_op 字段的赋值实现了网络操作的普通文件接口。如果调用write、read函数进行操作就会调用相应的sock_read 和 sock_write 函数。

  如何根据文件描述如fd找到相应的sock?

网络协议栈学习(二)创建 socket的更多相关文章

  1. 网络协议栈学习(一)socket通信实例

    网络协议栈学习(一)socket通信实例 该实例摘自<linux网络编程>(宋敬彬,孙海滨等著). 例子分为服务器端和客户端,客户端连接服务器后从标准输入读取输入的字符串,发送给服务器:服 ...

  2. 网络编程学习笔记-浅析socket

    一.问题的引入——socket的引入是为了解决不同计算机间进程间通信的问题 .socket与进程的关系 ).socket与进程间的关系:socket 用来让一个进程和其他的进程互通信息(IPC),而S ...

  3. 网络编程学习笔记:Socket编程

    文的主要内容如下: 1.网络中进程之间如何通信? 2.Socket是什么? 3.socket的基本操作 3.1.socket()函数 3.2.bind()函数 3.3.listen().connect ...

  4. 网络编程学习笔记--1.socket可读可写条件

    转至 :http://blog.csdn.net/majianfei1023/article/details/45788591 socket可读可写条件,经常做为面试题被问,因为它考察被面试者对网络编 ...

  5. Linux网络编程学习(二) ----- 进程控制(第三章)

    1.进程和程序 程序是一个可执行文件,而一个进程是一个执行中的程序实例.一个进程对应于一个程序的执行,进程是动态的,程序是静态的,多个进程可以并发执行同一个程序.比如几个用户可以同时运行一个编辑程序, ...

  6. 网络编程学习二(IP与端口)

    InetAddress类 封装计算机的ip地址,没有端口 // 使用getLocalHost方法创建InetAddress对象 InetAddress addr = InetAddress.getLo ...

  7. pipelinewise 学习二 创建一个简单的pipeline

    pipelinewise 提供了方便的创建简单pipeline的命令,可以简化pipeline 的创建,同时也可以帮我们学习 生成demo pipeline pipelinewise init --n ...

  8. sublime text 2学习(二):创建可复用的代码片段

    对于前端工程师来讲,写一个html页面的基本结构是体力活,每次去拷贝一个也麻烦,sublime text 2 提供了一个很好的复用代码片段.下面介绍一下创建一个html5的代码片段的过程. 在菜单上点 ...

  9. micronaut 学习 二 创建一个简单的服务

    micronaut 提供的cli 很方便,我们可以快速创建具有所需特性的应用,以下是一个简单的web server app 创建命令 mn create-app hello-world 效果 mn c ...

随机推荐

  1. 移动距离|2015年蓝桥杯B组题解析第八题-fishers

    移动距离 X星球居民小区的楼房全是一样的,并且按矩阵样式排列.其楼房的编号为1,2,3... 当排满一行时,从下一行相邻的楼往反方向排号. 比如:当小区排号宽度为6时,开始情形如下: 1 2 3 4 ...

  2. Sql 最简单的Sqlserver连接

    string name = txtUserName.Text.Trim();//移除用户名前部和后部的空格 string pwd = txtUserPwd.Text.Trim();//移除密码前部和后 ...

  3. 【Android实验】UI设计-Android计算器

    目录 实验目的 实验要求 实验过程 1. 界面设计 2. 功能设计 3. 运算处理 实验目的 自主完成一个简单APP的设计工作,综合应用已经学到的Android UI设计技巧,重点注意合理使用布局 实 ...

  4. Network Simulator for P4(NSP4) src内容介绍

    Structure What's NSP4? src source code introduction What's NSP4? NSP4是一个用于P4的网络仿真工具,旨在简化P4的环境部署和运行,将 ...

  5. FAST:通过Floodlight控制器下发流表

    参考: Floodlight+Mininet搭建OpenFlow(四):流表操作 通过Floodlight控制器下发流表 下发流表的方式有两种: 1.借助Floodlight的北向API,利用curl ...

  6. Hibernate与iBastis 比较(转载)

    Hibernate  VS  iBATIS 简介 Hibernate 是当前最流行的O/R mapping框架,当前版本是3.05.它出身于sf.net,现在已经成为Jboss的一部分了 iBATIS ...

  7. Cocos2d-x学习笔记(七)菜单

    菜单类继承关系如下: 图1 菜单类继承关系 文本菜单只能显示文本,包括:MenuItemLabel.MenuItemFont和MenuItemAtlasFont: #include "Hel ...

  8. python 元组列表转为字典

    #create a list l = [(), (), (), (), (), ()] d = {} for a, b in l: d.setdefault(a, []).append(b) prin ...

  9. c++ 容器元素填充指定数量的元素(generate_n)

    #include <iostream> // cout #include <algorithm> // generate_n using namespace std; ; in ...

  10. js 基础数据类型和引用类型 ,深浅拷贝问题,以及内存分配问题

    js 深浅拷贝问题 浅拷贝一般指的是基本类型的复制 深拷贝一般指引用类型的拷贝,把引用类型的值也拷贝出来 举例 h5的sessionStorage只能存放字符串,所以要存储json时就要把json使用 ...