在Linux *** 作系统中,可以将一切都看作是文件,包括普通文件,目录文件,字符设备文件(如键盘,鼠标…),块设备文件(如硬盘,光驱…),套接字等等,所有一切均抽象成文件,提供了统一的接口,方便应用程序调用。
既然在Linux *** 作系统中,你将一切都抽象为了文件,那么对于一个打开的文件,我应用程序怎么对应上呢?
文件描述符应运而生。
文件描述符:File descriptor,简称fd,当应用程序请求内核打开/新建一个文件时,内核会返回一个文件描述符用于对应这个打开/新建的文件,其fd本质上就是一个 非负整数 。实际上,它是一个索引值,指向 内核为每一个进程所维护的该进程打开文件的记录表。当程序打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符。 在程序设计中,一些涉及底层的程序编写往往会围绕着文件描述符展开。但是文件描述符这一概念往往只适用于UNIX、Linux这样的 *** 作系统。
*** 作系统的核心叫内核,是一个独立的软件。
*** 作系统为每一个进程维护了一个文件描述符表,该表的索引值都从从0开始的,所以在不同的进程中可以看到相同的文件描述符,这种情况下相同的文件描述符可能指向同一个文件,也可能指向不同的文件,具体情况需要具体分析,下面用一张简图就可以很容易的明白了。
通过上图可以看到,当不同进程中出现相同的文件描述符时,可能实际对应的文件并不是同一个,相反不同进程中不同的文件描述符也可可能对应同一个文件。
当一个应用程序刚刚启动的时候,0是标准输入,1是标准输出,2是标准错误。如果此时去打开一个新的文件,它的文件描述符会是3。POSIX标准要求每次打开文件时(含socket)必须使用当前进程中最小可用的文件描述符号。
文件描述符是一个重要的系统资源,理论上系统内存多大就应该可以打开多少个文件描述符,但是实际情况是,内核会有系统级限制,以及用户级限制(不让某一个应用程序进程消耗掉所有的文件资源,可以使用ulimit -n 查看)。
进程 + 文件描述符ID确认,因为内核为每个进程都有一份其所属的文件描述符表。
所以linux下两个进程返回的文件描述符是不一样的
多个进程之间的fd:
应用程序进程拿到的 文件描述符ID 对应 进程文件描述符表 的索引,通过索引拿到 文件指针 ,指向系统级文件描述符表的 文件偏移量 ,再通过文件偏移量找到 inode指针 ,最终对应到真实的文件。
在Linux下一切资源皆文件,普通文件是文件,磁盘打印机是文件,socket 当然也是文件。
关于Linux下系统,进程能最大能打开的文件描述符数看过好多文章,但大都没有完整,详细说明每个值表示什么意思,在实践中该怎么设置?
如何通过最简单的设置来实现最有效的性能调优,如何在有限资源的条件下保证程序的运作?
max-file 表示系统级别的能够打开的文件句柄的数量,是对整个系统的限制,并不是针对用户的。
ulimit -n 控制进程级别能够打开的文件句柄的数量,提供对shell及其启动的进程的可用文件句柄的控制,这是进程级别的。
对于服务器来说,file-max和ulimit都需要设置,否则会出现文件描述符耗尽的问题。
一般如果遇到文件句柄达到上限时,会碰到"Too many open files"或者Socket/File: Can’t open so many files等错误。
相关的3个文件:
/proc/sys/fs/file-max
/proc/sys/fs/file-nr
/etc/security/limits.conf
/proc/sys/fs/file-max
Linux系统级别限制所有用户进程能打开的文件描述符总数。
max-file 表示系统级别的能够打开的文件句柄的数量,是对整个系统的限制,并不是针对用户的。
/etc/security/limits.conf
用户级别的限制是通过可以通过命令ulimit命令和文件/etc/security/limits.conf
/proc/sys/fs/file-nr 该参数是只读的,不能修改。
file-nr的值由3部分组成:
1,已经分配的文件描述符数;
2,已经分配但未使用的文件描述符数;
3,内核最大能分配的文件描述符数
/proc/${pid}/fd
众所周知,在相应进程的/proc/$pid/fd 目录下存放了此进程所有打开的fd。
当然有些可能不是本进程自己打开的,如通过fork()从父进程继承而来的。
那么这个socket:后面的一串数字是什么呢?其实是该socket的inode号。
那么,知道了某个进程打开的socket的inode号后,我们可以做什么呢?
这就涉及到/proc/net/tcp(udp对应/proc/net/udp)文件了,其中也列出了相应socket的inode号通过比对此字段,我们能在/proc/net/tcp下获得此套接口的其他信息,如对应的<本地地址:端口号,远端地址:端口号>对,窗口大小,状态等信息。
具体字段含义详见net/ipv4/tcp_ipv4.c 中的 tcp4_seq_show 函数。
如果socket创建了,没有被使用,那么就只会在/proc/pid/fd下面有,而不会在/proc/net/下面有相关数据。
目录中的每一项都是一个符号链接,指向打开的文件,数字则代表文件描述符。
其中0 = /dev/null ,1 = stdout, 2 = stderr,用cat或tail查看即可。
Number of file descriptors: different between /proc/sys/fs/file-nr and /proc/$pid/fd?
https://serverfault.com/questions/485262/number-of-file-descriptors-different-between-proc-sys-fs-file-nr-and-proc-pi
Linux中最大文件描述符数
https://leokongwq.github.io/2016/11/09/linux-max-fd.html
How do linux file descriptor limits work?
https://stackoverflow.com/questions/3991223/how-do-linux-file-descriptor-limits-work
limits.conf(5) - Linux man page
https://linux.die.net/man/5/limits.conf
Why can't I tail -f /proc/$pid/fd/1 ?
https://unix.stackexchange.com/questions/152773/why-cant-i-tail-f-proc-pid-fd-1
Linux查看进程运行输出(/proc/<pid>/fd)
https://blog.csdn.net/u014756245/article/details/120023188
socket - 创建一个用于通信的端点
socket() 创建用于通信的端点并返回引用该端点的文件描述符。 成功调用时返回的文件描述符,将是当前没有被进程打开的所有文件描述符中编号最低的。
domain 参数指定一个通信域; 以决定用于通信的协议族。 这些系列在 <sys/socket.h>中定义。 目前 Linux 内核理解的格式包括:
当然最常用的当然是 AF_INET ,即IPV4。
上述地址族的更多详细信息以及其他几个地址族的信息可以在 address_families(7) 中找到。
套接字具有指定的 type ,它指定了通信语义。 当前定义的类型有:
某些套接字类型可能不会被所有协议族实现。
从 Linux 2.6.27 开始,type 参数有第二个用途:除了指定套接字类型之外,它还可以包含以下任何值的按位或,以修改 socket() 的行为:
老朋友了,上述两个,第一个是非阻塞,第二个是执行exec时自动关闭。
protocol 指定要与套接字一起使用的特定协议。 通常只存在一个协议来支持给定协议族中的特定套接字类型 ,在这种情况下,protocol 可以指定为 0。但是,可能存在许多协议,在这种情况下,必须在此指定特定协议方式。 特定协议对应的编号可以查看文件: /etc/protocols
SOCK_STREAM 类型的套接字是全双工字节流。 它们不保留记录边界。 流套接字必须处于连接状态,然后才能在其上发送或接收任何数据。 到另一个套接字的连接是通过 connect(2) 调用创建的。 连接后,可以使用 read(2) 和 write(2) 调用或 其变体send(2) 和 recv(2) 的来传输数据。 当会话完成时,可以执行 close(2)。 带外数据也可以按照 send(2) 中的描述进行传输,并按照 recv(2) 中的描述进行接收。
实现 SOCK_STREAM 的通信协议确保数据不会丢失或重复。 如果协议的缓冲空间中存在一条数据在合理的时间内不能成功传输,则认为该连接已失效。 当 SO_KEEPALIVE 在套接字上启用时,将会以特定于协议的方式检查另一端是否仍然存在。 如果进程在损坏的流上发送或接收,则会引发 SIGPIPE 信号; 这会导致不处理信号的进程退出。 SOCK_SEQPACKET 套接字使用与 SOCK_STREAM 套接字相同的系统调用。 唯一的区别是 read(2) 调用将只返回请求的数据量,到达数据包中剩余的其他数据都将被丢弃。 传入数据报中的所有消息边界也被保留。
SOCK_DGRAM 和 SOCK_RAW 套接字允许将数据报发送到在 sendto(2) 调用中指定的通信者。 数据报通常用 recvfrom(2) 接收,它返回下一个数据报及其发送者的地址。
SOCK_PACKET 是一种过时的套接字类型,用于直接从设备驱动程序接收原始数据包。 改用 packet(7)。
An fcntl(2) F_SETOWN operation can be used to specify a process or process group to receive a SIGURG signal when the out-of-band data arrives or SIGPIPE signal when a SOCK_STREAM connection breaks unexpectedly. This operation may also be used to set the process or process group that receives the I/O and asynchronous notification of I/O events via SIGIO. Using F_SETOWN is equivalent to an ioctl(2) call with the FIOSETOWN or SIOCSPGRP argument.
When the network signals an error condition to the protocol module (e.g., using an ICMP message for IP) the pending error flag is set for the socket. The next operation on this socket will return the error code of the pending error. For some protocols it is possible to enable a per-socket error queue to retrieve detailed information about the errorsee IP_RECVERR in ip(7).
套接字的 *** 作由套接字选项控制。 这些选项在 <sys/socket.h>中定义。 函数setsockopt(2) 和getsockopt(2) 用于设置和获取选项。对于选项的描述,详见socket(7).
成功时,将返回新套接字的文件描述符。 出错时,返回 -1,并设置 errno 以指示错误。
POSIX.1-2001, POSIX.1-2008, 4.4BSD.
The SOCK_NONBLOCK and SOCK_CLOEXEC flags are Linux-specific.
socket() appeared in 4.2BSD. It is generally portable to/from non-BSD systems supporting clones of the BSD socket layer (including System V variants).
在 4.x BSD 下用于协议族的清单常量是 PF_UNIX、PF_INET 等,而 AF_UNIX、AF_INET 等用于地址族。 但是,BSD 手册页已经承诺:“协议族通常与地址族相同”,随后的标准到处都使用 AF_*。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)