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

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);//创建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
};

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值