注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

BCB-DG's Blog

...

 
 
 

日志

 
 

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

2013-09-24 11:03:26|  分类: Linux |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
来源:Linux社区  作者:ce123

Kernel version:2.6.14

CPU architecture:ARM920T

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

/****************************************************************************/
/*简介:TCPServer示例 */
/****************************************************************************/
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
int main(int argc, char *argv[])
{
 int sockfd,new_fd;
 struct sockaddr_in server_addr;
 struct sockaddr_in client_addr;
 int sin_size,portnumber;
 const char hello[]="Hello\n";

 if(argc!=2)
  {
    fprintf(stderr,"Usage:%s portnumber\a\n",argv[0]);
    exit(1);
  }
  if((portnumber=atoi(argv[1]))<0)
  {
      fprintf(stderr,"Usage:%s portnumber\a\n",argv[0]);
      exit(1);
 }
  /* 服务器端开始建立socket描述符 */
  if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1)
  {
    fprintf(stderr,"Socket error:%s\n\a",strerror(errno));
    exit(1);
  }
  /* 服务器端填充 sockaddr结构 */
  bzero(&server_addr,sizeof(struct sockaddr_in));
  server_addr.sin_family=AF_INET;
  server_addr.sin_addr.s_addr=htonl(INADDR_ANY);
  server_addr.sin_port=htons(portnumber);
  /* 捆绑sockfd描述符 */
  if(bind(sockfd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr))==
  -1)
  {
    fprintf(stderr,"Bind error:%s\n\a",strerror(errno));
    exit(1);
  }
  /* 监听sockfd描述符 */
  if(listen(sockfd,5)==-1)
  {
      fprintf(stderr,"Listen error:%s\n\a",strerror(errno));
      exit(1);
  }
  while(1)
  {
  /* 服务器阻塞,直到客户程序建立连接 */
  sin_size=sizeof(struct sockaddr_in);
  if((new_fd=accept(sockfd,(struct sockaddr *)(&client_addr),&sin_size))==-1)
    {
      fprintf(stderr,"Accept error:%s\n\a",strerror(errno));
      exit(1);
    }
  fprintf(stderr,"Server get connection from %s\n",
  inet_ntoa(client_addr.sin_addr));
  if(write(new_fd,hello,strlen(hello))==-1)
  {
      fprintf(stderr,"Write Error:%s\n",strerror(errno));
      exit(1);
    }
    /* 这个通讯已经结束 */
      close(new_fd);
  /* 循环下一个 */
  }
  close(sockfd);
  exit(0);
}

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


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

net/socket.c
/*
 * System call vectors.
 *
 * Argument checking cleaned up. Saved 20% in size.
 *  This function doesn't need to set the kernel lock because
 *  it is set by the callees.
 */

asmlinkage long sys_socketcall(int call, unsigned long __user *args)
{
 unsigned long a[6];
 unsigned long a0,a1;
 int err;

 if(call<1||call>SYS_RECVMSG)
  return -EINVAL;

 /* copy_from_user should be SMP safe. */
 if (copy_from_user(a, args, nargs[call]))
  return -EFAULT;

 err = audit_socketcall(nargs[call]/sizeof(unsigned long), a);
 if (err)
  return 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 = sys_accept(a0,(struct sockaddr __user *)a1, (int __user *)a[2]);
   break;
  case SYS_GETSOCKNAME:
   err = sys_getsockname(a0,(struct sockaddr __user *)a1, (int __user *)a[2]);
   break;
  case SYS_GETPEERNAME:
   err = sys_getpeername(a0, (struct sockaddr __user *)a1, (int __user *)a[2]);
   break;
  case SYS_SOCKETPAIR:
   err = sys_socketpair(a0,a1, a[2], (int __user *)a[3]);
   break;
  case SYS_SEND:
   err = sys_send(a0, (void __user *)a1, a[2], a[3]);
   break;
  case SYS_SENDTO:
   err = sys_sendto(a0,(void __user *)a1, a[2], a[3],
      (struct sockaddr __user *)a[4], a[5]);
   break;
  case SYS_RECV:
   err = sys_recv(a0, (void __user *)a1, a[2], a[3]);
   break;
  case SYS_RECVFROM:
   err = sys_recvfrom(a0, (void __user *)a1, a[2], a[3],
        (struct sockaddr __user *)a[4], (int __user *)a[5]);
   break;
  case SYS_SHUTDOWN:
   err = sys_shutdown(a0,a1);
   break;
  case SYS_SETSOCKOPT:
   err = sys_setsockopt(a0, a1, a[2], (char __user *)a[3], a[4]);
   break;
  case SYS_GETSOCKOPT:
   err = sys_getsockopt(a0, a1, a[2], (char __user *)a[3], (int __user *)a[4]);
   break;
  case SYS_SENDMSG:
   err = sys_sendmsg(a0, (struct msghdr __user *) a1, a[2]);
   break;
  case SYS_RECVMSG:
   err = sys_recvmsg(a0, (struct msghdr __user *) a1, a[2]);
   break;
  default:
   err = -EINVAL;
   break;
 }
 return err;
} /* It may be already another descriptor 8) Not kernel problem. */
 return retval;

out_release:
 sock_release(sock);
 return retval;
}

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

asmlinkage long sys_socket(int family, int type, int protocol)
{
 int retval;
 struct socket *sock;

 retval = sock_create(family, type, protocol, &sock);//创建socket
 if (retval < 0)
  goto out;

 retval = sock_map_fd(sock);//分配一个未使用的文件描述符fd,并将socket和fd建立联系
 if (retval < 0)
  goto out_release;

out:
 /* It may be already another descriptor 8) Not kernel problem. */
 return retval;

out_release:
 sock_release(sock);
 return retval;
}

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

struct socket {
 socket_state  state;
 unsigned long  flags;
 struct proto_ops *ops;
 struct fasync_struct *fasync_list;
 struct file  *file;//通过这个和文件描述符建立联系
 struct sock  *sk;
 wait_queue_head_t wait;
 short   type;
};

下面我们再来看看sock_map_fd函数

int sock_map_fd(struct socket *sock)
{
 int fd;
 struct qstr this;
 char name[32];

 /*
  * Find a file descriptor suitable for return to the user.
  */

 fd = get_unused_fd();//分配一个未使用的fd
 if (fd >= 0) {
  struct file *file = get_empty_filp();

  if (!file) {
   put_unused_fd(fd);
   fd = -ENFILE;
   goto out;
  }

  this.len = sprintf(name, "[%lu]", SOCK_INODE(sock)->i_ino);
  this.name = name;
  this.hash = SOCK_INODE(sock)->i_ino;

  file->f_dentry = d_alloc(sock_mnt->mnt_sb->s_root, &this);
  if (!file->f_dentry) {
   put_filp(file);
   put_unused_fd(fd);
   fd = -ENOMEM;
   goto out;
  }
  file->f_dentry->d_op = &sockfs_dentry_operations;
  d_add(file->f_dentry, SOCK_INODE(sock));
  file->f_vfsmnt = mntget(sock_mnt);
  file->f_mapping = file->f_dentry->d_inode->i_mapping;

  sock->file = file;//建立联系
  file->f_op = SOCK_INODE(sock)->i_fop = &socket_file_ops;//socket操作函数,当使用文件系统的IO函数时,其实使用的是socket的IO函数
  file->f_mode = FMODE_READ | FMODE_WRITE;
  file->f_flags = O_RDWR;
  file->f_pos = 0;
  file->private_data = sock;
  fd_install(fd, file);
 }

out:
 return fd;
}

static 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,
 .mmap =  sock_mmap,
 .open =  sock_no_open, /* special open code to disallow open via /proc */
 .release = sock_close,
 .fasync = sock_fasync,
 .readv = sock_readv,
 .writev = sock_writev,
 .sendpage = sock_sendpage
};

  评论这张
 
阅读(607)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017