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

来源:互联网 发布:java httpclient 保持 编辑:程序博客网 时间:2024/05/29 13:14

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

Kernel version:2.6.14

CPU architecture:ARM920T

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

socket和文件系统紧密相关,我们可以通过文件系统的openreadwriteclose等操作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); }

下图说明了socketfd是怎样联系起来的。


下面通过来具体分析一下。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);//创建socketif (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_statestate;unsigned longflags;struct proto_ops*ops;struct fasync_struct*fasync_list;struct file*file;//通过这个和文件描述符建立联系struct sock*sk;wait_queue_head_twait;shorttype;};
下面我们再来看看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();//分配一个未使用的fdif (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};