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. [转帖]ps 命令详解

    ps 命令详解 https://www.jianshu.com/p/cba22cce2f97 ps 概述 Linux中的ps命令是Process Status的缩写.ps命令用来列出系统中当前运行的那 ...

  2. idea 本地调试,修改代码,代码自动生效

    修改1: 修改2: ctrl+shift+a 弹出页面中搜索“registry” 修改3:修改configerations

  3. R语言dataframe的常用操作总结

    前言:近段时间学习R语言用到最多的数据格式就是data.frame,现对data.frame常用操作进行总结,其中函数大部分来自dplyr包,该包由Hadley Wickham所作,主要用于数据的清洗 ...

  4. deepin安装卡在deepin标志界面解决方案

    再次重启前将U盘插上,进系统前按快速选择启动装置F12(不同品牌电脑可能不同),选择从U盘启动: 进入第一个安装界面时一定要注意:在跳转前,按E进入grub设置界面,移动光标到倒数第二行的”quiet ...

  5. Java学习:可变参数

    可变参数 可变参数:是JDK1.5 之后出现的新特性 使用前提: 当方法的参数列表数据类型已经确定,但是参数的个数不确定,就可以使用可变参数. 使用格式:定义方法时使用 修饰符 返回值类型 方法名(数 ...

  6. golang学习笔记--函数和方法

    在go中,函数类型是一等类型,这意味着可以吧函数当做一个值来传递和使用. func divide(dividend int,divisor int)(int,error){ //省略部分代码 } 参数 ...

  7. 基于已有集群动态发现方式部署 Etcd 集群

    etcd提供了多种部署集群的方式,在「通过静态发现方式部署etcd集群」 一文中我们介绍了如何通过静态发现方式部署集群. 不过很多时候,你只知道你要搭建一个多大(包含多少节点)的集群,但是并不能事先知 ...

  8. 【java】java 读写文件

    场景:JDK8  将上传的文件,保存到服务器 Java读写文件操作: MultipartFile file InputStream inputStream = file.getInputStream( ...

  9. 认清楚服务器的真正身份--深入ARP工作原理

    我们知道IP地址是ISP分配给我们的,IP不能作为服务器的唯一的身份,那么服务器真正的身份是什么呢?MAC IP地址直接的通信在底层要转换到MAC直接的通信,那他们如何通信的呢? 1.简介 出场人物: ...

  10. DotnetSpider爬虫简单示例 net core

    文章地址 https://blog.csdn.net/sD7O95O/article/details/78097556 安装爬虫框架  NUGET 安装DotnetSpider 创建HTTP协议数据包 ...