linux内核中的文件描述符(二)--socket和文件描述符
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\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
- };
linux内核中的文件描述符(二)--socket和文件描述符的更多相关文章
- 【转】在linux内核中读写文件 -- 不错
原文网址:http://blog.csdn.net/tommy_wxie/article/details/8194276 1. 序曲 在用户态,读写文件可以通过read和write这两个系统调用来完成 ...
- linux内核中链表代码分析---list.h头文件分析(二)【转】
转自:http://blog.chinaunix.net/uid-30254565-id-5637598.html linux内核中链表代码分析---list.h头文件分析(二) 16年2月28日16 ...
- 【转】 Linux内核中读写文件数据的方法--不错
原文网址:http://blog.csdn.net/tommy_wxie/article/details/8193954 Linux内核中读写文件数据的方法 有时候需要在Linuxkernel--大 ...
- linux内核中链表代码分析---list.h头文件分析(一)【转】
转自:http://blog.chinaunix.net/uid-30254565-id-5637596.html linux内核中链表代码分析---list.h头文件分析(一) 16年2月27日17 ...
- Linux内核中的GPIO系统之(3):pin controller driver代码分析
一.前言 对于一个嵌入式软件工程师,我们的软件模块经常和硬件打交道,pin control subsystem也不例外,被它驱动的硬件叫做pin controller(一般ARM soc的datash ...
- Linux内核--网络栈实现分析(二)--数据包的传递过程--转
转载地址http://blog.csdn.net/yming0221/article/details/7492423 作者:闫明 本文分析基于Linux Kernel 1.2.13 注:标题中的”(上 ...
- linux内核中socket的创建过程源码分析(总结性质)
在漫长地分析完socket的创建源码后,发现一片浆糊,所以特此总结,我的博客中同时有另外一篇详细的源码分析,内核版本为3.9,建议在阅读本文后若还有兴趣再去看另外一篇博文.绝对不要单独看另外一篇. 一 ...
- Linux内核中的GPIO系统之(3):pin controller driver代码分析--devm_kzalloc使用【转】
转自:http://www.wowotech.net/linux_kenrel/pin-controller-driver.html 一.前言 对于一个嵌入式软件工程师,我们的软件模块经常和硬件打交道 ...
- Linux内核--网络栈实现分析(二)--数据包的传递过程(上)
本文分析基于Linux Kernel 1.2.13 原创作品,转载请标明http://blog.csdn.net/yming0221/article/details/7492423 更多请看专栏,地址 ...
随机推荐
- [EXP]CVE-2019-0604 Microsoft SharePoint RCE Exploit
研表究明,汉字的序顺并不定一能影阅响读,比如当你看完这句话后,才发这现里的字全是都乱的. 剑桥大学的研究结果,当单词的字母顺序颠倒时,你仍旧可以明白整个单词的意思.其中重要的是:只要单词的第一个字母和 ...
- Oracle账号,用于下载jdk
账号:liwei@xiaostudy.com 密码:OracleTest1234
- 045 用户登录功能01----JWT和后台代码
(1)有状态登录概述 有状态服务,即服务端需要记录每次会话的客户端信息,从而识别客户端身份,根据用户身份进行请求的处理,典型的设计如tomcat中的session. 例如登录:用户登录后,我们把登录者 ...
- 单口 RAM、伪双口 RAM、真双口 RAM、单口 ROM、双口 ROM 到底有什么区别呢?
打开 IP Catalog,搜索 Block Memory Generator,即可看到其 Memory Type 可分为 5 中,分别是单口 RAM(Single Port RAM).伪双口 RAM ...
- kubectl 创建 Pod 背后到底发生了什么?
原文链接:kubectl 创建 Pod 背后到底发生了什么? 想象一下,如果我想将 nginx 部署到 Kubernetes 集群,我可能会在终端中输入类似这样的命令: $ kubectl run - ...
- Queue介绍
美人如斯! 前言 队列是一种先进先出(FIFO)的数据结构,与生活中的排队类似,即先来先被服务,这样的特点决定了其具有一定的优先级含义,可以被用于任务调度等场景.队列模型如图: 图1.队列模型 jav ...
- 6、VUE指令
1.指令的格式 1.1. 指令的概念 指令是指带有v-前缀的特殊属性,指令的职责是当其表达式的值改变时,相应的将某些行为应用到DOM上. 1.2. 指令必须是html的属性 指令只能以带前缀的html ...
- node-exporter常用指标含义,比如在prometheus中查询node_load1的指标数据
参考: https://blog.csdn.net/yjph83/article/details/84909319 https://www.gitbook.com/book/songjiayang/p ...
- 第一个Golang程序
1. 第一个 HelloGolang 程序 1.1 Go 源程序的基本概念 Go 源程序就是一个特殊格式的文本文件,可以使用任意文本编辑软件做 Go 的开发 Go 程序的 文件扩展名 通常都是 .go ...
- django配置文件
1.BASSE_DIR BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 当前工程的根目录,Django会依 ...