1 、 BIOS
你应该总是在系统启动的时候设置一个BIOS 密码和禁用从CD-ROM 和软盘引导,这将可以防止一些人未经允许访问你的系统和更改BIOS 设置
2 、sshd 服务
SSH 是一个协议,利用它可以登录到一个远程系统或远程执行系统命令,默认允许root 登录,并且sshv1 存在缺陷,我们应该在sshd_config 禁止root 访问和使用sshv2 来让ssh 更加安全
#vi /etc/ssh/sshd_config
Protocol 2
PermitRootLogin = no
#sshd /etc/rc.d/init.d/sshd restart
3 、 禁用 telnet
早期的Linux 默认开启telnet 服务,telnet,ftp,rlogin 都是明文传输的协议是容易被嗅探到的,这就是为什么推荐使用安全的版本(sftp,scp,ssh )的原因,如果你必须要使用telnet ,那么至少应该隐藏banner 信息
#vi /etc/xinetd.d/telnet
disable=yes
4 、 禁用代码编译
你可以禁用代码编译并且只把编译的权限分配给一个用户组
# 该组的成员可以编译代码
/usr/sbin/groupadd compiler
# 把常见的编译器所属组赋给编译用户组
chgrp compiler /usr/bin/*cc*
chgrp compiler /usr/bin/*++*
chgrp compiler /usr/bin/ld
chgrp compiler /usr/bin/as
# 设置mysqlaccess 的 访问
chgrp root mysqlaccess
# 设置权限
chmod 750 /usr/bin/*cc*
chmod 750 /usr/bin/*++*
chmod 750 /usr/bin/ld
chmod 750 /usr/bin/as
chmod 755 /usr/bin/mysqlaccess
# 把用户添加到组里
vi /etc/group
compiler:x:520:user1,user2
5 、ProFTP
# 修改proftpd.conf 来禁止root 登陆
vi /etc/proftpd.conf
Add RootLogin off
/sbin/service proftpd restart
6 、TCP wrappers
编辑hosts.allow 和hosts.deny 可以限制或允许访问inet 服务
vi /etc/hosts.allow
#Approved IP addresses
ALL:192.168.0.1
ALL:192.168.5.2
#CSV uploader machine
proftpd:10.0.0 .5
#pop3 from antwhere
ipop3:ALL
vi /etc/hosts.deny
ALL:ALL EXCEPT localhostENY
7 、创建su 用户组
因为我们在SSH 禁止了root 用户访问并且禁用了telnet ,所有我们应该分配给一些用户su 权限来获取root 特权
vi /etc/group ,添加一行如下
wheel:x:10:root,user1,user2
chgrp wheel /bin/su
chmod o-rwx /bin/su
说明:或者直接使用pam 模块来做,再则也可以编译/etc/sudoers 来做
8 、root 通知
当一个具有root 权限的用户登录的时候发mail ,具体实施方法如下:
vi /root/.bashrc ,当有root 权限的用户登录时发生email 通知
echo ‘ALERT – Root Shell Access (Server Name) on:’ `date` `who` | mail -s “Alert: Root Access from `who | cut -d”(” -f2 | cut -d”)” -f1`” your@email.com
9 、history 安全
这是一个避免删除.bash_history 或重定向到/dev/null 的好主意,因此他不能清除或删除他最后执行的命令
chattr +a .bash_history
chattr +i .bash_history
获取用户的人会知道他的历史命令锁定并且要同意才可以使用服务
10 、使用欢迎信息
你必须提供一些信息让攻击者知道该系统不对公众开放,在国外有类似案件,攻击者入侵一个系统并且系统没有这些信息,这种情况下法院不能做任何裁决,因为系统说welcome
1 )删除/etc/redhat-release
2 )编辑/etc/issue 与/etc/motd 并显示警告信息
11 、禁用所有特殊账户
# 你应该从系统中删除所有默认用户和组
例如news,lp,sync,shutdown,uucp,games,halt 等
userdel name
groupdel name
# 锁定特定账户
/usr/sbin/usermod -L -s /bin/false user
12 、chmod 危险文件
这可能是限制不具有root 权限的用户执行下面这些命令的好主意
chmod 700 /bin/ping
chmod 700 /usr/bin/finger
chmod 700 /usr/bin/who
chmod 700 /usr/bin/w
chmod 700 /usr/bin/locate
chmod 700 /usr/bin/whereis
chmod 700 /sbin/ifconfig
chmod 700 /usr/bin/pico
chmod 700 /usr/bin/vi
chmod 700 /usr/bin/which
chmod 700 /usr/bin/gcc
chmod 700 /usr/bin/make
chmod 700 /bin/rpm
13 、指定允许root 登陆的TTY 设备
因为/etc/securetty 文件允许你指定root 可以从哪个TTY 设备登录
vi /etc/securetty ,然后只留2 个连接
tty1
tty2
14 、选择一个安全的密码
在/etc/login.defs 文件中定义了shadow 密码的具体配置,默认密码长度最短为5 字符,你应该至少设置为8
vi /etc/login.defs
PASS_MIN_LEN 8
15 、检测Rootkit
用chkrootkit 或rkhunter ,以chkrootkit 为例
wget ftp://ftp.pangeia.com.br/pub/seg/pac/chkrootkit.tar.gz
wget ftp://ftp.pangeia.com.br/pub/seg/pac/chkrootkit.md5
md5sum chkrootkit.tar.gz # 首先检查md5 校验值
tar -zxvf chkrootkit.tar.gz
cd chkrootkit
./configure && make sense
然后运行./chkrootkit
我们可以将其添加到contrab 使其每天自动扫描:
vi /etc/cron.daily/chkrootkit.sh
#!/bin/bash
# 输入chkrootkit 的安装目录
cd /root/chkrootkit/
# 输入你想收到检测报告的email
./chkrootkit | mail -s “Daily chkrootkit from Server Name” your@email.com
16 、安装补丁
你要经常检查更新以修复某些缺陷或系统稳定性的改进,否则你存在漏洞的系统将会不时的遭受新的攻击
# 列出可用更新
up2date -l
# 安装未排除的更新
up2date -u
# 安装包括排除的更
up2date -uf
17 、隐藏Apache 信息
你应该隐藏Apache 的banner 信息使攻击者不知道Apache 的版本,从而使他们难以利用漏洞
vi /etc/httpd/conf/httpd.conf
# 改变服务器签名
ServerSignature Off
service httpd restart
18 、隐藏php 信息
你应该隐藏php 的banner 信息,原因同上
vi php.ini
expose_php=Off
service apache restart
19 、关闭不用的服务
你应该把任何未使用的服务关闭,可以在/etc/xinetd.d 文件夹里找到
cd /etc/xinetd.d
grep disable * | column -t
这将显示所有服务开启或关闭的状态,然后根据需要来开启或关闭服务
20 、检测监听的端口
检测是否有必要开放端口是非常重要的
netstat -tulp 或 lsof -i -n | egrep ‘COMMAND|LISTEN|UDP’ 或 nmap !
21 、关闭端口和服务
重点是关闭在系统启动时打开的不需要的端口,方法如下:
# 对于正在运行的服务,可以执行
chkconfig -list | grep on
# 禁用服务可以执行
chkconfig servicename off
# 然后停止正在运行的服务
/etc/init.d/service stop
22 、删除不用的rpm 包
首先应该清楚你的系统的作用,它是web,mail,file 服务器或其他,然后觉得哪些包是必要的,之后删除不需要的软件包,方法如下:
# 首先列出安装列表
rpm -qa
# 更详细的信息
rpm -qi rpmname
# 还可以检测删除包可能出现的冲突
rpm -e --test rpmname
23 、禁用危险的php 函数
你应该禁用php 的危险函数防止在网站上执行系统命令whereis php.ini
vi /usr/local/lib/php.ini
disable_functions = “symlink,shell_exec,exec,proc_close,proc_open,popen,
system,dl,passthru,escapeshellarg, escapeshellcmd”
24 、安装配置防火墙
高级策略防火墙(APF )是一种IP 表(网络过滤),它是基于当今互联网部署服务器防火墙系统的基本需要和客户部署LINUX 安装的唯一需要而设计的。 它是最好的开源防火墙之一
1 )配置APF 防火墙方法:
wget http://www.r-fx.ca/downloads/apf-current.tar.gz
tar -zxvf apf-current.tar.gz
cd apf-0.9.7 -1
./install.sh
2 )vi /etc/apf/conf.apf ,一般配置如下
启用防火墙使用DShield.org 块列表
USE_DS=”1″
然后我将列出常规的配置和CPanel 配置方式,因为CPanel 是应该最广泛的虚拟主机管理软件
(1 )常规配置(DNS,Mail,Web,FTP)
Common ingress (inbound)
# Common ingress (inbound) TCP ports -3000_3500 = passive port range for Pure FTPD IG_TCP_CPORTS=”21,22,25,53,80,110,143,443,995″
# Common ingress (inbound) UDP ports IG_UDP_CPORTS=”53″
# Egress filtering [0 = Disabled / 1 = Enabled]
EGF=”1″
# Common egress (outbound) TCP ports
EG_TCP_CPORTS=”21,25,80,443,43″
# Common egress (outbound) UDP ports
EG_UDP_CPORTS=”20,21,53″
(2 )CPanel 配置
Common ingress (inbound) ports
# Common ingress (inbound) TCP ports -3000_3500 = passive port range for Pure FTPD IG_TCP_CPORTS=”21,22,25,53,80,110,143,443,2082,2083, 2086,2087,2095, 2096,3000_3500″
# Common ingress (inbound) UDP ports
IG_UDP_CPORTS=”53″
Common egress (outbound) ports
# Egress filtering [0 = Disabled / 1 = Enabled]
EGF=”1″
# Common egress (outbound) TCP ports
EG_TCP_CPORTS=”21,25,80,443,43,2089″
# Common egress (outbound) UDP ports
EG_UDP_CPORTS=”20,21,53″
之后启动防火墙:/etc/apf/apf -s ,如果运行良好我在回去修改配置文件,使DEVM=”0″
(3 )配置APF 的AntiDos
vi /etc/apf/ad/conf.antidos ,找到下面的内容并替换成你的资料
# Organization name to display on outgoing alert emails
CONAME=”Your Company”
# Send out user defined attack alerts [0=off,1=on]
USR_ALERT=”0″
# User for alerts to be mailed to
USR=you@yourco.com
你应把USR_ALERT 改为1
保存后重启APF :/etc/apf/apf –r
To make the firewall start with the Operating System: chkconfig –level 2345 apf on
APF 开机自启动:chkconfig –level 2345 apf on
禁止一个IP 用/etc/apf/apf -d ip 或vi /etc/apf/deny_hosts.rules
允许一个IP 用/etc/apf/apf -a ip 或vi /etc/apf/deny_hosts.rules
25 、安装配置BFD (暴力破解检测)
BFD 是一个用于分析应用日志和检测验证失败的模块化shell 脚本,而且安装配置和用法都是非常容易的。使用BFD 的原因很简单,其实在LINUX 领域几乎没有结合防火墙或实时设备来监控不验证和暴力攻击审计的程序。在用BFD 之前你必须安装APF 防火墙
wget http://www.r-fx.ca/downloads/bfd-current.tar.gz
tar -zxvf bfd-current.tar.gz
cd bfd-0.9
vi /usr/local/bfd/conf.bfd
# 把以下内容改为你的资料
# Enable/disable user alerts [0 = off; 1 = on]
ALERT_USR=”1″
# User alert email address
EMAIL_USR=”your@mail.com”
# User alert email; subject
SUBJ_USR=”Brute Force Warning for $HOSTNAME”
vi /usr/local/bfd/ignore.hosts ,然后把你的IP 设置成允许主机,避免意外的锁定自己
# 重启BFD
/usr/local/sbin/bfd -s
26 、内核加固(sysctl.conf )
sysctl.conf 用来加固内核,目的是避免DOS 和欺骗攻击,具体的方法如下:
# 了解下当前配置的大概情况
sysctl -a
vi /etc/sysctl.conf
添加如下内容:
# Kernel sysctl configuration file for Red Hat Linux
# For binary values, 0 is disabled, 1 is enabled. See sysctl(8) and
# sysctl.conf(5) for more details.
# Controls IP packet forwarding
net.ipv4.ip_forward = 0
# Controls source route verification
net.ipv4.conf.default.rp_filter = 1
# Controls the System Request debugging functionality of the kernel
kernel.sysrq = 0
# Controls whether core dumps will append the PID to the core filename.
# Useful for debugging multi-threaded applications.
kernel.core_uses_pid = 1
#Prevent SYN attack
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_max_syn_backlog = 2048
net.ipv4.tcp_synack_retries = 2
# Disables packet forwarding
net.ipv4.ip_forward=0
# Disables IP source routing
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.conf.lo.accept_source_route = 0
net.ipv4.conf.eth0.accept_source_route = 0
net.ipv4.conf.default.accept_source_route = 0
# Enable IP spoofing protection, turn on source route verification
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.lo.rp_filter = 1
net.ipv4.conf.eth0.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
# Disable ICMP Redirect Acceptance
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.lo.accept_redirects = 0
net.ipv4.conf.eth0.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
# Enable Log Spoofed Packets, Source Routed Packets, Redirect Packets
net.ipv4.conf.all.log_martians = 1
net.ipv4.conf.lo.log_martians = 1
net.ipv4.conf.eth0.log_martians = 1
# Disables IP source routing
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.conf.lo.accept_source_route = 0
net.ipv4.conf.eth0.accept_source_route = 0
net.ipv4.conf.default.accept_source_route = 0
# Enable IP spoofing protection, turn on source route verification
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.lo.rp_filter = 1
net.ipv4.conf.eth0.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
# Disable ICMP Redirect Acceptance
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.lo.accept_redirects = 0
net.ipv4.conf.eth0.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
# Disables the magic-sysrq key
kernel.sysrq = 0
# Modify system limits for Ensim WEBppliance
fs.file-max = 65000
# Decrease the time default value for tcp_fin_timeout connection
net.ipv4.tcp_fin_timeout = 15
# Decrease the time default value for tcp_keepalive_time connection
net.ipv4.tcp_keepalive_time = 1800
# Turn off the tcp_window_scaling
net.ipv4.tcp_window_scaling = 0
# Turn off the tcp_sack
net.ipv4.tcp_sack = 0
# Turn off the tcp_timestamps
net.ipv4.tcp_timestamps = 0
# Enable TCP SYN Cookie Protection
net.ipv4.tcp_syncookies = 1
# Enable ignoring broadcasts request
net.ipv4.icmp_echo_ignore_broadcasts = 1
# Enable bad error message Protection
net.ipv4.icmp_ignore_bogus_error_responses = 1
# Log Spoofed Packets, Source Routed Packets, Redirect Packets
net.ipv4.conf.all.log_martians = 1
# Set maximum amount of memory allocated to shm to 256MB
kernel.shmmax = 268435456
# Improve file system performance
vm.bdflush = 100 1200 128 512 15 5000 500 1884 2
# Improve virtual memory performance
vm.buffermem = 90 10 60
# Increases the size of the socket queue (effectively, q0).
net.ipv4.tcp_max_syn_backlog = 1024
# Increase the maximum total TCP buffer-space allocatable
net.ipv4.tcp_mem = 57344 57344 65536
# Increase the maximum TCP write-buffer-space allocatable
net.ipv4.tcp_wmem = 32768 65536 524288
# Increase the maximum TCP read-buffer space allocatable
net.ipv4.tcp_rmem = 98304 196608 1572864
# Increase the maximum and default receive socket buffer size
net.core.rmem_max = 524280
net.core.rmem_default = 524280
# Increase the maximum and default send socket buffer size
net.core.wmem_max = 524280
net.core.wmem_default = 524280
# Increase the tcp-time-wait buckets pool size
net.ipv4.tcp_max_tw_buckets = 1440000
# Allowed local port range
net.ipv4.ip_local_port_range = 16384 65536
# Increase the maximum memory used to reassemble IP fragments
net.ipv4.ipfrag_high_thresh = 512000
net.ipv4.ipfrag_low_thresh = 446464
# Increase the maximum amount of option memory buffers
net.core.optmem_max = 57344
# Increase the maximum number of skb-heads to be cached
net.core.hot_list_length = 1024
## DO NOT REMOVE THE FOLLOWING LINE!
## nsobuild:20051206
# 重启后生效
/sbin/sysctl -p
sysctl -w net.ipv4.route.flush=1
27 、更改SSH 端口
更改SSH 默认端口号在一定程度上可以提高安全性
vi /etc/ssh/sshd_config
Port 22 改为其他端口
当然不要忘记把更改的端口加进防火墙,然后重启生效:/etc/init.d/ssh restart ,如果安装了APF 并把端口添加之后,还要重启APF:/etc/init.d/apf restart
28 、/tmp,/var/tmp,/dev/shm 分区的安全
/tmp,/var/tmp,/dev/shm 目录是不安全的,任何用户都可以执行脚本,最好的解决办法是挂载nocexec 和nosuid 选项的参数( 注意:不建议在CPanel 使用)
1 )/tmp 目录
cd /dev
# 创建 100M (“count”) 的存储文件:
dd if=/dev/zero of=tmpMnt bs=1024 count=100000
# 设为一个扩展的文件系统
/sbin/mke2fs /dev/tmpMnt (“…is not a block special device. continue?” 回答yes)
# 备份现有临时文件
cp -R /tmp/ /tmp_backup
# 用noexec 挂载新文件系统:
mount -o loop,rw,nosuid,noexec /dev/tmpMnt /tmp
chmod 0777 /tmp
# 把备份的文件拷贝回去
cp -R /tmp_backup/* /tmp/
# 删除备份
rm -rf /tmp_backup
# 修改/etc/fstab 添加下面的条目使其在引导时仍然有效
/dev/tmpMnt /tmp ext2 loop,rw,nosuid,noexec 0 0
2 )/var/tmp 目录
mv /var/tmp /var/tmpbak
ln -s /tmp /var/tmp
cp /var/tmpbak/* /tmp/
3 )/dev/shm 目录
vi /etc/fstab
"none /dev/shm tmpfs defaults,rw 0 0" 改为 "none /dev/shm tmpfs defaults,nosuid,noexec,rw 0 0"
在TimesTen的优化中,如果涉及到 Replication 或者Cache Group的话,那么针对 TCP/IP相关的参数的优化对性能是有益的。因为它们俩都是通过TCP/IP协议进行数据交互的。在安装文档(install.pdf)的39页有具体的说明:
For replication, TCP send and receive buffers should be increased to a minimum of 512KB. To make these changes, add the lines:
net.ipv4.tcp_rmem=”4096 4194304 4194304″
net.ipv4.tcp_wmem=”98304 4194304 4194304″
net.ipv4.tcp_mem=”98304 4194304 4194304″
net.core.rmem_default=65535
net.core.wmem_default=65535
net.core.rmem_max=4194304
net.core.wmem_max=4194304
net.ipv4.tcp_window_scaling=1
to the /etc/sysctl.conf file and reboot.
For Cache Connect, TCP send and receive buffers should be increased to even greater values. To make these changes, add the lines:
net.ipv4.tcp_rmem=”4096 4194304 4194304″
net.ipv4.tcp_wmem=”98304 4194304 4194304″
net.ipv4.tcp_mem=”98304 4194304 4194304″
net.core.rmem_default=262144
net.core.wmem_default=262144
net.core.rmem_max=4194304
net.core.wmem_max=4194304
net.ipv4.tcp_window_scaling=1
net.ipv4.ip_local_port_range=”1024 65000″
to the /etc/sysctl.conf file and reboot.(方法: 使用 /etc/sysctl.conf 在系统启动时将参数配置成您所设置的值:)
另外一个方法:
把下面代码增加到/etc/rc.local文件, 然后保存文件, 系统重新引导的时候会自动修改下面的TCP/IP参数:
echo 256960 > /proc/sys/net/core/rmem_default
echo 256960 > /proc/sys/net/core/rmem_max
echo 256960 > /proc/sys/net/core/wmem_default
echo 256960 > /proc/sys/net/core/wmem_max
echo 0 > /proc/sys/net/ipv4/tcp_timestamps
echo 1 > /proc/sys/net/ipv4/tcp_sack
echo 1 > /proc/sys/net/ipv4/tcp_window_scaling
下面是一篇参考文章,可能会有些帮助,引用于IBM。
使用 Sockets API,我们可以开发客户机和服务器应用程序,它们可以在本地网络上进行通信,也可以通过 Internet 在全球范围内进行通信。与其他 API 一样,您可以通过一些方法使用Sockets API,从而提高 Socket 的性能,或者限制 Socket 的性能。本文探索了 4 种使用 Sockets API 来获取应用程序的最大性能并对 GNU/Linux® 环境进行优化从而达到最好结果的方法。
在开发 socket 应用程序时,首要任务通常是确保可靠性并满足一些特定的需求。利用本文中给出的 4 个提示,您就可以从头开始为实现最佳性能来设计并开发 socket 程序。本文内容包括对于 Sockets API 的使用、两个可以提高性能的 socket 选项以及 GNU/Linux 优化。
为了能够开发性能卓越的应用程序,请遵循以下技巧:
•最小化报文传输的延时。
•最小化系统调用的负载。
•为 Bandwidth Delay Product 调节 TCP 窗口。
•动态优化 GNU/Linux TCP/IP 栈。
技巧 1. 最小化报文传输的延时
在通过 TCP socket 进行通信时,数据都拆分成了数据块,这样它们就可以封装到给定连接的 TCP payload(指 TCP 数据包中的有效负荷)中了。TCP payload 的大小取决于几个因素(例如最大报文长度和路径),但是这些因素在连接发起时都是已知的。为了达到最好的性能,我们的目标是使用尽可能多的可用数据来填充每个报文。当没有足够的数据来填充 payload 时(也称为最大报文段长度(maximum segment size) 或 MSS),TCP 就会采用 Nagle 算法自动将一些小的缓冲区连接到一个报文段中。这样可以通过最小化所发送的报文的数量来提高应用程序的效率,并减轻整体的网络拥塞问题。
尽管 John Nagle 的算法可以通过将这些数据连接成更大的报文来最小化所发送的报文的数量,但是有时您可能希望只发送一些较小的报文。一个简单的例子是 telnet 程序,它让用户可以与远程系统进行交互,这通常都是通过一个 shell 来进行的。如果用户被要求用发送报文之前输入的字符来填充某个报文段,那么这种方法就绝对不能满足我们的需要。
另外一个例子是 HTTP 协议。通常,客户机浏览器会产生一个小请求(一条 HTTP 请求消息),然后 Web 服务器就会返回一个更大的响应(Web 页面)。
解决方案
您应该考虑的第一件事情是 Nagle 算法满足一种需求。由于这种算法对数据进行合并,试图构成一个完整的 TCP 报文段,因此它会引入一些延时。但是这种算法可以最小化在线路上发送的报文的数量,因此可以最小化网络拥塞的问题。
但是在需要最小化传输延时的情况中,Sockets API 可以提供一种解决方案。要禁用 Nagle 算法,您可以设置 TCP_NODELAY socket 选项,如清单 1 所示。
清单 1. 为 TCP socket 禁用 Nagle 算法
int sock, flag, ret;
/* Create new stream socket */
sock = socket( AF_INET, SOCK_STREAM, 0 );
/* Disable the Nagle (TCP No Delay) algorithm */
flag = 1;
ret = setsockopt( sock, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, sizeof(flag) );
if (ret == -1) {
printf(“Couldn’t setsockopt(TCP_NODELAY)/n”);
exit(-1);
}
提示:使用 Samba 的实验表明,在从 Microsoft® Windows® 服务器上的 Samba 驱动器上读取数据时,禁用 Nagle 算法几乎可以加倍提高读性能。
技巧 2. 最小化系统调用的负载
任何时候通过一个 socket 来读写数据时,您都是在使用一个系统调用(system call)。这个调用(例如 read 或 write)跨越了用户空间应用程序与内核的边界。另外,在进入内核之前,您的调用会通过 C 库来进入内核中的一个通用函数(system_call())。从 system_call() 中,这个调用会进入文件系统层,内核会在这儿确定正在处理的是哪种类型的设备。最后,调用会进入 socket 层,数据就是在这里进行读取或进行排队从而通过 socket 进行传输的(这涉及数据的副本)。
这个过程说明系统调用不仅仅是在应用程序和内核中进行操作的,而且还要经过应用程序和内核中的很多层次。这个过程耗费的资源很高,因此调用次数越多,通过这个调用链进行的工作所需要的时间就越长,应用程序的性能也就越低。
由于我们无法避免这些系统调用,因此惟一的选择是最小化使用这些调用的次数。幸运的是,我们可以对这个过程进行控制。
解决方案
在将数据写入一个 socket 时,尽量一次写入所有的数据,而不是执行多次写数据的操作。对于读操作来说,最好传入可以支持的最大缓冲区,因为如果没有足够多的数据,内核也会试图填充整个缓冲区(另外还需要保持 TCP 的通告窗口为打开状态)。这样,您就可以最小化调用的次数,并可以实现更好的整体性能。
技巧 3. 为 Bandwidth Delay Product 调节 TCP 窗口
TCP 的性能取决于几个方面的因素。两个最重要的因素是链接带宽(link bandwidth)(报文在网络上传输的速率)和 往返时间(round-trip time) 或 RTT(发送报文与接收到另一端的响应之间的延时)。这两个值确定了称为 Bandwidth Delay Product(BDP)的内容。
给定链接带宽和 RTT 之后,您就可以计算出 BDP 的值了,不过这代表什么意义呢?BDP 给出了一种简单的方法来计算理论上最优的 TCP socket 缓冲区大小(其中保存了排队等待传输和等待应用程序接收的数据)。如果缓冲区太小,那么 TCP 窗口就不能完全打开,这会对性能造成限制。如果缓冲区太大,那么宝贵的内存资源就会造成浪费。如果您设置的缓冲区大小正好合适,那么就可以完全利用可用的带宽。下面我们来看一个例子:
BDP = link_bandwidth * RTT
如果应用程序是通过一个 100Mbps 的局域网进行通信,其 RRT 为 50 ms,那么 BDP 就是:
100MBps * 0.050 sec / 8 = 0.625MB = 625KB
注意:此处除以 8 是将位转换成通信使用的字节。
因此,我们可以将 TCP 窗口设置为 BDP 或 1.25MB。但是在 Linux 2.6 上默认的 TCP 窗口大小是 110KB,这会将连接的带宽限制为 2.2MBps,计算方法如下:
throughput = window_size / RTT
110KB / 0.050 = 2.2MBps
如果使用上面计算的窗口大小,我们得到的带宽就是 12.5MBps,计算方法如下:
625KB / 0.050 = 12.5MBps
差别的确很大,并且可以为 socket 提供更大的吞吐量。因此现在您就知道如何为您的 socket 计算最优的缓冲区大小了。但是又该如何来改变呢?
解决方案
Sockets API 提供了几个 socket 选项,其中两个可以用于修改 socket 的发送和接收缓冲区的大小。清单 2 展示了如何使用 SO_SNDBUF 和 SO_RCVBUF 选项来调整发送和接收缓冲区的大小。
注意:尽管 socket 缓冲区的大小确定了通告 TCP 窗口的大小,但是 TCP 还在通告窗口内维护了一个拥塞窗口。因此,由于这个拥塞窗口的存在,给定的 socket 可能永远都不会利用最大的通告窗口。
清单 2. 手动设置发送和接收 socket 缓冲区大小
int ret, sock, sock_buf_size;
sock = socket( AF_INET, SOCK_STREAM, 0 );
sock_buf_size = BDP;
ret = setsockopt( sock, SOL_SOCKET, SO_SNDBUF,
(char *)&sock_buf_size, sizeof(sock_buf_size) );
ret = setsockopt( sock, SOL_SOCKET, SO_RCVBUF,
(char *)&sock_buf_size, sizeof(sock_buf_size) );
在 Linux 2.6 内核中,发送缓冲区的大小是由调用用户来定义的,但是接收缓冲区会自动加倍。您可以进行 getsockopt 调用来验证每个缓冲区的大小。。{最终缓冲区的大小还受限于linux系统的TCP/IP相关的参数配置: /proc/sys/net/core/rmem_max wmem_max rmem_default ,wmem_default }
巨帧(jumbo frame)
我们还可以考虑将包的大小从 1,500 字节修改为 9,000 字节(称为巨帧)。在本地网络中可以通过设置最大传输单元(Maximum Transmit Unit,MTU)来设置巨帧,这可以极大地提高性能。
就 window scaling 来说,TCP 最初可以支持最大为 64KB 的窗口(使用 16 位的值来定义窗口的大小)。采用 window scaling(RFC 1323)扩展之后,您就可以使用 32 位的值来表示窗口的大小了。GNU/Linux 中提供的 TCP/IP 栈可以支持这个选项(以及其他一些选项)。
提示:Linux 内核还包括了自动对这些 socket 缓冲区进行优化的能力(请参阅下面 表 1 中的 tcp_rmem 和 tcp_wmem),不过这些选项会对整个栈造成影响。如果您只需要为一个连接或一类连接调节窗口的大小,那么这种机制也许不能满足您的需要了。
技巧 4. 动态优化 GNU/Linux TCP/IP 栈
标准的 GNU/Linux 发行版试图对各种部署情况都进行优化。这意味着标准的发行版可能并没有对您的环境进行特殊的优化。
解决方案
GNU/Linux 提供了很多可调节的内核参数,您可以使用这些参数为您自己的用途对操作系统进行动态配置。下面我们来了解一下影响 socket 性能的一些更重要的选项。
在 /proc 虚拟文件系统中存在一些可调节的内核参数。这个文件系统中的每个文件都表示一个或多个参数,它们可以通过 cat 工具进行读取,或使用 echo 命令进行修改。清单 3 展示了如何查询或启用一个可调节的参数(在这种情况中,可以在 TCP/IP 栈中启用 IP 转发)。
清单 3. 调优:在 TCP/IP 栈中启用 IP 转发
[root@camus]# cat /proc/sys/net/ipv4/ip_forward
0
[root@camus]# echo “1″ > /poc/sys/net/ipv4/ip_forward
[root@camus]# cat /proc/sys/net/ipv4/ip_forward
1
[root@camus]#
表 1 给出了几个可调节的参数,它们可以帮助您提高 Linux TCP/IP 栈的性能。
表 1. TCP/IP 栈性能使用的可调节内核参数
|
可调节的参数
|
默认值
|
选项说明
|
|
/proc/sys/net/core/rmem_default
|
“110592″
|
定义默认的接收窗口大小;对于更大的 BDP 来说,这个大小也应该更大。
|
|
/proc/sys/net/core/rmem_max
|
“110592″
|
定义接收窗口的最大大小;对于更大的 BDP 来说,这个大小也应该更大。
|
|
/proc/sys/net/core/wmem_default
|
“110592″
|
定义默认的发送窗口大小;对于更大的 BDP 来说,这个大小也应该更大。
|
|
/proc/sys/net/core/wmem_max
|
“110592″
|
定义发送窗口的最大大小;对于更大的 BDP 来说,这个大小也应该更大。
|
|
/proc/sys/net/ipv4/tcp_window_scaling
|
“1″
|
启用 RFC 1323 定义的 window scaling;要支持超过 64KB的窗口,必须启用该值
|
|
/proc/sys/net/ipv4/tcp_sack
|
“1″
|
启用有选择的应答(Selective Acknowledgment),这可以通过有选择地应答乱序接收到的报文来提高性能(这样可以让发送者只发送丢失的报文段);(对于广域网通信来说)这个选项应该启用,但是这会增加对 CPU 的占用。
|
|
/proc/sys/net/ipv4/tcp_fack
|
“1″
|
启用转发应答(Forward Acknowledgment),这可以进行有选择应答(SACK)从而减少拥塞情况的发生;这个选项也应该启用。
|
|
/proc/sys/net/ipv4/tcp_timestamps
|
“1″
|
以一种比重发超时更精确的方法(请参阅 RFC 1323)来启用对RTT 的计算;为了实现更好的性能应该启用这个选项
|
|
/proc/sys/net/ipv4/tcp_mem
|
“24576 32768 49152″
|
确定 TCP 栈应该如何反映内存使用;每个值的单位都是内存页(通常是 4KB)。第一个值是内存使用的下限。第二个值是内存压力模式开始对缓冲区使用应用压力的上限。第三个值是内存上限。在这个层次上可以将报文丢弃,从而减少对内存的使用。对于较大的 BDP 可以增大这些值(但是要记住,其单位是内存页,而不是字节)。
|
|
/proc/sys/net/ipv4/tcp_wmem
|
“4096 16384 131072″
|
为自动调优定义每个 socket 使用的内存。第一个值是为socket 的发送缓冲区分配的最少字节数。第二个值是默认值(该值会被 wmem_default 覆盖),缓冲区在系统负载不重的情况下可以增长到这个值。第三个值是发送缓冲区空间的最大字节数(该值会被 wmem_max 覆盖)。
|
|
/proc/sys/net/ipv4/tcp_rmem
|
“4096 87380 174760″
|
与 tcp_wmem 类似,不过它表示的是为自动调优所使用的接收缓冲区的值。
|
|
/proc/sys/net/ipv4/tcp_low_latency
|
“0″
|
允许 TCP/IP 栈适应在高吞吐量情况下低延时的情况;这个选项应该禁用。
|
|
/proc/sys/net/ipv4/tcp_westwood
|
“0″
|
启用发送者端的拥塞控制算法,它可以维护对吞吐量的评估,并试图对带宽的整体利用情况进行优化;对于 WAN 通信来说应该启用这个选项。
|
|
/proc/sys/net/ipv4/tcp_bic
|
“1″
|
为快速长距离网络启用 Binary Increase Congestion;这样可以更好地利用以 GB 速度进行操作的链接;对于 WAN 通信应该启用这个选项。
|
与任何调优努力一样,最好的方法实际上就是不断进行实验。您的应用程序的行为、处理器的速度以及可用内存的多少都会影响到这些参数影响性能的方式。在某些情况中,您认为有益的操作可能恰恰是有害的(反之亦然)。因此,我们需要逐一试验各个选项,然后检查每个选项的结果。换而言之,我们需要相信自己的经验,但是对每次修改都要进行验证。
提示:下面介绍一个有关永久性配置的问题。注意,如果您重新启动了 GNU/Linux 系统,那么您所需要的任何可调节的内核参数都会恢复成默认值。为了将您所设置的值作为这些参数的默认值,可以使用 /etc/sysctl.conf 在系统启动时将这些参数配置成您所设置的值。
GNU/Linux 工具
GNU/Linux 对我非常有吸引力,这是因为其中有很多工具可以使用。尽管其中大部分都是命令行工具,但是它们都非常有用,而且非常直观。GNU/Linux 提供了几个工具 —— 有些是GNU/Linux 自己提供的,有些是开放源码软件 —— 用于调试网络应用程序,测量带宽/吞吐量,以及检查链接的使用情况。
表 2 列出最有用的几个 GNU/Linux 工具,以及它们的用途。表 3 列出了 GNU/Linux 发行版没有提供的几个有用工具。有关表 3 中工具的更多信息请参阅 参考资料。
表 2. 任何 GNU/Linux 发行版中都可以找到的工具 GNU/Linux 工具 用途
ping 这是用于检查主机的可用性的最常用的工具,但是也可以用于识别带宽延时产品计算的 RTT。
traceroute 打印某个连接到网络主机所经过的包括一系列路由器和网关的路径(路由),从而确定每个 hop 之间的延时。
netstat 确定有关网络子系统、协议和连接的各种统计信息。
tcpdump 显示一个或多个连接的协议级的报文跟踪信息;其中还包括时间信息,您可以使用这些信息来研究不同协议服务的报文时间。
表 3. GNU/Linux 发行版中没有提供的有用性能工具 GNU/Linux 工具 用途
netlog 为应用程序提供一些有关网络性能方面的信息。
nettimer 为瓶颈链接带宽生成一个度量标准;可以用于协议的自动优化。
Ethereal 以一个易于使用的图形化界面提供了 tcpump(报文跟踪)的特性。
iperf 测量 TCP 和 UDP 的网络性能;测量最大带宽,并汇报延时和数据报的丢失情况。
结束语
尝试使用本文中介绍的技巧和技术来提高 socket 应用程序的性能,包括通过禁用 Nagle 算法来减少传输延时,通过设置缓冲区的大小来提高 socket 带宽的利用,通过最小化系统调用的个数来降低系统调用的负载,以及使用可调节的内核参数来优化 Linux 的 TCP/IP 栈。
在进行优化时还需要考虑应用程序的特性。例如,您的应用程序是基于 LAN 的还是会通过 Internet 进行通信?如果您的应用程序仅仅会在 LAN 内部进行操作,那么增大 socket 缓冲区的大小可能不会带来太大的改进,不过启用巨帧却一定会极大地改进性能!
最后,还要使用 tcpdump 或 Ethereal 来检查优化之后的结果。在报文级看到的变化可以帮助展示使用这些技术进行优化之后所取得的成功效果。