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。下面是一个简单的例子。
- /****************************************************************************/
- /*简介: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 ";
- if(argc!=2)
- {
- fprintf(stderr,"Usage:%s portnumbera ",argv[0]);
- exit(1);
- }
- if((portnumber=atoi(argv[1]))<0)
- {
- fprintf(stderr,"Usage:%s portnumbera ",argv[0]);
- exit(1);
- }
- /* 服务器端开始建立socket描述符 */
- if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1)
- {
- fprintf(stderr,"Socket error:%s 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 a",strerror(errno));
- exit(1);
- }
- /* 监听sockfd描述符 */
- if(listen(sockfd,5)==-1)
- {
- fprintf(stderr,"Listen error:%s 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 a",strerror(errno));
- exit(1);
- }
- fprintf(stderr,"Server get connection from %s ",
- inet_ntoa(client_addr.sin_addr));
- if(write(new_fd,hello,strlen(hello))==-1)
- {
- fprintf(stderr,"Write Error:%s ",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;
- }
- 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;
- }
- 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;
- };
- 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
- };