您的位置:首页 > 运维架构 > Linux

linux 内核参数图解

2016-05-02 16:44 656 查看

https://www.suse.com/documentation/sles11/book_sle_tuning/data/part_tuning_kernel.html

http://blog.csdn.net/maimang1001/article/details/34941471

https://www.suse.com/documentation/sles11/book_sle_tuning/data/sec_tuning_network_buffers.html

http://dirtysalt.info/os.html

Linux

Table of Contents

1. vmlinuz

2. TCP IO

2.1. create connection

2.2. packet reception

2.3. packet transmission

2.4. congestion control

3. kernel panic

4. Disk IO

5. 程序返回值问题

6. dp8网卡问题

7. 修改资源限制

8. CPU温度过高

9. sync hangup

10. 更换glibc

11. 允许不在tty上执行sudo

12. ssh proxy

13. 修改最大打开文件句柄数

14. apt-get hang

15. 使用自选锁来实现信号量

1 vmlinuz

vmlinuz是可引导的、压缩的内核。“vm”代表“Virtual Memory”。Linux 支持虚拟内存,不像老的操作系统比如DOS有640KB内存的限制。Linux能够使用硬盘空间作为虚拟内存,因此得名“vm”。vmlinuz是可执行的Linux内核,它位于/boot/vmlinuz,它一般是一个软链接。vmlinux是未压缩的内核,vmlinuz是vmlinux的压缩文件。

vmlinuz的建立有两种方式。一是编译内核时通过“make zImage”创建,然后通过:“cp /usr/src/linux-2.4/arch/i386/linux/boot/zImage /boot/vmlinuz”产生。zImage适用于小内核的情况,它的存在是为了向后的兼容性。二是内核编译时通过命令make bzImage创建,然后通过:“cp /usr/src/linux-2.4/arch/i386/linux/boot/bzImage /boot/vmlinuz”产生。bzImage是压缩的内核映像,需要注意,bzImage不是用bzip2压缩的,bzImage中的bz容易引起误解,bz表示“big zImage”。 bzImage中的b是“big”意思。

zImage(vmlinuz)和bzImage(vmlinuz)都是用gzip压缩的。它们不仅是一个压缩文件,而且在这两个文件的开头部分内嵌有gzip解压缩代码。所以你不能用gunzip 或 gzip –dc解包vmlinuz。内核文件中包含一个微型的gzip用于解压缩内核并引导它。两者的不同之处在于,老的zImage解压缩内核到低端内存(第一个640K),bzImage解压缩内核到高端内存(1M以上)。如果内核比较小,那么可以采用zImage或bzImage之一,两种方式引导的系统运行时是相同的。大的内核采用bzImage,不能采用zImage。

2 TCP IO

http://www.ece.virginia.edu/cheetah/documents/papers/TCPlinux.pdf

2.1 create connection

下面摘自 "Linux TCP队列相关参数的总结":



简单看下连接的建立过程,客户端向server发送SYN包,server回复SYN+ACK,同时将这个处于SYN_RECV状态的连接保存到半连接队列。客户端返回ACK包完成三次握手,server将ESTABLISHED状态的连接移入accept队列,等待应用调用accept()。可以看到建立连接涉及两个队列:

半连接队列,保存SYN_RECV状态的连接。队列长度由net.ipv4.tcp_max_syn_backlog设置。

accept队列,保存ESTABLISHED状态的连接。队列长度为min(net.core.somaxconn, backlog). # listen(sockfd, backlog)

另外,为了应对SYN flooding(即客户端只发送SYN包发起握手而不回应ACK完成连接建立,填满server端的半连接队列,让它无法处理正常的握手请求),Linux实现了一种称为SYN cookie的机制,通过net.ipv4.tcp_syncookies控制,设置为1表示开启。简单说SYN cookie就是将连接信息编码在ISN(initial sequence number)中返回给客户端,这时server不需要将半连接保存在队列中,而是利用客户端随后发来的ACK带回的ISN还原连接信息,以完成连接的建立,避免了半连接队列被攻击SYN包填满。对于一去不复返的客户端握手,不理它就是了。

2.2 packet reception



整个流程大致如下:

linux里面使用sk_buff数据结构来描述packet.

NIC检测到packet到达,从Kernel Memory(sk_buffs)分配sk_buff数据结构,调用DMA Engine将packet放到sk_buff数据结构里面 #note: NIC检测有packet到达和有packet发送,都不是触发而是主动poll的方式来完成的

将sk_buff并且加入rx_ring这个ring_buffer里面。如果rx_ring满了的话那么将packet丢弃。

当DMA Engine完成处理之后, NIC通过向CPU发起中断 通知kernel进行处理。

kernel将这个packet传递给IP层进行处理。IP层需要将信息组装成为ip packet。如果ip packet是tcp的话那么放到socket backlog里面。如果socket backlog满了的话那么将ip packet丢弃。 copy packet data to ip buffer to form ip packet #note: 这个步骤完成之后IP layer就可以释放sk_buffer结构

tcp layer从socket backlog里面取出tcp packet, copy ip packet tcp recv buffer to form tcp packet

tcp recv buffer交给application layer处理, copy tcp recv buffer to app buffer to form app packet

其中内核参数有

/proc/sys/net/ipv4/tcp_rmem # tcp recv buffer大小

/proc/sys/net/core/netdev_max_backlog # 图中socket backlog大小,和accept系统调用的backlog区分开。

下面这些是从文章摘取出来的

Linux TCP队列相关参数的总结

https://www.suse.com/documentation/sles11/book_sle_tuning/data/sec_tuning_network_buffers.html

Linux在2.6.17以后增加了recv Buffer自动调节机制,recv buffer的实际大小会自动在最小值和最大值之间浮动,以期找到性能和资源的平衡点,因此大多数情况下不建议将recv buffer手工设置成固定值。

当net.ipv4.tcp_moderate_rcvbuf设置为1时,自动调节机制生效,每个TCP连接的recv Buffer由下面的3元数组指定:net.ipv4.tcp_rmem = (min, default, max). 最初recv buffer被设置为<default>,同时这个缺省值会覆盖net.core.rmem_default的设置。随后recv buffer根据实际情况在最大值和最小值之间动态调节。在缓冲的动态调优机制开启的情况下,我们将net.ipv4.tcp_rmem的最大值设置为BDP(Bandwidth-Delay Product)。

当net.ipv4.tcp_moderate_rcvbuf被设置为0,或者设置了socket选项SO_RCVBUF,缓冲的动态调节机制被关闭。recv buffer的缺省值由net.core.rmem_default设置,但如果设置了net.ipv4.tcp_rmem,缺省值则被覆盖。可以通过系统调用setsockopt()设置recv buffer的最大值为net.core.rmem_max。在缓冲动态调节机制关闭的情况下,建议把缓冲的缺省值设置为BDP。

发送端缓冲的自动调节机制很早就已经实现,并且是无条件开启,没有参数去设置。如果指定了tcp_wmem,则net.core.wmem_default被tcp_wmem的覆盖。send Buffer在tcp_wmem的最小值和最大值之间自动调节。如果调用setsockopt()设置了socket选项SO_SNDBUF,将关闭发送端缓冲的自动调节机制,tcp_wmem将被忽略,SO_SNDBUF的最大值由net.core.wmem_max限制。

2.3 packet transmission



整个流程大致如下:

application layer将数据copy到tcp send buffer里面,如果空间不够的话那么就会出现阻塞。 copy app buffer to tcp send buffer as app packet

tcp layer等待tcp send buffer存在数据或者是需要做ack的时候,组装ip packet推送到IP layer copy tcp send buffer to ip send buffer as tcp packet

IP layer从kernel memory申请sk_buffer,将ip data包装成为packet data,然后塞到qdisc(txqueuelen控制队列长度)里面(指针)。如果队列满的话那么就会出现阻塞,反馈到tcp layer阻塞。copy ip send buffer to packet data as ip packet

NIC driver如果检测到qdisc有数据的话,调用NIC DMA Engine将packet发送出去。发送完成之后NIC向CPU发起中断释放packet data内存到Kernel Memory

其中内核参数有:

/proc/sys/net/ipv4/tcp_wmem 这个和rmem非常类似

#note: 与上面类比,相关参数还有net.core.wmem_default和net.core.wmem_max.

#note: 在wangyx的帮助下, qdisc队列长度参数txqueuelen这个配置在ifconfig下面找到了. txqueuelen = 1000.

➜  ~  ifconfig
eth0      Link encap:Ethernet  HWaddr 12:31:40:00:49:d1
inet addr:10.170.78.31  Bcast:10.170.79.255  Mask:255.255.254.0
inet6 addr: fe80::1031:40ff:fe00:49d1/64 Scope:Link
UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
RX packets:13028359 errors:0 dropped:0 overruns:0 frame:0
TX packets:9504902 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:2464083770 (2.4 GB)  TX bytes:20165782073 (20.1 GB)
Interrupt:25

下面摘自: Linux TCP队列相关参数的总结

QDisc(queueing discipline )位于IP层和网卡的ring buffer之间。我们已经知道,ring buffer是一个简单的FIFO队列,这种设计使网卡的驱动层保持简单和快速。而QDisc实现了流量管理的高级功能,包括流量分类,优先级和流量整形(rate-shaping)。可以使用tc命令配置QDisc。

QDisc的队列长度由txqueuelen设置,和接收数据包的队列长度由内核参数net.core.netdev_max_backlog控制所不同,txqueuelen是和网卡关联

2.4 congestion control



初始状态是slow start

cwnd(congestion window) 拥塞窗口,表示一次最多发送的数据包多少。

ssthresh(slow start threshold) 慢速启动阈值。

MSS(maximum segment size) 最大分节大小,和传输网络的MTU相关。

为什么多 TCP 连接分块下载比单连接下载快?

3 kernel panic



4 Disk IO

Linux IO Stack Diagram

Linux Storage Stack Diagram

5 程序返回值问题

首先看下面一段Java程序

/* coding:utf-8
* Copyright (C) dirlt
*/

public class X{
public static void main(String[] args) {
System.exit(1);
}
}


然后这个Java程序被Python调用,判断这个打印值

#!/usr/bin/env python
#coding:utf-8
#Copyright (C) dirlt

import os
print os.system('java X')


返回值不为1而是256,对此解释是这样的

a 16-bit number, whose low byte is the signal number that killed the process, and whose high byte is the exit status (if the signal number is zero); the high bit of the low byte is set if a core file was produced.

但是下面这段Python程序,使用echo $?判断返回值为0而不是256

#!/usr/bin/env python
#coding:utf-8
#Copyright (C) dirlt

code=256
exit(code)


6 dp8网卡问题

当时dp8的网络流量从一个非常大的值变为非常小的值,检查/proc/net/netstat,以下几个统计数值dp8和其他机器差距较大(相差1-2个数量级):

TCPDirectCopyFromPrequeue

TCPHPHitsToUser

TCPDSACKUndo

TCPLossUndo

TCPLostRetransmit

TCPFastRetrans

TCPSlowStartRetrans

TCPSackShiftFallback

之后在dmesg上面发现如下线索:

dp@dp8:~$ dmesg | grep eth0
[ 15.635160] eth0: Broadcom NetXtreme II BCM5716 1000Base-T (C0) PCI Express f
[ 15.736389] bnx2: eth0: using MSIX
[ 15.738263] ADDRCONF(NETDEV_UP): eth0: link is not ready
[ 37.848755] bnx2: eth0 NIC Copper Link is Up, 100 Mbps full duplex
[ 37.850623] ADDRCONF(NETDEV_CHANGE): eth0: link becomes ready
[ 1933.934668] bnx2: eth0: using MSIX
[ 1933.936960] ADDRCONF(NETDEV_UP): eth0: link is not ready
[ 1956.130773] bnx2: eth0 NIC Copper Link is Up, 100 Mbps full duplex
[ 1956.132625] ADDRCONF(NETDEV_CHANGE): eth0: link becomes ready
[4804526.542976] bnx2: eth0 NIC Copper Link is Down
[4804552.008858] bnx2: eth0 NIC Copper Link is Up, 100 Mbps full duplex

日志 [4804552.008858] bnx2: eth0 NIC Copper Link is Up, 100 Mbps full duplex 表明dp8上的网卡速度被识别成100 Mbps了。

可能的原因如下:

网线、水晶头质量太差或老化、水晶头没压好,从而导致网线接触不良或短路等,可以重新压水晶头或更换网线,建议用质量可靠的六类网线六类水晶头

本地连接―右键―属性―配置―高级―速度和双工,这里设置错误,改为自动感应或1000Mbps全双工即可

网卡所接的交换机或路由器等硬件设备出现故障,或者这些设备是百兆的(千和百连在一起,千变百向下兼容)

电磁场干扰有时也会变百兆,所以说网线尽量别与电线一起穿管(论坛会员tchack友情提供)

我们的网线都是由 世xx联 提供的,质量应该不错,有两种情况需要优先排除。

网线问题(测试方法:换根网线试试)

交换机dp8连接的口坏了(测试方法:把dp8的网线换一个交换机的口)

7 修改资源限制

临时的修改方式可以通过ulimit来进行修改,也可以通过修改文件/etc/security/limits.conf来永久修改

hadoop - nofile 102400
hadoop - nproc 40960


8 CPU温度过高

这个问题是我在Ubuntu PC上面遇到的,明显的感觉就是运行速度变慢。然后在syslog里面出现如下日志:

May  2 18:24:21 umeng-ubuntu-pc kernel: [ 1188.717609] CPU1: Core temperature/speed normal
May  2 18:24:21 umeng-ubuntu-pc kernel: [ 1188.717612] CPU0: Package temperature above threshold, cpu clock throttled (total events = 137902)
May  2 18:24:21 umeng-ubuntu-pc kernel: [ 1188.717615] CPU2: Package temperature above threshold, cpu clock throttled (total events = 137902)
May  2 18:24:21 umeng-ubuntu-pc kernel: [ 1188.717619] CPU1: Package temperature above threshold, cpu clock throttled (total events = 137902)
May  2 18:24:21 umeng-ubuntu-pc kernel: [ 1188.717622] CPU3: Package temperature above threshold, cpu clock throttled (total events = 137902)


9 sync hangup

kill -KILL fails to kill process : http://lists.freebsd.org/pipermail/freebsd-questions/2008-September/182821.html

Linux-Kernel Archive: Bug: sync's hangup forever in call_rwsem_down_read_failed : http://lkml.indiana.edu/hypermail/linux/kernel/1011.2/04099.html

10 更换glibc

linux - How to recover after deleting the symbolic link libc.so.6? - Stack Overflow : http://stackoverflow.com/questions/12249547/how-to-recover-after-deleting-the-symbolic-link-libc-so-6

@2013-05-23 https://docs.google.com/a/umeng.com/document/d/12dzJ3OhVlrEax3yIdz0k08F8tM8DDQva1wdrD3K49PI/edit 怀疑glibc版本存在问题,在dp45上操作但是出现问题。

我的操作顺序计划是这样的:

将dp20的glibc copy到自己的目录下面/home/dp/dirlt/libc-2.11.so

将dp45的glibc backup. mv /lib64/libc-2.12.so /lib64/libc-2.12.bak.so(补充一点,就是在lib64下面还有软链接 libc.so.6 -> libc-2.12.so,这个文件应该是被程序查找使用的)

cp /home/dp/dirlt/libc-2.11.so /lib64/libc-2.12.so

但是进行到2之后就发现cp不可用了,并且ls等命令也不能够使用了。原因非常简单,就是因为2之后libc.so.6没有对应的文件了,而cp,ls这些基本的命令依赖于这个动态链接库。

~ $ ldd /bin/cp
linux-vdso.so.1 =>  (0x00007fff9717f000)
libselinux.so.1 => /lib/x86_64-linux-gnu/libselinux.so.1 (0x00007f5efb804000)
librt.so.1 => /lib/x86_64-linux-gnu/librt.so.1 (0x00007f5efb5fc000)
libacl.so.1 => /lib/x86_64-linux-gnu/libacl.so.1 (0x00007f5efb3f3000)
libattr.so.1 => /lib/x86_64-linux-gnu/libattr.so.1 (0x00007f5efb1ee000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f5efae2f000)
libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f5efac2a000)
/lib64/ld-linux-x86-64.so.2 (0x00007f5efba2d000)
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f5efaa0d000)


@2013-08-03

A copy of the C library was found in an unexpected directory | Blog : http://blog.i-al.net/2013/03/a-copy-of-the-c-library-was-found-in-an-unexpected-directory/

上面的链接给出了升级glibc的方法

sudo su - root # 首先切换到root账号下面

mv libc.so librt.so /root # 将glibc等相关的so移动到root账号下面,主要不要移动软连接文件。

LD_PRELOAD=/root/libc.so:/root/librt.so bash # 这个时候如果执行bash是找不到glibc等so了,所以需要使用LD_PRELOAD来预先加载

apt-get install # 在这个bash下面使用apt-get来安装和升级glibc.

11 允许不在tty上执行sudo

修改/etc/sudoers文件,注释掉

Defaults requiretty


12 ssh proxy

http://serverfault.com/questions/37629/how-do-i-do-multihop-scp-transfers

目的机器是D,端口是16021,用户是x

跳板机器是T,端口是18021,用户是y

client需要和x@D以及y@T建立信任关系

方法A

从T上和D建立链接并且配置转发端口p, 所有和T:p的数据交互都会转发到D:16021

在T上执行 ssh -L "*:5502:D:16021" x@D # 转发端口是5502

-o ServerAliveInterval=60 # 我才想单位应该是s。这样每隔60s可以和server做一些keepalive的通信,确保长时间没有数据通信的情况下,连接不会断开。

ssh -p 5502 x@T 或者 scp -P 5502 <file> x@T:<path-at-D>

方法B

scp可以指定proxyCommand配合D上nc命令完成

scp -o ProxyCommand="ssh -p 18021 y@T 'nc D 16021'" <file> x@D:<path-at-D>

13 修改最大打开文件句柄数

http://blog.csdn.net/superchanon/article/details/13303705

http://unix.stackexchange.com/questions/127777/how-to-configure-the-process-open-file-limit-of-a-user

https://www.kernel.org/doc/Documentation/sysctl/fs.txt

首先需要修改系统上限

/proc/sys/fs/file-max # 所有进程打开文件句柄数上限

/proc/sys/fs/nr_open # 单个进程打开文件句柄数上限

/proc/sys/fs/file-nr # 系统当前打开文件句柄数

然后修改用户(进程)使用上限

/etc/security/limits.conf

ulimit

14 apt-get hang

在使用ubuntu的apt-get时候,可能会出现一些异常的状况,我们直接终止了apt-get。但是这个时候apt-get软件本身出于一个不正常的状态,导致之后不能够启动apt-get。如果观察进程的话会出现下面一些可疑的进程

dp@dp1:~$ ps aux | grep "apt"
root      3587  0.0  0.0  36148 22800 ?        Ds   Oct08   0:00 /usr/bin/dpkg --status-fd 50 --unpack --auto-deconfigure /var/cache/apt/archives/sgml-data_2.0.4_all.deb
root      9579  0.0  0.0  35992 22744 ?        Ds   Oct19   0:00 /usr/bin/dpkg --status-fd 50 --unpack --auto-deconfigure /var/cache/apt/archives/iftop_0.17-16_amd64.deb
root     25957  0.0  0.0  36120 22796 ?        Ds   Nov05   0:00 /usr/bin/dpkg --status-fd 50 --unpack --auto-deconfigure /var/cache/apt/archives/iftop_0.17-16_amd64.deb /var/cache/apt/archives/iotop_0.4-1_all.deb
dp       30586  0.0  0.0   7628  1020 pts/2    S+   08:59   0:00 grep --color=auto apt

这些进程的父进程都是init进程,并且状态是uninterruptible sleep,给kill -9也没有办法终止,唯一的办法只能reboot机器来解决这个问题。关于这个问题可以看stackoverflow上面的解答 How to stop 'uninterruptible' process on Linux? - Stack Overflow http://stackoverflow.com/questions/767551/how-to-stop-uninterruptible-process-on-linux

Simple answer: you cannot. Longer answer: the uninterruptable sleep means the process will not be woken up by signals. It can be only woken up by what it's waiting for. When I get such situations eg. with CD-ROM, I usually reset the computer by using suspend-to-disk and resuming.

The D state basically means that the process is waiting for disk I/O, or other block I/O that can't be interrupted. Sometimes this means the kernel or device is feverishly trying to read a bad block (especially from an optical disk). Sometimes it means there's something else. The process cannot be killed until it gets out of the D state. Find out what it is waiting for and fix that. The easy way is to reboot. Sometimes removing the disk in question helps, but that can be rather dangerous: unfixable catastrophic hardware failure if you don't know what you're doing (read: smoke coming out).

15 使用自选锁来实现信号量

typedef struct sema
{
lock_t lock;
int count;
queue q;
} sema_t;

void init_sema(sema_t* sema, int init_cnt)
{
init_lock(&sema->lock);
init_queue(&sema->q);
sema->count=init_cnt;
}

void p(sema_t* sema)
{
lock(&sema->lock);
sema->count--;
if (sema->count < 0) {
q.push(current_process_context());
unlock(&sema->lock);
swtch(); // switch to another process.
return;
}
unlock(&sema->lock);
}

void v(sema_t* sema)
{
lock(&sema->lock);
sema->count++;
if (sema->count <= 0) {
pcs_ctx* ctx = q.pop();
unlock(&sema->lock);
enqueue(&running_queue, ctx);
return ;
}
unlock(&sema->lock);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: