Linux中所有东西都是文件,一个socket就对应着一个文件描述符,因此系统配置的最大打开文件数以及单个进程能够打开的最大文件数就决定了socket的数目上限;但是linux是有文件句柄限制的,而且默认不是很高,一般都是1024,生产服务器用其实很容易就达到这个数量
一、limits.conf文件
工作原理
limits.conf文件实际是Linux PAM(插入式认证模块,Pluggable Authentication Modules 中 pam_limits.so 的配置文件),突破系统的默认限制,对系统访问资源有一定保护作用,当用户访问服务器时,服务程序将请求发送到PAM模块,PAM模块根据服务名称在/etc/pam.d
目录下选择一个对应的服务文件,然后根据服务文件的内容选择具体的PAM模块进行处理
limits.conf 和sysctl.conf区别在于limits.conf是针对用户,而sysctl.conf是针对整个系统参数配置
文件格式
1
|
username|@groupname type resource limit
|
1)username|@groupname
设置需要被限制的用户名,组名前面加@和用户名区别。也可用通配符*来做所有用户的限制
2)type
类型有soft,hard 和 -
-
soft 指的是当前系统生效的设置值
-
hard 表明系统中所能设定的最大值,soft 的限制不能比 hard 限制高
-
- 就表明同时设置了 soft 和 hard 的值
3)resource: 表示要限制的资源
-
core - 限制内核文件的大小
-
core file : 当一个程序崩溃时,在进程当前工作目录的core文件中复制了该进程的存储映像。core文件仅仅是一个内存映象(同时加上调试信息),主要是用来调试的。core文件是个二进制文件,需要用相应的工具来分析程序崩溃时的内存映像,系统默认core文件的大小为0,所以没有被创建。可以用ulimit命令查看和修改core文件的大小。 #ulimit -c 0 #ulimit -c 1000 #ulimit -c unlimited 注意:如果想让修改永久生效,则需要修改配置文件,如 .bash_profile、/etc/profile或/etc/security/limits.conf
-
date - 最大数据大小
-
fsize - 最大文件大小
-
memlock - 最大锁定内存地址空间
-
nofile - 打开文件的最大数目
-
对于需要做许多套接字连接并使它们处于打开状态的应用程序而言,最好通过使用ulimit -n,或者通过设置nofile参数,为用户把文件描述符的数量设置得比默认值高一些
-
rss - 最大持久设置大小
-
stack - 最大栈大小
-
cpu - 以分钟为单位的最多 CPU 时间
-
noproc - 进程的最大数目
-
as - 地址空间限制
-
maxlogins - 此用户允许登录的最大数目
示例
限制admin用户登录到sshd的服务不能超 过2个
1
2
|
echo session required pam_limits.so >> /etc/pam.d/sshd
echo admin - maxlogins 2 >> /etc/security/limits.conf
|
-
查看应用程序能否被PAM支持,用ldd
-
同理limits.conf要使用就必须保证/etc/pam.d/login 中有下面:session required pam_***.so
二、ulimit命令
ulimit 用于限制 shell 启动进程所占用的资源,支持以下各种类型的限制:所创建的内核文件的大小、进程数据块的大小、Shell 进程创建文件的大小、内存锁住的大小、常驻内存集的大小、打开文件描述符的数量、分配堆栈的最大大小、CPU 时间、单个用户的最大线程数、Shell 进程所能使用的最大虚拟内存,同时它支持硬资源和软资源的限制
作为临时限制,ulimit 可以作用于通过使用其命令登录的 shell 会话,在会话终止时便结束限制,并不影响于其他 shell 会话,对于长期的固定限制,ulimit 命令语句又可以被添加到由登录 shell 读取的文件中,作用于特定的 shell 用户,该命令总结如下:
-
一般只对当前tty(终端有效),若要每次都生效的话,可以把ulimit参数放到对应用户的.bash_profile里面
-
ulimit命令本身就有分软硬设置,加-H就是硬,加-S就是软
-
默认显示的是软限制,如果运行ulimit命令修改的时候没有加上的话,就是两个参数一起改变生效
ulimit命令用来限制系统用户对shell资源的访问,常用参数解释如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
ulimit(选项)
-a:显示目前资源限制的设定;
-c <core文件上限>:设定core文件的最大值,单位为区块;
-d <数据节区大小>:程序数据节区的最大值,单位为KB;
-f <文件大小>:shell所能建立的最大文件,单位为区块;
-H:设定资源的硬性限制,也就是管理员所设下的限制;
-m <内存大小>:指定可使用内存的上限,单位为KB;
-n <文件数目>:指定同一时间最多可开启的文件数;
-p <缓冲区大小>:指定管道缓冲区的大小,单位512字节;
-s <堆叠大小>:指定堆叠的上限,单位为KB;
-S:设定资源的弹性限制;
-t <CPU时间>:指定CPU使用时间的上限,单位为秒;
-u <程序数目>:用户最多可开启的程序数目;
-v <虚拟内存大小>:指定可使用的虚拟内存上限,单位为KB。
|
如何修改ulimit最大限制
1)使用命令修改
查询当前终端的文件句柄数: ulimit -n,一般的系统默认的1024;修改文件句柄数为65535,ulimit -n 65535
2)将ulimit 值添加到/etc/profile文件中(适用于有root权限登录的系统),为了每次系统重新启动时,都可以获取更大的ulimit值,将ulimit 加入到/etc/profile 文件底部
1
2
3
|
echo ulimit -n 65535 >>/etc/profile
source /etc/profile
ulimit -n
|
3)好多朋友都以为大功告成了,可以突然发现自己再次登录进来的时候,ulimit的值还是1024,这是为什么呢 ?
关键的原因是你登录的用户是什么身份,是不是root用户,由于服务器的root用户权限很大,一般是不能用来登录的,都是通过自己本人的登录权限进行登录,并通过sudo方式切换到root用户下进行工作。 用户登录的时候执行shell脚本的顺序:
1
2
3
4
5
|
/etc/profile.d/file
/etc/profile
/etc/bashrc
/home/.admin/.bashrc
/home/.admin/.bash_profile
|
由于ulimit -n的脚本命令加载在第二部分,用户登录时由于权限原因在第二步还不能完成ulimit的修改,所以ulimit的值还是系统默认的1024
解决办法: 修改linux的软硬件限制文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
echo
'
* soft nproc 11000
* hard nproc 11000
* soft nofile 655350
* hard nofile 655350' >>
/etc/security/limits
.conf
/*
这里针对的是所有用户
nproc 打开的最大进程数
nofile 打的开最大文件数
所有进程打开的文件描述符数不能超过
/proc/sys/fs/file-max
单个进程打开的文件描述符数不能超过user limit中nofile的soft limit
nofile的soft limit不能超过其hard limit
nofile的hard limit不能超过
/proc/sys/fs/nr_open
*/
|
4)经过以上修改,在有些系统中,用一般用户再登陆,仍然没有修改过来,那么需要检查是否有如下文件,如果没有,则要添加如下内容:
1
2
|
echo session required /lib/security/pam_limits.so >> /etc/pam.d/sshd
service sshd reload
|
5)如果仍然不行,那么需要修改如下文件
1
|
echo
UsePrivilegeSeparation no >>
/etc/ssh/sshd_config
|
三、File Descriptors的设置
设置多少合适?
a、/proc/sys/fs/file-max
决定了当前内核可以打开的最大的文件句柄数
The value in file-max denotes the maximum number of file handles that the Linux kernel will allocate. When you get a lot of error messages about running out of file handles, you might want to raise this limit. The default value is 10% of RAM in kilobytes. To change it, just write the new number into the file
意思是file-max一般为内存大小(KB)的10%来计算,如果使用shell,可以这样计算
1
|
grep -r MemTotal /proc/meminfo | awk
'{printf("%d",$2/10)}'
|
b、/proc/sys/fs/file-nr
file-nr 中的值由三部分组成,分别为:
-
已经分配的文件句柄数
-
已经分配单没有使用的文件句柄数
-
最大文件句柄数
第二项的值总为0,这并不是一个错误,它实际上意味着已经分配的文件句柄无一浪费的都已经被使用了
Historically, the three values in file-nr denoted the number of allocated file handles, the number of allocated but unused file handles, and the maximum number of file handles. Linux 2.6 always reports 0 as the number of free file handles -- this is not an error, it just means that the number of allocated file handles exactly matches the number of used file handles.
查找文件句柄问题的时候,还有一个很实用的程序lsof,可以很方便看到某个进程开了那些句柄,也可以看到某个文件/目录被什么进程占用了
1
|
lsof -n |awk
'{print $2}'
|sort|uniq -c |sort -nr|more
|
这两个参数可以让你来决定设置多少比较合适
如何设置?
系统级别的设置
1
2
3
4
5
6
7
8
|
sysctl -w fs.
file
-max=100000
//
临时生效
vi
/etc/sysctl
.conf +fs.
file
-max = 100000
//
永久生效
//
查看
cat
/proc/sys/fs/file-max
or
sysctl fs.
file
-max
|
用户级别的设置
-
在/etc/security/limits.conf文件,可以对系统用户组,进行cpu、文件数等限制的,通过它可以针对某个用户或全部进行限制
-
(*表示所有用户、soft表示可以超出,但只是警告;hard表示绝对不能超出,unlimited用于表示不限制)也可以放在/etc/profile文件里面,下面是该文件里面的默认参数:ulimit -S -c 0 > /dev/null 2>&1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
vi
/etc/security/limits
.conf
//
添加
httpd soft nofile 4096
httpd hard nofile 10240
//
查看
su
- httpd
ulimit
-Hn
ulimit
-Sn
//
如果不生效
/etc/pam
.d
/login
中加上pam_limts.so文件 +session required pam_limits.so
|
ulimit -n and /proc/sys/fs/file-max 有什么区别?
1、file-max 是内核级别的,所有的进程总和不能超过这个数
2、ulimit是进程级别的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
[root@dev ~]
# ulimit -n
1024
[root@dev ~]
# lsof | grep me | wc -l
8145
#!/usr/bin/perl
$count = 0;
@filedescriptors;
while
($count <= 1024) {
$FILE = ${count};
open
$FILE,
">"
,
"/tmp/example$count"
or die
"\n\n FDs: $count $!"
;
push(@filedescriptors, $FILE);
$count ++;
}
//FDs
: 1021 Too many
open
files at .
/test
.pl line 8.
//1021
because there were 3
open
file
descriptors before reaching the
while
loop (stdout, stdin and stderr)
|
三、关闭IPV6
目前为止我们还不需要IPv6,系统安装完之后是自带并且开启了的,需要我们关闭
1
|
ifconfig | grep inet6 || lsmod | grep ipv6
|
1、如果出现inet6 addr…的字样,说明就是安装了
2、显示内核加载的ipv6相关模块
通过以下命令禁用
1
2
3
4
5
6
7
|
sed -i
's/^NETWORKING_IPV6=yes/NETWORKING_IPV6=no/'
/etc/sysconfig/network
echo '
alias net-pf-10 off
alias ipv6 off ' >> /etc/modprobe.d/dist.conf
chkconfig ip6tables off
|
四、sysctl.conf的配置
sysctl命令被用于在内核运行时动态地修改内核的运行参数,可用的内核参数在目录/proc/sys
中。它包含一些TCP/IP堆栈和虚拟内存系统的高级选项, 这可以让有经验的管理员提高引人注目的系统性能。用sysctl可以读取设置超过五百个系统变量
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
|
#禁用包过滤功能
net.ipv4.ip_forward = 0
#启用源路由核查功能
net.ipv4.conf.
default
.rp_filter = 1
#禁用所有IP源路由
net.ipv4.conf.
default
.accept_source_route = 0
#使用sysrq组合键是了解系统目前运行情况,为安全起见设为0关闭
kernel.sysrq = 0
#控制core文件的文件名是否添加pid作为扩展
kernel.core_uses_pid = 1
#开启SYN Cookies,当出现SYN等待队列溢出时,启用cookies来处理
net.ipv4.tcp_syncookies = 1
#每个消息队列的大小(单位:字节)限制
kernel.msgmnb = 65536
#整个系统最大消息队列数量限制
kernel.msgmax = 65536
#单个共享内存段的大小(单位:字节)限制,计算公式64G*1024*1024*1024(字节)
kernel.shmmax = 68719476736
#所有内存大小(单位:页,1页 = 4Kb),计算公式16G*1024*1024*1024/4KB(页)
kernel.shmall = 4294967296
#timewait的数量,默认是180000
net.ipv4.tcp_max_tw_buckets = 6000
#开启有选择的应答
net.ipv4.tcp_sack = 1
#支持更大的TCP窗口. 如果TCP窗口最大超过65535(64K), 必须设置该数值为1
net.ipv4.tcp_window_scaling = 1
#TCP读buffer
net.ipv4.tcp_rmem = 4096 131072 1048576
#TCP写buffer
net.ipv4.tcp_wmem = 4096 131072 1048576
#为TCP socket预留用于发送缓冲的内存默认值(单位:字节)
net.core.wmem_default = 8388608
#为TCP socket预留用于发送缓冲的内存最大值(单位:字节)
net.core.wmem_max = 16777216
#为TCP socket预留用于接收缓冲的内存默认值(单位:字节)
net.core.rmem_default = 8388608
#为TCP socket预留用于接收缓冲的内存最大值(单位:字节)
net.core.rmem_max = 16777216
#每个网络接口接收数据包的速率比内核处理这些包的速率快时,允许送到队列的数据包的最大数目
net.core.netdev_max_backlog = 262144
#web应用中listen函数的backlog默认会给我们内核参数的net.core.somaxconn限制到128,而nginx定义的NGX_LISTEN_BACKLOG默认为511,所以有必要调整这个值
net.core.somaxconn = 262144
#系统中最多有多少个TCP套接字不被关联到任何一个用户文件句柄上。这个限制仅仅是为了防止简单的DoS攻击,不能过分依靠它或者人为地减小这个值,更应该增加这个值(如果增加了内存之后)
net.ipv4.tcp_max_orphans = 3276800
#记录的那些尚未收到客户端确认信息的连接请求的最大值。对于有128M内存的系统而言,缺省值是1024,小内存的系统则是128
net.ipv4.tcp_max_syn_backlog = 262144
#时间戳可以避免序列号的卷绕。一个1Gbps的链路肯定会遇到以前用过的序列号。时间戳能够让内核接受这种“异常”的数据包。这里需要将其关掉
net.ipv4.tcp_timestamps = 0
#为了打开对端的连接,内核需要发送一个SYN并附带一个回应前面一个SYN的ACK。也就是所谓三次握手中的第二次握手。这个设置决定了内核放弃连接之前发送SYN+ACK包的数量
net.ipv4.tcp_synack_retries = 1
#在内核放弃建立连接之前发送SYN包的数量
net.ipv4.tcp_syn_retries = 1
#开启TCP连接中time_wait sockets的快速回收
net.ipv4.tcp_tw_recycle = 1
#开启TCP连接复用功能,允许将time_wait sockets重新用于新的TCP连接(主要针对time_wait连接)
net.ipv4.tcp_tw_reuse = 1
#1st低于此值,TCP没有内存压力,2nd进入内存压力阶段,3rdTCP拒绝分配socket(单位:内存页)
net.ipv4.tcp_mem = 94500000 915000000 927000000
#如果套接字由本端要求关闭,这个参数决定了它保持在FIN-WAIT-2状态的时间。对端可以出错并永远不关闭连接,甚至意外当机。缺省值是60 秒。2.2 内核的通常值是180秒,你可以按这个设置,但要记住的是,即使你的机器是一个轻载的WEB服务器,也有因为大量的死套接字而内存溢出的风险,FIN- WAIT-2的危险性比FIN-WAIT-1要小,因为它最多只能吃掉1.5K内存,但是它们的生存期长些。
net.ipv4.tcp_fin_timeout = 15
#表示当keepalive起用的时候,TCP发送keepalive消息的频度(单位:秒)
net.ipv4.tcp_keepalive_time = 30
#对外连接端口范围
net.ipv4.ip_local_port_range = 2048 65000
#表示文件句柄的最大数量
fs.file-max = 102400
|
/sbin/sysctl -p最后记得刷新立即生效
参考与转载:
http://www.cnblogs.com/chenpingzhao/p/4975956.html
根据上述的文章,出现一个问题
- 关于TIME_WAIT数量太多。从上面的描述我们可以知道,TIME_WAIT是个很重要的状态,但是如果在大并发的短链接下,TIME_WAIT 就会太多,这也会消耗很多系统资源。只要搜一下,你就会发现,十有八九的处理方式都是教你设置两个参数,一个叫tcp_tw_reuse,另一个叫tcp_tw_recycle的参数,这两个参数默认值都是被关闭的,后者recyle比前者resue更为激进,resue要温柔一些。另外,如果使用tcp_tw_reuse,必需设置tcp_timestamps=1,否则无效。这里,你一定要注意,打开这两个参数会有比较大的坑——可能会让TCP连接出一些诡异的问题(因为如上述一样,如果不等待超时重用连接的话,新的连接可能会建不上。正如官方文档上说的一样“It should not be changed without advice/request of technical experts”)。
- 关于tcp_tw_reuse。官方文档上说tcp_tw_reuse 加上tcp_timestamps(又叫PAWS, for Protection Against Wrapped Sequence Numbers)可以保证协议的角度上的安全,但是你需要tcp_timestamps在两边都被打开(你可以读一下tcp_twsk_unique的源码 )。我个人估计还是有一些场景会有问题。
- 关于tcp_tw_recycle。如果是tcp_tw_recycle被打开了话,会假设对端开启了tcp_timestamps,然后会去比较时间戳,如果时间戳变大了,就可以重用。但是,如果对端是一个NAT网络的话(如:一个公司只用一个IP出公网)或是对端的IP被另一台重用了,这个事就复杂了。建链接的SYN可能就被直接丢掉了(你可能会看到connection time out的错误)(如果你想观摩一下Linux的内核代码,请参看源码 tcp_timewait_state_process)。
- 关于tcp_max_tw_buckets。这个是控制并发的TIME_WAIT的数量,默认值是180000,如果超限,那么,系统会把多的给destory掉,然后在日志里打一个警告(如:time wait bucket table overflow),官网文档说这个参数是用来对抗DDoS攻击的。也说的默认值180000并不小。这个还是需要根据实际情况考虑。
Again,使用tcp_tw_reuse和tcp_tw_recycle来解决TIME_WAIT的问题是非常非常危险的,因为这两个参数违反了TCP协议(RFC 1122)
请注意上面的两个参数
请参考TCP的那些事儿
参考
https://coolshell.cn/articles/11564.html