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

时间:2022-10-26 00:14:24

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是怎样联系起来的。

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

下面通过来具体分析一下。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. };