http://blog.csdn.net/ce123_zhouwei/article/details/8459730

Linux内核中的文件描述符(二)--socket和文件描述符

Kernel version:2.6.14

CPU architecture:ARM920T

Author:ce123(http://blog.csdn.NET/ce123)

socket和文件系统紧密相关,我们可以通过文件系统的open、read、write和close等操作socket。下面是一个简单的例子。

  1. /****************************************************************************/
  2. /*简介:TCPServer示例 */
  3. /****************************************************************************/
  4. #include <stdlib.h>
  5. #include <stdio.h>
  6. #include <errno.h>
  7. #include <string.h>
  8. #include <netdb.h>
  9. #include <sys/types.h>
  10. #include <netinet/in.h>
  11. #include <sys/socket.h>
  12. int main(int argc, char *argv[])
  13. {
  14. int sockfd,new_fd;
  15. struct sockaddr_in server_addr;
  16. struct sockaddr_in client_addr;
  17. int sin_size,portnumber;
  18. const char hello[]="Hello\n";
  19. if(argc!=2)
  20. {
  21. fprintf(stderr,"Usage:%s portnumber\a\n",argv[0]);
  22. exit(1);
  23. }
  24. if((portnumber=atoi(argv[1]))<0)
  25. {
  26. fprintf(stderr,"Usage:%s portnumber\a\n",argv[0]);
  27. exit(1);
  28. }
  29. /* 服务器端开始建立socket描述符 */
  30. if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1)
  31. {
  32. fprintf(stderr,"Socket error:%s\n\a",strerror(errno));
  33. exit(1);
  34. }
  35. /* 服务器端填充 sockaddr结构 */
  36. bzero(&server_addr,sizeof(struct sockaddr_in));
  37. server_addr.sin_family=AF_INET;
  38. server_addr.sin_addr.s_addr=htonl(INADDR_ANY);
  39. server_addr.sin_port=htons(portnumber);
  40. /* 捆绑sockfd描述符 */
  41. if(bind(sockfd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr))==
  42. -1)
  43. {
  44. fprintf(stderr,"Bind error:%s\n\a",strerror(errno));
  45. exit(1);
  46. }
  47. /* 监听sockfd描述符 */
  48. if(listen(sockfd,5)==-1)
  49. {
  50. fprintf(stderr,"Listen error:%s\n\a",strerror(errno));
  51. exit(1);
  52. }
  53. while(1)
  54. {
  55. /* 服务器阻塞,直到客户程序建立连接 */
  56. sin_size=sizeof(struct sockaddr_in);
  57. if((new_fd=accept(sockfd,(struct sockaddr *)(&client_addr),&sin_size))==-1)
  58. {
  59. fprintf(stderr,"Accept error:%s\n\a",strerror(errno));
  60. exit(1);
  61. }
  62. fprintf(stderr,"Server get connection from %s\n",
  63. inet_ntoa(client_addr.sin_addr));
  64. if(write(new_fd,hello,strlen(hello))==-1)
  65. {
  66. fprintf(stderr,"Write Error:%s\n",strerror(errno));
  67. exit(1);
  68. }
  69. /* 这个通讯已经结束 */
  70. close(new_fd);
  71. /* 循环下一个 */
  72. }
  73. close(sockfd);
  74. exit(0);
  75. }

下图说明了socket和fd是怎样联系起来的。

下面通过来具体分析一下。sys_socket是socket相关函数的总入口。

  1. net/socket.c
  2. /*
  3. *  System call vectors.
  4. *
  5. *  Argument checking cleaned up. Saved 20% in size.
  6. *  This function doesn't need to set the kernel lock because
  7. *  it is set by the callees.
  8. */
  9. asmlinkage long sys_socketcall(int call, unsigned long __user *args)
  10. {
  11. unsigned long a[6];
  12. unsigned long a0,a1;
  13. int err;
  14. if(call<1||call>SYS_RECVMSG)
  15. return -EINVAL;
  16. /* copy_from_user should be SMP safe. */
  17. if (copy_from_user(a, args, nargs[call]))
  18. return -EFAULT;
  19. err = audit_socketcall(nargs[call]/sizeof(unsigned long), a);
  20. if (err)
  21. return err;
  22. a0=a[0];
  23. a1=a[1];
  24. switch(call)
  25. {
  26. case SYS_SOCKET:
  27. err = sys_socket(a0,a1,a[2]);
  28. break;
  29. case SYS_BIND:
  30. err = sys_bind(a0,(struct sockaddr __user *)a1, a[2]);
  31. break;
  32. case SYS_CONNECT:
  33. err = sys_connect(a0, (struct sockaddr __user *)a1, a[2]);
  34. break;
  35. case SYS_LISTEN:
  36. err = sys_listen(a0,a1);
  37. break;
  38. case SYS_ACCEPT:
  39. err = sys_accept(a0,(struct sockaddr __user *)a1, (int __user *)a[2]);
  40. break;
  41. case SYS_GETSOCKNAME:
  42. err = sys_getsockname(a0,(struct sockaddr __user *)a1, (int __user *)a[2]);
  43. break;
  44. case SYS_GETPEERNAME:
  45. err = sys_getpeername(a0, (struct sockaddr __user *)a1, (int __user *)a[2]);
  46. break;
  47. case SYS_SOCKETPAIR:
  48. err = sys_socketpair(a0,a1, a[2], (int __user *)a[3]);
  49. break;
  50. case SYS_SEND:
  51. err = sys_send(a0, (void __user *)a1, a[2], a[3]);
  52. break;
  53. case SYS_SENDTO:
  54. err = sys_sendto(a0,(void __user *)a1, a[2], a[3],
  55. (struct sockaddr __user *)a[4], a[5]);
  56. break;
  57. case SYS_RECV:
  58. err = sys_recv(a0, (void __user *)a1, a[2], a[3]);
  59. break;
  60. case SYS_RECVFROM:
  61. err = sys_recvfrom(a0, (void __user *)a1, a[2], a[3],
  62. (struct sockaddr __user *)a[4], (int __user *)a[5]);
  63. break;
  64. case SYS_SHUTDOWN:
  65. err = sys_shutdown(a0,a1);
  66. break;
  67. case SYS_SETSOCKOPT:
  68. err = sys_setsockopt(a0, a1, a[2], (char __user *)a[3], a[4]);
  69. break;
  70. case SYS_GETSOCKOPT:
  71. err = sys_getsockopt(a0, a1, a[2], (char __user *)a[3], (int __user *)a[4]);
  72. break;
  73. case SYS_SENDMSG:
  74. err = sys_sendmsg(a0, (struct msghdr __user *) a1, a[2]);
  75. break;
  76. case SYS_RECVMSG:
  77. err = sys_recvmsg(a0, (struct msghdr __user *) a1, a[2]);
  78. break;
  79. default:
  80. err = -EINVAL;
  81. break;
  82. }
  83. return err;
  84. }   /* It may be already another descriptor 8) Not kernel problem. */
  85. return retval;
  86. out_release:
  87. sock_release(sock);
  88. return retval;
  89. }

当应用程序使用socket()创建一个socket时,会执行sys_socket,其定义如下

  1. asmlinkage long sys_socket(int family, int type, int protocol)
  2. {
  3. int retval;
  4. struct socket *sock;
  5. retval = sock_create(family, type, protocol, &sock);//创建socket
  6. if (retval < 0)
  7. goto out;
  8. retval = sock_map_fd(sock);//分配一个未使用的文件描述符fd,并将socket和fd建立联系
  9. if (retval < 0)
  10. goto out_release;
  11. out:
  12. /* It may be already another descriptor 8) Not kernel problem. */
  13. return retval;
  14. out_release:
  15. sock_release(sock);
  16. return retval;
  17. }

结构体socket的定义如下(include\linux\net.h):

  1. struct socket {
  2. socket_state        state;
  3. unsigned long       flags;
  4. struct proto_ops    *ops;
  5. struct fasync_struct    *fasync_list;
  6. struct file     *file;//通过这个和文件描述符建立联系
  7. struct sock     *sk;
  8. wait_queue_head_t   wait;
  9. short           type;
  10. };

下面我们再来看看sock_map_fd函数

  1. int sock_map_fd(struct socket *sock)
  2. {
  3. int fd;
  4. struct qstr this;
  5. char name[32];
  6. /*
  7. *  Find a file descriptor suitable for return to the user.
  8. */
  9. fd = get_unused_fd();//分配一个未使用的fd
  10. if (fd >= 0) {
  11. struct file *file = get_empty_filp();
  12. if (!file) {
  13. put_unused_fd(fd);
  14. fd = -ENFILE;
  15. goto out;
  16. }
  17. this.len = sprintf(name, "[%lu]", SOCK_INODE(sock)->i_ino);
  18. this.name = name;
  19. this.hash = SOCK_INODE(sock)->i_ino;
  20. file->f_dentry = d_alloc(sock_mnt->mnt_sb->s_root, &this);
  21. if (!file->f_dentry) {
  22. put_filp(file);
  23. put_unused_fd(fd);
  24. fd = -ENOMEM;
  25. goto out;
  26. }
  27. file->f_dentry->d_op = &sockfs_dentry_operations;
  28. d_add(file->f_dentry, SOCK_INODE(sock));
  29. file->f_vfsmnt = mntget(sock_mnt);
  30. file->f_mapping = file->f_dentry->d_inode->i_mapping;
  31. sock->file = file;//建立联系
  32. file->f_op = SOCK_INODE(sock)->i_fop = &socket_file_ops;//socket操作函数,当使用文件系统的IO函数时,其实使用的是socket的IO函数
  33. file->f_mode = FMODE_READ | FMODE_WRITE;
  34. file->f_flags = O_RDWR;
  35. file->f_pos = 0;
  36. file->private_data = sock;
  37. fd_install(fd, file);
  38. }
  39. out:
  40. return fd;
  41. }
  42. static struct file_operations socket_file_ops = {
  43. .owner =    THIS_MODULE,
  44. .llseek =   no_llseek,
  45. .aio_read = sock_aio_read,
  46. .aio_write =    sock_aio_write,
  47. .poll =     sock_poll,
  48. .unlocked_ioctl = sock_ioctl,
  49. .mmap =     sock_mmap,
  50. .open =     sock_no_open,   /* special open code to disallow open via /proc */
  51. .release =  sock_close,
  52. .fasync =   sock_fasync,
  53. .readv =    sock_readv,
  54. .writev =   sock_writev,
  55. .sendpage = sock_sendpage
  56. };
 
 

linux内核中的文件描述符(二)--socket和文件描述符的更多相关文章

  1. 【转】在linux内核中读写文件 -- 不错

    原文网址:http://blog.csdn.net/tommy_wxie/article/details/8194276 1. 序曲 在用户态,读写文件可以通过read和write这两个系统调用来完成 ...

  2. linux内核中链表代码分析---list.h头文件分析(二)【转】

    转自:http://blog.chinaunix.net/uid-30254565-id-5637598.html linux内核中链表代码分析---list.h头文件分析(二) 16年2月28日16 ...

  3. 【转】 Linux内核中读写文件数据的方法--不错

    原文网址:http://blog.csdn.net/tommy_wxie/article/details/8193954 Linux内核中读写文件数据的方法  有时候需要在Linuxkernel--大 ...

  4. linux内核中链表代码分析---list.h头文件分析(一)【转】

    转自:http://blog.chinaunix.net/uid-30254565-id-5637596.html linux内核中链表代码分析---list.h头文件分析(一) 16年2月27日17 ...

  5. Linux内核中的GPIO系统之(3):pin controller driver代码分析

    一.前言 对于一个嵌入式软件工程师,我们的软件模块经常和硬件打交道,pin control subsystem也不例外,被它驱动的硬件叫做pin controller(一般ARM soc的datash ...

  6. Linux内核--网络栈实现分析(二)--数据包的传递过程--转

    转载地址http://blog.csdn.net/yming0221/article/details/7492423 作者:闫明 本文分析基于Linux Kernel 1.2.13 注:标题中的”(上 ...

  7. linux内核中socket的创建过程源码分析(总结性质)

    在漫长地分析完socket的创建源码后,发现一片浆糊,所以特此总结,我的博客中同时有另外一篇详细的源码分析,内核版本为3.9,建议在阅读本文后若还有兴趣再去看另外一篇博文.绝对不要单独看另外一篇. 一 ...

  8. Linux内核中的GPIO系统之(3):pin controller driver代码分析--devm_kzalloc使用【转】

    转自:http://www.wowotech.net/linux_kenrel/pin-controller-driver.html 一.前言 对于一个嵌入式软件工程师,我们的软件模块经常和硬件打交道 ...

  9. Linux内核--网络栈实现分析(二)--数据包的传递过程(上)

    本文分析基于Linux Kernel 1.2.13 原创作品,转载请标明http://blog.csdn.net/yming0221/article/details/7492423 更多请看专栏,地址 ...

随机推荐

  1. CentOS7 下 yum 安装 Docker CE

    前言 Docker 使用越来越多,安装也很简单,本次记录一下基本的步骤. Docker 目前支持 CentOS 7 及以后的版本,内核要求至少为 3.10. Docker 官网有安装步骤,本文只是记录 ...

  2. 用ab每隔30分钟并发一次休息10分钟

    linux脚本监控程序运行情况(重启程序)主要有两种情况:一种是一个可执行文件:如shell脚本文件:另一种是使用python打开的多个程序.第一种:它的进程名字由路径名字和程序名字组成,比如:我有个 ...

  3. count和distinct

    一.count和distinct count是统计数据条数,distinct是去掉重复列: count统计的时候会忽略null值,distinct会将重复的null值列作为一个. 综上select c ...

  4. SQL系列(十三)—— 关于表的DDL

    前面的文章一直都在讲述关于DML方面的SQL Statement.这篇文章来说说表方面的DDL: CREATE 创建表 ALTER 修改表 DROP 删除表 CREATE 1.语法 CREATE TA ...

  5. 50道Redis面试题及答案整理,史上最全!

    在网上看到有关Redis的50道面试题,但是没有给出答案,之前我也在寻找这份Redis面试题的答案,今天特地把答案分享出来. 花了大量时间整理了这套Redis面试题及答案,希望对大家有帮助哈~ 弄明白 ...

  6. java中多重循环和break、continue语句

    一.嵌套循环 循环可以互相嵌套,以实现更加复杂的逻辑,其代码的复杂程度也会提高,对初学者而言这应该是个难点,下面我们通过一些例子说明嵌套循环的使用,读者要自己把这些代码上机练习,并理解程序运行的流程. ...

  7. SQL Server的NTEXT类型不支持等号"="操作(转载)

    SQL SERVER – Fix: Error : 402 The data types ntext and varchar are incompatible in the equal to oper ...

  8. 重磅开源 KSQL:用于 Apache Kafka 的流数据 SQL 引擎 2017.8.29

    Kafka 的作者 Neha Narkhede 在 Confluent 上发表了一篇博文,介绍了Kafka 新引入的KSQL 引擎——一个基于流的SQL.推出KSQL 是为了降低流式处理的门槛,为处理 ...

  9. Echarts 学习系列(2)-常见的静态ECharts图

    目录 写在前面 折线(面积)图 1.折线图 2.堆叠折线图 3.堆积面积图 柱状(条形)图 1.柱状图 2.条形图 3.堆积条形图 饼(圆环)图 1.饼图 2.环形图 3.南丁格尔图 写在前面 上一小 ...

  10. [Windows] - DNS防污染工具Pcap_DNSProxy

    最近试过非常多的DNS防污染工具(包括:dnsforwarder.dnsforwarder.dnscrypt-proxy.SimpleDNSCrypt等),感觉这个Pcap_DNSProxy简单.快捷 ...