手把手写c++服务器(25):万物皆可文件之socketfd(代码片段)

沉迷单车的追风少年 沉迷单车的追风少年     2022-12-23     797

关键词:

 本系列文章导航: 手把手写C++服务器(0):专栏文章-汇总导航【更新中】 

前言:大家一定听说过在Linux当中,万物皆是文件,任何客观的存在都是以文件形式呈现。前面讲socket编程的时候(手把手写C++服务器(21):Linux socket网络编程入门基础手把手写C++服务器(22):Linux socket网络编程进阶第一弹)可以看出,sockfd伴随socket的“生老病死”,这一讲就从Linux文件描述符开始讲起,详细聊一聊socket fd。

目录

万物皆可文件的Linux

服务器最宝贵的资源之一——文件描述符

文件描述符

最大文件描述符限制

系统建立三个表维护文件描述符

 查看进程占用的文件描述符限制

网络文件描述符

什么是inode?

查看当tcp连接状态

sockfs 文件系统

创建socket()

绑定bind()

监听listen()

epoll池化管理socket

参考


万物皆可文件的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);
  1. 先通过 fd 找到对应的 struct socket 结构体;

  2. 然后把 address 和 socket 绑定对应起来(调用 sock->ops->bind 函数);

监听listen()

#include<sys/socket.h>
int listen(int sockfd,int backlog);
  1. 通过 fd 找到 struct socket 结构体;

  2. 调用 sock->ops->listen 函数(对应 inet_listen );

epoll池化管理socket

epoll 池能管理 socketfd,因为 socket fd 实现 poll 接口。具体的管理方法会在本系列后面的文章中详细讲解。

参考

手把手写c++服务器(27):五大文件描述符零拷贝控制总结(代码片段)

 本系列文章导航:手把手写C++服务器(0):专栏文章-汇总导航【更新中】 前言:前文《手把手写C++服务器(26):常用I/O操作、创建文件描述符》、《手把手写C++服务器(25):万物皆可文件之sock... 查看详情

手把手写c++服务器(31):服务器性能提升关键——io复用技术两万字长文

 本系列文章导航:手把手写C++服务器(0):专栏文章-汇总导航【更新中】 前言:Linux中素有“万物皆文件,一切皆IO”的说法。前面几讲手撕了CGI网关服务器、echo回显服务器、discard服务的代码,但是这几个一次只能监听... 查看详情

手把手写c++服务器(32):三大事件之信号详解(代码片段)

本系列文章导航:手把手写C++服务器(0):专栏文章-汇总导航【更新中】 前言:信号实际上是一种软中断,信号机制实际上是进程间通信的一种方式。状态改变、系统异常、系统状态的变化等等,这些是... 查看详情

手把手写c++服务器(31):服务器性能提升关键——io复用技术两万字长文(代码片段)

 本系列文章导航:手把手写C++服务器(0):专栏文章-汇总导航【更新中】 前言:Linux中素有“万物皆文件,一切皆IO”的说法。前面几讲手撕了CGI网关服务器、echo回显服务器、discard服务的代码,但是这... 查看详情

万物皆可gan生成对抗网络生成手写数字part2(代码片段)

【万物皆可GAN】生成对抗网络生成手写数字Part2概述完整代码模型主函数输出结果生成的图片概述GAN(GenerativeAdversarialNetwork)即生成对抗网络.GAN网络包括一个生成器(Generator)和一个判别器(Discriminator).GAN可以自动提取特征,并判断和... 查看详情

万物皆可gan生成对抗网络生成手写数字part1(代码片段)

【万物皆可GAN】生成对抗网络生成手写数字Part1概述GAN网络结构GAN训练流程模型详解生成器判别器概述GAN(GenerativeAdversarialNetwork)即生成对抗网络.GAN网络包括一个生成器(Generator)和一个判别器(Discriminator).GAN可以自动提取特征,并判... 查看详情

手把手写c++服务器:专栏文章-汇总导航更新中

手把手写C++服务器(1):网络编程常见误区手把手写C++服务器(2):C/C++编译链接模型、函数重载隐患、头文件使用规范手把手写C++服务器(3):C++编译常见问题、编译优化方法、C++库发... 查看详情

手把手写c++服务器(36):手撕代码——高并发高qps技术基石之非阻塞recv万字长文(代码片段)

本系列文章导航: 手把手写C++服务器(0):专栏文章-汇总导航【更新中】 前言:没有什么东西是永恒,没有什么方案是万能,阻塞模式和非阻塞模式各有利弊。创建socket是默认阻塞的。但是在高并发多Q... 查看详情

手把手写c++服务器(37):手撕代码——高并发多线程技术基石之异步connect万字长文(代码片段)

本系列文章导航: 手把手写C++服务器(0):专栏文章-汇总导航【更新中】 前言:connect创建的时候是默认阻塞模式的,但是现实情况里可能会因为网络差、中间代理服务器、网关等因素造成连接速度慢。此... 查看详情

手把手写c++服务器(35):手撕代码——高并发高qps技术基石之非阻塞send万字长文(代码片段)

本系列文章导航: 手把手写C++服务器(0):专栏文章-汇总导航【更新中】  前言:创建socket是默认阻塞的。但是在高并发多QPS的场景中,阻塞模式会极大程度上影响并发性,使之并发名存实亡。而send函... 查看详情

手把手写c++服务器(28):手撕cgi通用网关接口服务器代码(代码片段)

 本系列文章导航:手把手写C++服务器(0):专栏文章-汇总导航【更新中】 前言:前文《手把手写C++服务器(26):常用I/O操作、创建文件描述符》《手把手写C++服务器(27):五大文件描述符零拷... 查看详情

手把手写c++服务器(29):手撕echo回射服务器代码(代码片段)

 本系列文章导航:手把手写C++服务器(0):专栏文章-汇总导航【更新中】 前言:上一讲《手把手写C++服务器(28):手撕CGI通用网关接口服务器代码》讲解了如何利用复制文件描述符dup重定位标准输出&#x... 查看详情

手把手写c++服务器(33):linux常用命令合集

 本系列文章导航: 手把手写C++服务器(0):专栏文章-汇总导航【更新中】 前言:服务端编程的过程当中,各种常用的命令行也会大量使用;熟悉常用Linux命令不仅仅是运维的基本要求,也是一个主程的基本门槛。这里... 查看详情

手把手写c++服务器(30):手撕代码——基于tcp/ip的抛弃服务discard(代码片段)

 本系列文章导航:手把手写C++服务器(0):专栏文章-汇总导航【更新中】 前言:前面两讲讲了echo服务器和CGI网关服务器《手把手写C++服务器(29):手撕echo回射服务器代码》《手把手写C++服务器(2... 查看详情

论文|万物皆可vector之word2vec:2个模型2个优化及实战使用

万物皆可Embedding系列会结合论文和实践经验进行介绍,前期主要集中在论文中,后期会加入实践经验和案例,目前已更新:万物皆可Vector之语言模型:从N-Gram到NNLM、RNNLM万物皆可Vector之Word2vec:2个模型、2... 查看详情

手把手写c++服务器(21):linuxsocket网络编程入门基础(代码片段)

本系列文章导航:手把手写C++服务器(0):专栏文章-汇总导航【更新中】前言:刚开始写C++服务器的时候,我们进行网络编程肯定是使用socketAPI,等熟练之后,会根据我们自己的需要,封装... 查看详情

手把手写c++服务器(22):linuxsocket网络编程进阶第一弹(代码片段)

前言:前面一篇文章手把手写C++服务器(21):Linuxsocket网络编程入门基础,讲解了如何建立socket连接、如何转换/使用socket地址、如何绑定/监听/发起/接受/断开/终止/关闭连接。socket博大精深,进阶会多写几弹&... 查看详情

手把手写c++服务器(22):linuxsocket网络编程进阶第一弹(代码片段)

前言:前面一篇文章手把手写C++服务器(21):Linuxsocket网络编程入门基础,讲解了如何建立socket连接、如何转换/使用socket地址、如何绑定/监听/发起/接受/断开/终止/关闭连接。socket博大精深,进阶会多写几弹&... 查看详情