手把手写C++服务器(25):万物皆可文件之socket fd
Posted 沉迷单车的追风少年
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了手把手写C++服务器(25):万物皆可文件之socket fd相关的知识,希望对你有一定的参考价值。
本系列文章导航: 手把手写C++服务器(0):专栏文章-汇总导航【更新中】
前言:大家一定听说过在Linux当中,万物皆是文件,任何客观的存在都是以文件形式呈现。前面讲socket编程的时候(手把手写C++服务器(21):Linux socket网络编程入门基础、手把手写C++服务器(22):Linux socket网络编程进阶第一弹)可以看出,sockfd伴随socket的“生老病死”,这一讲就从Linux文件描述符开始讲起,详细聊一聊socket fd。
目录
万物皆可文件的Linux
在Linux系统中一切皆可以看成是文件,文件又可分为:普通文件、目录文件、链接文件和设备文件。在操作这些所谓的文件的时候,我们每操作一次就找一次名字,这会耗费大量的时间和效率。所以Linux中规定每一个文件对应一个索引,这样要操作文件的时候,我们直接找到索引就可以对其进行操作了。
服务器最宝贵的资源之一——文件描述符
文件描述符
文件描述符(file descriptor)就是内核为了高效管理这些已经被打开的文件所创建的索引,其是一个非负整数(通常是小整数),用于指代被打开的文件,所有执行I/O操作的系统调用都通过文件描述符来实现。
文件描述符是服务器程序最宝贵的资源之一,几乎所有系统调用都是和文件描述符打交道,但是系统分配给应用程序的文件描述符数量是有限的。同时还规定系统刚刚启动的时候,0是标准输入,1是标准输出,2是标准错误。这意味着如果此时去打开一个新的文件,它的文件描述符会是3,再打开一个文件文件描述符就是4,以此类推。
每个进程在PCB(Process Control Block)中保存着一份文件描述符表,文件描述符就是这个表的索引,每个表项都有一个指向已打开文件的指针。 当程序打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符。
最大文件描述符限制
Linux对应用程序能打开的最大文件描述符数量总共有两个层次的限制:用户级限制和系统级限制。
- 用户级限制:目标用户运行的所有进程总共能打开的文件描述符数。
- 系统级限制:所有用户总共能打开的文件描述符数。
查看用户级文件文件描述符限制:
ulimit -n
1024
显示目前系统资源限定:
ulimit -a
core file size (blocks, -c) unlimited
data seg size (kbytes, -d) unlimited
scheduling priority (-e) 0
file size (blocks, -f) unlimited
pending signals (-i) 514162
max locked memory (kbytes, -l) 16384
max memory size (kbytes, -m) unlimited
open files (-n) 1024
pipe size (512 bytes, -p) 8
POSIX message queues (bytes, -q) 819200
real-time priority (-r) 0
stack size (kbytes, -s) 8192
cpu time (seconds, -t) unlimited
max user processes (-u) unlimited
virtual memory (kbytes, -v) unlimited
file locks (-x) unlimited
系统建立三个表维护文件描述符
-
进程级的文件描述符表
-
系统级的文件描述符表
-
文件系统的i-node表
查看进程占用的文件描述符限制
不熟悉这些Linux命令的可以去看我的系列文章,虽然还没有发出哈哈哈
ps -aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.0 18384 3128 ? Ss Aug04 0:49 /bin/bash -c service ssh start; while true;do sleep 10;done
root 14 0.0 0.0 72308 4088 ? Ss Aug04 0:00 /usr/sbin/sshd
root 39608 0.0 0.0 30564 3208 ? Ss Aug14 0:28 SCREEN -S linear
root 39609 0.0 0.0 20452 4072 pts/2 Ss+ Aug14 0:00 /bin/bash
root 50635 0.0 0.0 107992 7216 ? Rs 07:18 0:00 sshd: root@pts/0
root 50646 0.0 0.0 20436 4092 pts/0 Ss 07:18 0:00 -bash
root 50825 0.0 0.0 4548 796 ? S 07:42 0:00 sleep 10
root 50828 0.0 0.0 38380 3572 pts/0 R+ 07:42 0:00 ps -aux
cat /proc/39608/limits
Limit Soft Limit Hard Limit Units
Max cpu time unlimited unlimited seconds
Max file size unlimited unlimited bytes
Max data size unlimited unlimited bytes
Max stack size 8388608 unlimited bytes
Max core file size unlimited unlimited bytes
Max resident set unlimited unlimited bytes
Max processes unlimited unlimited processes
Max open files 1024 1048576 files
Max locked memory 16777216 16777216 bytes
Max address space unlimited unlimited bytes
Max file locks unlimited unlimited locks
Max pending signals 514162 514162 signals
Max msgqueue size 819200 819200 bytes
Max nice priority 0 0
Max realtime priority 0 0
Max realtime timeout unlimited unlimited us
在 Max open files 那一行,可以看到当前设置中最大文件描述符的数量为1024
网络文件描述符
什么是inode?
阮一峰说的非常精彩了:http://www.ruanyifeng.com/blog/2011/12/inode.html
inode 是 vfs 抽象的适配所有文件系统的结构体,但分配其实是有下层具体文件系统分配出来的,以 ext4 文件系统来说,使用 ext4_alloc_inode
函数分配出 ext4_inode_info
这个大结构体,然后返回的是 inode 的地址而已。
查看当tcp连接状态
可以获取当前tcp连接的innode信息
cat /proc/net/tcp
sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode
0: 00000000:0016 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 14111686 1 0000000000000000 100 0 0 10 0
1: 040011AC:0016 1602260A:EBA5 01 00000024:00000000 01:0000001B 00000000 0 0 99084810 4 0000000000000000 28 4 31 10 -1
sockfs 文件系统
sockfs 文件系统,普通用户看不见,这是只由内核管理的文件系统,位于 vfs 之下,为了封装 socket 对上的文件语义。
// net/socket.c
static int __init sock_init(void)
{
// 注册 sockfs 文件系统
err = register_filesystem(&sock_fs_type);
// 内核挂载
sock_mnt = kern_mount(&sock_fs_type);
}
创建socket()
#include<sys/types.h>
#include<sys/socket.h>
int socket(int domain,int type,int protocol);
socket( )
返回的是非负整数的 fd,与 struct file
对应,而 struct file
则与具体的 struct socket
关联,从而实现一切皆文件的封装的一部分(另一部分 inode 的创建处理在 sock_alloc 的函数里体现);
绑定bind()
#include<sys/types.h>
#include<sys/socket.h>
int bind(int sockfd,const struct sockaddr*my_addr,socklen_t addrlen);
-
先通过 fd 找到对应的
struct socket
结构体; -
然后把 address 和 socket 绑定对应起来(调用
sock->ops->bind
函数);
监听listen()
#include<sys/socket.h>
int listen(int sockfd,int backlog);
-
通过 fd 找到
struct socket
结构体; -
调用
sock->ops->listen
函数(对应inet_listen
);
epoll池化管理socket
epoll 池能管理 socketfd,因为 socket fd 实现 poll 接口。具体的管理方法会在本系列后面的文章中详细讲解。
参考
以上是关于手把手写C++服务器(25):万物皆可文件之socket fd的主要内容,如果未能解决你的问题,请参考以下文章
手把手写C++服务器(31):服务器性能提升关键——IO复用技术两万字长文