hook

使用了很久git了,但是hook用的很少,今天研究一下
git的hook放在GIT_DIR/.git/hooks目录下

1
2
3
4
5
6
7
8
9
10
11
12
13
➜ hooks git:(master) ls -al
总用量 48
drwxrwxr-x 2 eric eric 4096 4月 20 16:18 .
drwxrwxr-x 7 eric eric 4096 4月 20 16:18 ..
-rwxrwxr-x 1 eric eric 452 4月 20 16:18 applypatch-msg.sample
-rwxrwxr-x 1 eric eric 896 4月 20 16:18 commit-msg.sample
-rwxrwxr-x 1 eric eric 189 4月 20 16:18 post-update.sample
-rwxrwxr-x 1 eric eric 398 4月 20 16:18 pre-applypatch.sample
-rwxrwxr-x 1 eric eric 1642 4月 20 16:18 pre-commit.sample
-rwxrwxr-x 1 eric eric 1239 4月 20 16:18 prepare-commit-msg.sample
-rwxrwxr-x 1 eric eric 1352 4月 20 16:18 pre-push.sample
-rwxrwxr-x 1 eric eric 4898 4月 20 16:18 pre-rebase.sample
-rwxrwxr-x 1 eric eric 3611 4月 20 16:18 update.sample

可以看到该目录下有很多以sample结尾的文件
这里尝试几个简单的吧

客户端钩子

commit-msg

将commit-msg.sample 重命名commit-msg

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
mv commit-msg.sample commit-msg
➜ hooks git:(master) cat commit-msg
#!/bin/sh
#
# An example hook script to check the commit log message.
# Called by "git commit" with one argument, the name of the file
# that has the commit message. The hook should exit with non-zero
# status after issuing an appropriate message if it wants to stop the
# commit. The hook is allowed to edit the commit message file.
#
# To enable this hook, rename this file to "commit-msg".
# Uncomment the below to add a Signed-off-by line to the message.
# Doing this in a hook is a bad idea in general, but the prepare-commit-msg
# hook is more suited to it.
#
# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p')
# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1"
# This example catches duplicate Signed-off-by lines.
test "" = "$(grep '^Signed-off-by: ' "$1" |
sort | uniq -c | sed -e '/^[ ]*1[ ]/d')" || {
echo >&2 Duplicate Signed-off-by lines.
exit 1
}

看介绍,应该是在git commit 命令时检查log message的脚本,有一个参数,存放commit message的文件名
在最后加上判断log message低于3个时不得提交的脚本

1
2
3
4
5
words=$(cat $1 |wc -w)
if test $words -lt 3
then echo "commit message should larger than 3 words"
exit 1
fi

然后添加一个文件,提交。无法提交,原因是message单词数没有超过3个,过于简单

1
2
3
4
➜ git-hooks git:(master) ✗ echo 1111>aaa
➜ git-hooks git:(master) ✗ git add .
➜ git-hooks git:(master) ✗ git commit -m 'add aaa'
commit message should larger than 3 words

pre-commit

git commit 触发 先于commit-msg,不接收参数

prepare-commit-msg

效果与pre-commit类似,先于pre-commit执行

服务器钩子

pre-receive

在客户端推送时最先执行,可以用它来拒绝客户端的推送。
我能想到的,可以做语法检查,拼写等等

update

与 pre-receive 类似,但会在每个分支都执行一次。

post-receive

在客户端推送完成后执行

使用gitlab的webhook

现在很多版本控制都是使用git,gitlab来管理git项目仓库,其提供了方便的web管理,用户访问及权限,是搭建私人git仓库的推荐选择。
具体安装可以参考官方的wiki,因为gitlab比较吃内存,,所以我不想把它安装到机器上,所以就用docker来操作吧。
创建一个名为gitlab的容器,并将其80端口映射到苏主机的8088端口

1
docker run -itd -p 8088:80 --name gitlab gitlab/gitlab-ce

等待一段时间,就可以访问本机的8088端口,进入到gitlab的web管理界面了,初次使用要设置root的密码,然后就可以登录了,默认管理员帐号是root,密码是初次进入时设置的。
登录后点击 new project创建一个git项目,就叫做testHook把
接下来再启动一个docker 容器来作为git的客户端

1
docker run -it --link=gitlab ubuntu:14.04 /bin/bash

启动后安装git

1
root@ed6dd236d56b:~# apt-get update && apt-get install git

生成sshkey

1
ssh-keygen

一直回车就可以了,在~/.ssh/下就生成了2个文件,私钥和公钥

1
cat ~/.ssh/id_rsa.pub

把公钥设置到gitlab里,搜索框里搜索sshkey,把公钥复制到key中,title可以随意设置,gitlab也会自动生成
接下来把git项目clone到客户端中

1
root@ed6dd236d56b:~#git clone git@b9a4402e85aa:root/testhook.git

home目录下就会有一个空的git项目testHook,

接下来设置webhook,可以为每个项目设置webhook,比如我创建的这个git项目,在http://localhost:8088/root/testhook/hooks 中设置
这里我只添加push event,这个钩子作用应该类似post-receive,url是钩子触发时,需要向“谁”报告,这里我只在本机上启动了一个swoole-server,url请求swoole来看看gitlab钩子触发时具体发了些什么信息
swoole的代码就直接粘贴到这里了

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
<?php
// Server
class Server
{
private $serv;
public function __construct() {
$this->serv = new swoole_server("0.0.0.0", 9501);
$this->serv->set(array(
'worker_num' => 8,
'daemonize' => false,
'max_request' => 10000,
'dispatch_mode' => 2,
'debug_mode'=> 1
));
$this->serv->on('Start', array($this, 'onStart'));
$this->serv->on('Connect', array($this, 'onConnect'));
$this->serv->on('Receive', array($this, 'onReceive'));
$this->serv->on('Close', array($this, 'onClose'));
$this->serv->start();
}
public function onStart( $serv ) {
echo "Start\n";
}
public function onConnect( $serv, $fd, $from_id ) {
$serv->send( $fd, "Hello {$fd}!,this is swoole server " );
}
public function onReceive( swoole_server $serv, $fd, $from_id, $data ) {
echo "Get Message From Client {$fd}:{$data}\n";
}
public function onClose( $serv, $fd, $from_id ) {
echo "Client {$fd} close connection\n";
}
}
// 启动服务器
$server = new Server();

启动swooleserver

1
php swoole-server.php

然后来操作下客户端,需要设置git的user.name 和user.email不然不能commit

1
2
3
4
5
6
root@ed6dd236d56b:~/testhook# echo 1>test
root@ed6dd236d56b:~/testhook# git add test
root@ed6dd236d56b:~/testhook# git commit -m 'add test'
[master 7923a94] add test
1 file changed, 1 insertion(+)
create mode 100644 test

接下来push,让gitlab产生一个触发

1
2
3
4
5
6
7
8
root@ed6dd236d56b:~/testhook# git push origin master
Counting objects: 4, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 256 bytes | 0 bytes/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To git@b9a4402e85aa:root/testhook.git
79a7567..7923a94 master -> master

可以看到swoole产生的日志

1
2
3
4
5
6
7
8
9
10
11
➜ php swoole-server.php
Start
Get Message From Client 1:POST /swoole/swoole-client.php HTTP/1.1
Content-Type: application/json
X-Gitlab-Event: Push Hook
Connection: close
Host: 10.207.26.234:9501
Content-Length: 1532
Get Message From Client 1:{"object_kind":"push","before":"79a7567c469738a689d38510f9cfc6b5132eda02","after":"7923a944b9c2451be90d8f219fba6f0732c958ba","ref":"refs/heads/master","checkout_sha":"7923a944b9c2451be90d8f219fba6f0732c958ba","message":null,"user_id":1,"user_name":"Administrator","user_email":"admin@example.com","user_avatar":"http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon","project_id":1,"project":{"name":"testhook","description":"","web_url":"http://b9a4402e85aa/root/testhook","avatar_url":null,"git_ssh_url":"git@b9a4402e85aa:root/testhook.git","git_http_url":"http://b9a4402e85aa/root/testhook.git","namespace":"root","visibility_level":20,"path_with_namespace":"root/testhook","default_branch":"master","homepage":"http://b9a4402e85aa/root/testhook","url":"git@b9a4402e85aa:root/testhook.git","ssh_url":"git@b9a4402e85aa:root/testhook.git","http_url":"http://b9a4402e85aa/root/testhook.git"},"commits":[{"id":"7923a944b9c2451be90d8f219fba6f0732c958ba","message":"add test\n","timestamp":"2016-05-12T03:36:03+00:00","url":"http://b9a4402e85aa/root/testhook/commit/7923a944b9c2451be90d8f219fba6f0732c958ba","author":{"name":"ericwang","email":"ericwang@leju.com"},"added":["test"],"modified":[],"removed":[]}],"total_commits_count":1,"repository":{"name":"testhook","url":"git@b9a4402e85aa:root/testhook.git","description":"","homepage":"http://b9a4402e85aa/root/testhook","git_http_url":"http://b9a4402e85aa/root/testhook.git","git_ssh_url":"git@b9a4402e85aa:root/testhook.git","visibility_level":20}}

关于webhook具体的细节,在gitlab搜索框中搜索webhook也可以查阅

参考

[1] https://www.kernel.org/pub/software/scm/git/docs/githooks.html
[2] https://www.atlassian.com/git/tutorials/git-hooks/local-hooks
[3] http://www.ituring.com.cn/article/206985

erlang 安装

关于erlang 的下载,可以到官网找最新版本

1
2
3
4
5
6
wget http://erlang.org/download/otp_src_18.3.tar.gz
tar zxvf otp_src_18.3.tar.gz
cd otp_src_18.3
./configure
sudo make
sudo make install

erlang编译会依赖一些环境,这里就不一一列举了
缺省情况下需要libncurses5-dev libssl-dev 以及java环境
因为本人用的zsh,在编译的时候提示/bin/sh javac not found,原因大概是javac并没有在/bin/sh的path的环境变量中,暴力了一点,直接在/usr/local/bin下做了javac,jar的软连接,解决了

rabbitmq

下载地址:http://www.rabbitmq.com/download.html
选择binary安装包

1
2
3
4
5
wget www.rabbitmq.com/releases/rabbitmq-server/v3.6.1/rabbitmq-server-generic-unix-3.6.1.tar.xz
xz -d rabbitmq-server-generic-unix-3.6.1.tar.xz
tar xvf rabbitmq-server-generic-unix-3.6.1.tar
mv rabbitmq_server-3.6.1 /usr
mv rabbitmq_server-3.6.1 /usr/local/rabbitmq

下载的是二进制包,所以可以直接使用
启动rabbitmq

/usr/local/rabbitmq/sbin/rabbitmq-server

以下部分参考自深入理解linux系统下proc文件系统内容
Linux系统上的/proc目录是一种文件系统,即proc文件系统。与其它常见的文件系统不同的是,/proc是一种伪文件系统(也即虚拟文件系统),存储的是当前内核运行状态的一系列特殊文件,用户可以通过这些文件查看有关系统硬件及当前正在运行进程的信息,甚至可以通过更改其中某些文件来改变内核的运行状态。

基于/proc文件系统如上所述的特殊性,其内的文件也常被称作虚拟文件,并具有一些独特的特点。例如,其中有些文件虽然使用查看命令查看时会返回大量信息,但文件本身的大小却会显示为0字节。此外,这些特殊文件中大多数文件的时间及日期属性通常为当前系统时间和日期,这跟它们随时会被刷新(存储于RAM中)有关。

为了查看及使用上的方便,这些文件通常会按照相关性进行分类存储于不同的目录甚至子目录中,如/proc/scsi目录中存储的就是当前系统上所有SCSI设备的相关信息,/proc/N中存储的则是系统当前正在运行的进程的相关信息,其中N为正在运行的进程(可以想象得到,在某进程结束后其相关目录则会消失)。

大多数虚拟文件可以使用文件查看命令如cat、more或者less进行查看,有些文件信息表述的内容可以一目了然,但也有文件的信息却不怎么具有可读性。不过,这些可读性较差的文件在使用一些命令如apm、free、lspci或top查看时却可以有着不错的表现。

进程目录中的常见文件介绍

/proc目录中包含许多以数字命名的子目录,这些数字表示系统当前正在运行进程的进程号,里面包含对应进程相关的多个信息文件。
以下实验都是在基于docker的nginx容器里进行的,部分内容可能会与真实系统有些区别(用docker的原因是进程比较少,可以直接使用docker run -it nginx:1.7 /bin/bash创建一个nginx的容器并进入容器内部)

1
2
3
4
5
6
root@cdb3d33f6cb4:/# ls /bin/
bash cp dir egrep gunzip ln mknod mv pwd run-parts ss tar uname zcat zforce
cat dash dmesg false gzexe login mktemp nisdomainname rbash sed stty tempfile uncompress zcmp zgrep
chgrp date dnsdomainname fgrep gzip ls more pidof readlink sh su touch vdir zdiff zless
chmod dd domainname findmnt hostname lsblk mount ping rm sh.distrib sync true which zegrep zmore
chown df echo grep ip mkdir mountpoint ping6 rmdir sleep tailf umount ypdomainname fgrep znew

容器中可用的命令少之又少,都是些基本的命令,但是有这些命令,可以做很多事情了,虽然很多命令我也不懂。

1
2
3
4
5
6
root@c27f523688ea:/# cd proc/
root@c27f523688ea:/proc# ls
1 bus consoles diskstats fb iomem kcore kpagecount meminfo mtrr scsi stat sysvipc tty vmstat
15 cgroups cpuinfo dma filesystems ioports key-users kpageflags misc net self swaps thread-self uptime zoneinfo
acpi cmdline crypto driver fs irq keys loadavg modules pagetypeinfo slabinfo sys timer_list version
buddyinfo config.gz devices execdomains interrupts kallsyms kmsg locks mounts partitions softirqs sysrq-trigger timer_stats vmallocinfo

上面列出的是/proc目录中一些进程相关的目录,每个目录中是当程本身相关信息的文件。
下面我们来启动一个nginx,看看会发生什么

1
2
3
4
5
6
root@c27f523688ea:/proc# nginx
root@c27f523688ea:/proc# ls
1 acpi cmdline crypto driver fs irq keys loadavg modules pagetypeinfo slabinfo sys timer_list version
17 buddyinfo config.gz devices execdomains interrupts kallsyms kmsg locks mounts partitions softirqs sysrq-trigger timer_stats vmallocinfo
18 bus consoles diskstats fb iomem kcore kpagecount meminfo mtrr scsi stat sysvipc tty vmstat
19 cgroups cpuinfo dma filesystems ioports key-users kpageflags misc net self swaps thread-self uptime zoneinfo

嗯? 多出来3个数字命名的目录,对就是pid 为17,18,19的进程。再次ls,发现19小时,没错19应该是运行ls产生的进程,运行完成就消失了,猜测17,18一个是nginx master,另一个应该是worker
查看以下pid为18的进程,属组和用户都是nginx,更加确定它就是nginx的worker

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
root@c27f523688ea:/proc# ls -al 18
ls: cannot read symbolic link 18/cwd: Permission denied
ls: cannot read symbolic link 18/root: Permission denied
ls: cannot read symbolic link 18/exe: Permission denied
total 0
dr-xr-xr-x 9 nginx nginx 0 Feb 15 02:13 .
dr-xr-xr-x 134 root root 0 Feb 15 02:11 ..
dr-xr-xr-x 2 nginx nginx 0 Feb 15 02:17 attr
-rw-r--r-- 1 nginx nginx 0 Feb 15 02:17 autogroup
-r-------- 1 nginx nginx 0 Feb 15 02:17 auxv
-r--r--r-- 1 nginx nginx 0 Feb 15 02:17 cgroup
--w------- 1 nginx nginx 0 Feb 15 02:17 clear_refs
-r--r--r-- 1 nginx nginx 0 Feb 15 02:17 cmdline
-rw-r--r-- 1 nginx nginx 0 Feb 15 02:17 comm
-rw-r--r-- 1 nginx nginx 0 Feb 15 02:17 coredump_filter
-r--r--r-- 1 nginx nginx 0 Feb 15 02:17 cpuset
lrwxrwxrwx 1 nginx nginx 0 Feb 15 02:17 cwd
-r-------- 1 nginx nginx 0 Feb 15 02:17 environ
lrwxrwxrwx 1 nginx nginx 0 Feb 15 02:17 exe
dr-x------ 2 nginx nginx 0 Feb 15 02:17 fd
dr-x------ 2 nginx nginx 0 Feb 15 02:17 fdinfo
-rw-r--r-- 1 nginx nginx 0 Feb 15 02:17 gid_map
-r--r--r-- 1 nginx nginx 0 Feb 15 02:17 limits
dr-x------ 2 nginx nginx 0 Feb 15 02:17 map_files
-r--r--r-- 1 nginx nginx 0 Feb 15 02:17 maps
-rw------- 1 nginx nginx 0 Feb 15 02:17 mem
-r--r--r-- 1 nginx nginx 0 Feb 15 02:17 mountinfo
-r--r--r-- 1 nginx nginx 0 Feb 15 02:17 mounts
-r-------- 1 nginx nginx 0 Feb 15 02:17 mountstats
dr-xr-xr-x 7 nginx nginx 0 Feb 15 02:17 net
dr-x--x--x 2 nginx nginx 0 Feb 15 02:17 ns
-rw-r--r-- 1 nginx nginx 0 Feb 15 02:17 oom_adj
-r--r--r-- 1 nginx nginx 0 Feb 15 02:17 oom_score
-rw-r--r-- 1 nginx nginx 0 Feb 15 02:17 oom_score_adj
-r-------- 1 nginx nginx 0 Feb 15 02:17 pagemap
-r-------- 1 nginx nginx 0 Feb 15 02:17 personality
-rw-r--r-- 1 nginx nginx 0 Feb 15 02:17 projid_map
lrwxrwxrwx 1 nginx nginx 0 Feb 15 02:17 root
-rw-r--r-- 1 nginx nginx 0 Feb 15 02:17 setgroups
-r--r--r-- 1 nginx nginx 0 Feb 15 02:17 smaps
-r-------- 1 nginx nginx 0 Feb 15 02:17 stack
-r--r--r-- 1 nginx nginx 0 Feb 15 02:17 stat
-r--r--r-- 1 nginx nginx 0 Feb 15 02:17 statm
-r--r--r-- 1 nginx nginx 0 Feb 15 02:17 status
-r-------- 1 nginx nginx 0 Feb 15 02:17 syscall
dr-xr-xr-x 3 nginx nginx 0 Feb 15 02:17 task
-r--r--r-- 1 nginx nginx 0 Feb 15 02:17 timers
-rw-r--r-- 1 nginx nginx 0 Feb 15 02:17 uid_map
-r--r--r-- 1 nginx nginx 0 Feb 15 02:17 wchar

有些文件没有权限,还是看nginx的master吧

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
root@c27f523688ea:/proc# ls -al 17/
total 0
dr-xr-xr-x 9 root root 0 Feb 15 02:13 .
dr-xr-xr-x 134 root root 0 Feb 15 02:11 ..
dr-xr-xr-x 2 root root 0 Feb 15 02:19 attr
-rw-r--r-- 1 root root 0 Feb 15 02:19 autogroup
-r-------- 1 root root 0 Feb 15 02:19 auxv
-r--r--r-- 1 root root 0 Feb 15 02:19 cgroup
--w------- 1 root root 0 Feb 15 02:19 clear_refs
-r--r--r-- 1 root root 0 Feb 15 02:19 cmdline
-rw-r--r-- 1 root root 0 Feb 15 02:19 comm
-rw-r--r-- 1 root root 0 Feb 15 02:19 coredump_filter
-r--r--r-- 1 root root 0 Feb 15 02:19 cpuset
lrwxrwxrwx 1 root root 0 Feb 15 02:19 cwd -> /proc
-r-------- 1 root root 0 Feb 15 02:19 environ
lrwxrwxrwx 1 root root 0 Feb 15 02:19 exe -> /usr/sbin/nginx
dr-x------ 2 root root 0 Feb 15 02:19 fd
dr-x------ 2 root root 0 Feb 15 02:19 fdinfo
-rw-r--r-- 1 root root 0 Feb 15 02:19 gid_map
-r--r--r-- 1 root root 0 Feb 15 02:19 limits
dr-x------ 2 root root 0 Feb 15 02:19 map_files
-r--r--r-- 1 root root 0 Feb 15 02:19 maps
-rw------- 1 root root 0 Feb 15 02:19 mem
-r--r--r-- 1 root root 0 Feb 15 02:19 mountinfo
-r--r--r-- 1 root root 0 Feb 15 02:19 mounts
-r-------- 1 root root 0 Feb 15 02:19 mountstats
dr-xr-xr-x 7 root root 0 Feb 15 02:19 net
dr-x--x--x 2 root root 0 Feb 15 02:19 ns
-rw-r--r-- 1 root root 0 Feb 15 02:19 oom_adj
-r--r--r-- 1 root root 0 Feb 15 02:19 oom_score
-rw-r--r-- 1 root root 0 Feb 15 02:19 oom_score_adj
-r-------- 1 root root 0 Feb 15 02:19 pagemap
-r-------- 1 root root 0 Feb 15 02:19 personality
-rw-r--r-- 1 root root 0 Feb 15 02:19 projid_map
lrwxrwxrwx 1 root root 0 Feb 15 02:19 root -> /
-rw-r--r-- 1 root root 0 Feb 15 02:19 setgroups
-r--r--r-- 1 root root 0 Feb 15 02:19 smaps
-r-------- 1 root root 0 Feb 15 02:19 stack
-r--r--r-- 1 root root 0 Feb 15 02:19 stat
-r--r--r-- 1 root root 0 Feb 15 02:19 statm
-r--r--r-- 1 root root 0 Feb 15 02:19 status
-r-------- 1 root root 0 Feb 15 02:19 syscall
dr-xr-xr-x 3 root root 0 Feb 15 02:19 task
-r--r--r-- 1 root root 0 Feb 15 02:19 timers
-rw-r--r-- 1 root root 0 Feb 15 02:19 uid_map
-r--r--r-- 1 root root 0 Feb 15 02:19 wchan

接下来看看各个文件都是干吗用的吧

  • cmdline — 启动当前进程的完整命令,但僵尸进程目录中的此文件不包含任何信息;
1
2
3
root@c27f523688ea:/proc# cd 17
root@c27f523688ea:/proc/17# more cmdline
nginx: master process nginx
  • cwd 指向当前进程运行目录的一个符号链接;

  • environ 当前进程的环境变量列表,彼此间用空字符(NULL)隔开;变量用大写字母表示,其值用小写字母表示;

1
2
root@c27f523688ea:/proc/17# more environ
master process nginx
  • exe 指向启动当前进程的可执行文件(完整路径)的符号链接,通过/proc/N/exe可以启动当前进程的一个拷贝;
1
2
root@c27f523688ea:/proc/17# ls -al exe
lrwxrwxrwx 1 root root 0 Feb 15 02:19 exe -> /usr/sbin/nginx

实际的可执行文件就是/usr/sbin/nginx

  • fd 这是个目录,包含当前进程打开的每一个文件的文件描述符(file descriptor),这些文件描述符是指向实际文件的一个符号链接;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    root@c27f523688ea:/proc/17# ls -al fd
    total 0
    dr-x------ 2 root root 0 Feb 15 02:19 .
    dr-xr-xr-x 9 root root 0 Feb 15 02:13 ..
    lrwx------ 1 root root 64 Feb 15 02:35 0 -> /dev/null
    lrwx------ 1 root root 64 Feb 15 02:35 1 -> /dev/null
    l-wx------ 1 root root 64 Feb 15 02:35 2 -> /1
    lrwx------ 1 root root 64 Feb 15 02:35 3 -> socket:[63800]
    l-wx------ 1 root root 64 Feb 15 02:35 4 -> /1
    l-wx------ 1 root root 64 Feb 15 02:35 5 -> /1
    lrwx------ 1 root root 64 Feb 15 02:35 6 -> socket:[63798]
    lrwx------ 1 root root 64 Feb 15 02:35 7 -> socket:[63801]
  • limits 当前进程所使用的每一个受限资源的软限制、硬限制和管理单元;此文件仅可由实际启动当前进程的UID用户读取

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    root@c27f523688ea:/proc/17# cat 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 0 unlimited bytes
    Max resident set unlimited unlimited bytes
    Max processes 1048576 1048576 processes
    Max open files 1048576 1048576 files
    Max locked memory 65536 65536 bytes
    Max address space unlimited unlimited bytes
    Max file locks unlimited unlimited locks
    Max pending signals 3867 3867 signals
    Max msgqueue size 819200 819200 bytes
    Max nice priority 0 0
    Max realtime priority 0 0
    Max realtime timeout unlimited unlimited us

再运行 ulimit ,因为用户都是root,所以 ulimit应该和这个limits表示的内容是相同的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
root@c27f523688ea:/proc/17# ulimit -a
core file size (blocks, -c) 0
data seg size (kbytes, -d) unlimited
scheduling priority (-e) 0
file size (blocks, -f) unlimited
pending signals (-i) 3867
max locked memory (kbytes, -l) 64
max memory size (kbytes, -m) unlimited
open files (-n) 1048576
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) 1048576
virtual memory (kbytes, -v) unlimited
file locks (-x) unlimited
  • maps — 当前进程关联到的每个可执行文件和库文件在内存中的映射区域及其访问权限所组成的列表;
    看不懂 先过了
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
67
68
69
root@c27f523688ea:/proc/17# cat maps
00400000-004c1000 r-xp 00000000 00:20 92 /usr/sbin/nginx
006c0000-006c1000 r--p 000c0000 00:20 92 /usr/sbin/nginx
006c1000-006d7000 rw-p 000c1000 00:20 92 /usr/sbin/nginx
006d7000-006e6000 rw-p 00000000 00:00 0
01c28000-01c85000 rw-p 00000000 00:00 0 [heap]
7fb1c3d4f000-7fb1c3d5a000 r-xp 00000000 00:20 47 /lib/x86_64-linux-gnu/libnss_files-2.13.so
7fb1c3d5a000-7fb1c3f59000 ---p 0000b000 00:20 47 /lib/x86_64-linux-gnu/libnss_files-2.13.so
7fb1c3f59000-7fb1c3f5a000 r--p 0000a000 00:20 47 /lib/x86_64-linux-gnu/libnss_files-2.13.so
7fb1c3f5a000-7fb1c3f5b000 rw-p 0000b000 00:20 47 /lib/x86_64-linux-gnu/libnss_files-2.13.so
7fb1c3f5b000-7fb1c3f65000 r-xp 00000000 00:20 45 /lib/x86_64-linux-gnu/libnss_nis-2.13.so
7fb1c3f65000-7fb1c4164000 ---p 0000a000 00:20 45 /lib/x86_64-linux-gnu/libnss_nis-2.13.so
7fb1c4164000-7fb1c4165000 r--p 00009000 00:20 45 /lib/x86_64-linux-gnu/libnss_nis-2.13.so
7fb1c4165000-7fb1c4166000 rw-p 0000a000 00:20 45 /lib/x86_64-linux-gnu/libnss_nis-2.13.so
7fb1c4166000-7fb1c417b000 r-xp 00000000 00:20 43 /lib/x86_64-linux-gnu/libnsl-2.13.so
7fb1c417b000-7fb1c437a000 ---p 00015000 00:20 43 /lib/x86_64-linux-gnu/libnsl-2.13.so
7fb1c437a000-7fb1c437b000 r--p 00014000 00:20 43 /lib/x86_64-linux-gnu/libnsl-2.13.so
7fb1c437b000-7fb1c437c000 rw-p 00015000 00:20 43 /lib/x86_64-linux-gnu/libnsl-2.13.so
7fb1c437c000-7fb1c437e000 rw-p 00000000 00:00 0
7fb1c437e000-7fb1c4385000 r-xp 00000000 00:20 41 /lib/x86_64-linux-gnu/libnss_compat-2.13.so
7fb1c4385000-7fb1c4584000 ---p 00007000 00:20 41 /lib/x86_64-linux-gnu/libnss_compat-2.13.so
7fb1c4584000-7fb1c4585000 r--p 00006000 00:20 41 /lib/x86_64-linux-gnu/libnss_compat-2.13.so
7fb1c4585000-7fb1c4586000 rw-p 00007000 00:20 41 /lib/x86_64-linux-gnu/libnss_compat-2.13.so
7fb1c4586000-7fb1c4588000 r-xp 00000000 00:20 34 /lib/x86_64-linux-gnu/libdl-2.13.so
7fb1c4588000-7fb1c4788000 ---p 00002000 00:20 34 /lib/x86_64-linux-gnu/libdl-2.13.so
7fb1c4788000-7fb1c4789000 r--p 00002000 00:20 34 /lib/x86_64-linux-gnu/libdl-2.13.so
7fb1c4789000-7fb1c478a000 rw-p 00003000 00:20 34 /lib/x86_64-linux-gnu/libdl-2.13.so
7fb1c478a000-7fb1c490b000 r-xp 00000000 00:20 36 /lib/x86_64-linux-gnu/libc-2.13.so
7fb1c490b000-7fb1c4b0b000 ---p 00181000 00:20 36 /lib/x86_64-linux-gnu/libc-2.13.so
7fb1c4b0b000-7fb1c4b0f000 r--p 00181000 00:20 36 /lib/x86_64-linux-gnu/libc-2.13.so
7fb1c4b0f000-7fb1c4b10000 rw-p 00185000 00:20 36 /lib/x86_64-linux-gnu/libc-2.13.so
7fb1c4b10000-7fb1c4b15000 rw-p 00000000 00:00 0
7fb1c4b15000-7fb1c4b2b000 r-xp 00000000 00:20 519 /lib/x86_64-linux-gnu/libz.so.1.2.7
7fb1c4b2b000-7fb1c4d2a000 ---p 00016000 00:20 519 /lib/x86_64-linux-gnu/libz.so.1.2.7
7fb1c4d2a000-7fb1c4d2b000 r--p 00015000 00:20 519 /lib/x86_64-linux-gnu/libz.so.1.2.7
7fb1c4d2b000-7fb1c4d2c000 rw-p 00016000 00:20 519 /lib/x86_64-linux-gnu/libz.so.1.2.7
7fb1c4d2c000-7fb1c4ef6000 r-xp 00000000 00:20 517 /usr/lib/x86_64-linux-gnu/libcrypto.so.1.0.0
7fb1c4ef6000-7fb1c50f6000 ---p 001ca000 00:20 517 /usr/lib/x86_64-linux-gnu/libcrypto.so.1.0.0
7fb1c50f6000-7fb1c5111000 r--p 001ca000 00:20 517 /usr/lib/x86_64-linux-gnu/libcrypto.so.1.0.0
7fb1c5111000-7fb1c5120000 rw-p 001e5000 00:20 517 /usr/lib/x86_64-linux-gnu/libcrypto.so.1.0.0
7fb1c5120000-7fb1c5124000 rw-p 00000000 00:00 0
7fb1c5124000-7fb1c517a000 r-xp 00000000 00:20 516 /usr/lib/x86_64-linux-gnu/libssl.so.1.0.0
7fb1c517a000-7fb1c537a000 ---p 00056000 00:20 516 /usr/lib/x86_64-linux-gnu/libssl.so.1.0.0
7fb1c537a000-7fb1c537d000 r--p 00056000 00:20 516 /usr/lib/x86_64-linux-gnu/libssl.so.1.0.0
7fb1c537d000-7fb1c5384000 rw-p 00059000 00:20 516 /usr/lib/x86_64-linux-gnu/libssl.so.1.0.0
7fb1c5384000-7fb1c53c0000 r-xp 00000000 00:20 514 /lib/x86_64-linux-gnu/libpcre.so.3.13.1
7fb1c53c0000-7fb1c55c0000 ---p 0003c000 00:20 514 /lib/x86_64-linux-gnu/libpcre.so.3.13.1
7fb1c55c0000-7fb1c55c1000 rw-p 0003c000 00:20 514 /lib/x86_64-linux-gnu/libpcre.so.3.13.1
7fb1c55c1000-7fb1c55c9000 r-xp 00000000 00:20 512 /lib/x86_64-linux-gnu/libcrypt-2.13.so
7fb1c55c9000-7fb1c57c8000 ---p 00008000 00:20 512 /lib/x86_64-linux-gnu/libcrypt-2.13.so
7fb1c57c8000-7fb1c57c9000 r--p 00007000 00:20 512 /lib/x86_64-linux-gnu/libcrypt-2.13.so
7fb1c57c9000-7fb1c57ca000 rw-p 00008000 00:20 512 /lib/x86_64-linux-gnu/libcrypt-2.13.so
7fb1c57ca000-7fb1c57f8000 rw-p 00000000 00:00 0
7fb1c57f8000-7fb1c580f000 r-xp 00000000 00:20 73 /lib/x86_64-linux-gnu/libpthread-2.13.so
7fb1c580f000-7fb1c5a0e000 ---p 00017000 00:20 73 /lib/x86_64-linux-gnu/libpthread-2.13.so
7fb1c5a0e000-7fb1c5a0f000 r--p 00016000 00:20 73 /lib/x86_64-linux-gnu/libpthread-2.13.so
7fb1c5a0f000-7fb1c5a10000 rw-p 00017000 00:20 73 /lib/x86_64-linux-gnu/libpthread-2.13.so
7fb1c5a10000-7fb1c5a14000 rw-p 00000000 00:00 0
7fb1c5a14000-7fb1c5a34000 r-xp 00000000 00:20 29 /lib/x86_64-linux-gnu/ld-2.13.so
7fb1c5c2a000-7fb1c5c2f000 rw-p 00000000 00:00 0
7fb1c5c30000-7fb1c5c31000 rw-s 00000000 00:01 63799 /dev/zero (deleted)
7fb1c5c31000-7fb1c5c33000 rw-p 00000000 00:00 0
7fb1c5c33000-7fb1c5c34000 r--p 0001f000 00:20 29 /lib/x86_64-linux-gnu/ld-2.13.so
7fb1c5c34000-7fb1c5c35000 rw-p 00020000 00:20 29 /lib/x86_64-linux-gnu/ld-2.13.so
7fb1c5c35000-7fb1c5c36000 rw-p 00000000 00:00 0
7ffe5a572000-7ffe5a593000 rw-p 00000000 00:00 0 [stack]
7ffe5a5e3000-7ffe5a5e5000 r--p 00000000 00:00 0 [vvar]
7ffe5a5e5000-7ffe5a5e7000 r-xp 00000000 00:00 0 [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]
  • mem 当前进程所占用的内存空间,由open、read和lseek等系统调用使用,不能被用户读取;
  • root 指向当前进程运行根目录的符号链接;在Unix和Linux系统上,通常采用chroot命令使每个进程运行于独立的根目录;
  • stat 当前进程的状态信息,包含一系统格式化后的数据列,可读性差,通常由ps命令使用;

    1
    2
    3
    root@c27f523688ea:/proc/17# more stat
    17 (nginx) S 1 17 17 0 -1 4218944 49 0 0 0 0 0 0 0 20 0 1 0 5428469 31825920 195 18446744073709551615 4194304 4982492 140730414209264 140730414208128 140401482384618 0 0 1073745920
    402745863 0 0 0 17 0 0 0 0 0 0 7081352 7168288 29523968 140730414214953 140730414214959 140730414214959 140730414215144 0
  • status 与stat所提供信息类似,但可读性较好,如下所示,每行表示一个属性信息;其详细介绍请参见 proc的man手册页;

    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
    root@c27f523688ea:/proc/17# more status
    Name: nginx
    State: S (sleeping)
    Tgid: 17
    Ngid: 0
    Pid: 17
    PPid: 1
    TracerPid: 0
    Uid: 0 0 0 0
    Gid: 0 0 0 0
    FDSize: 64
    Groups:
    NStgid: 17
    NSpid: 17
    NSpgid: 17
    NSsid: 17
    VmPeak: 31080 kB
    VmSize: 31080 kB
    VmLck: 0 kB
    VmPin: 0 kB
    VmHWM: 780 kB
    VmRSS: 780 kB
    VmData: 724 kB
    VmStk: 136 kB
    VmExe: 772 kB
    VmLib: 4500 kB
    VmPTE: 76 kB
    VmPMD: 12 kB
    VmSwap: 0 kB
    Threads: 1
    SigQ: 0/3867
    SigPnd: 0000000000000000
    ShdPnd: 0000000000000000
    SigBlk: 0000000000000000
    SigIgn: 0000000040001000
    SigCgt: 0000000198016a07
    CapInh: 00000000a80425fb
    CapPrm: 00000000a80425fb
    CapEff: 00000000a80425fb
    CapBnd: 00000000a80425fb
    Seccomp: 0
    Cpus_allowed: 1
    Cpus_allowed_list: 0
    Mems_allowed: 1
    Mems_allowed_list: 0
    voluntary_ctxt_switches: 1
    --More--(0%)
  • task 目录文件,包含由当前进程所运行的每一个线程的相关信息,每个线程的相关信息文件均保存在一个由线程号(tid)命名的目录中,这类似于其内容类似于每个进程目录中的内容

/proc目录下常见的文件介绍

  • /proc/apm 高级电源管理(APM)版本信息及电池相关状态信息,通常由apm命令使用(容器中没有)
  • /proc/buddyinfo 用于诊断内存碎片问题的相关信息文件(容器中没有)
  • /proc/cmdline 在启动时传递至内核的相关参数信息,这些信息通常由lilo或grub等启动管理工具进行传递
  • /proc/cpuinfo 处理器的相关信息的文件
  • /proc/crypto 系统上已安装的内核使用的密码算法及每个算法的详细信息列表
  • /proc/devices 系统已经加载的所有块设备和字符设备的信息,包含主设备号和设备组(与主设备号对应的设备类型)名
  • /proc/diskstats 每块磁盘设备的磁盘I/O统计信息列表
  • /proc/dma 每个正在使用且注册的ISA DMA通道的信息列表
  • /proc/execdomains 内核当前支持的执行域(每种操作系统独特“个性”)信息列表
  • /proc/fb 帧缓冲设备列表文件,包含帧缓冲设备的设备号和相关驱动信息;
  • /proc/filesystems 当前被内核支持的文件系统类型列表文件,被标示为nodev的文件系统表示不需要块设备的支持;通常mount一个设备时,如果没有指定文件系统类型将通过此文件来决定其所需文件系统的类型
  • /proc/interrupts X86或X86_64体系架构系统上每个IRQ相关的中断号列表;多路处理器平台上每个CPU对于每个I/O设备均有自己的中断号
  • /proc/iomem 每个物理设备上的记忆体(RAM或者ROM)在系统内存中的映射信息
  • /proc/ioports 当前正在使用且已经注册过的与物理设备进行通讯的输入-输出端口范围信息列表;如下面所示,第一列表示注册的I/O端口范围,其后表示相关的设备
  • /proc/kallsyms 模块管理工具用来动态链接或绑定可装载模块的符号定义,由内核输出;通常这个文件中的信息量相当大;
  • /proc/kcore 系统使用的物理内存,以ELF核心文件(core file)格式存储,其文件大小为已使用的物理内存(RAM)加上4KB;这个文件用来检查内核数据结构的当前状态,因此,通常由GDB通常调试工具使用,但不能使用文件查看命令打开此文件
  • /proc/kmsg 此文件用来保存由内核输出的信息,通常由/sbin/klogd或/bin/dmsg等程序使用,不要试图使用查看命令打开此文件
  • /proc/locks 保存当前由内核锁定的文件的相关信息,包含内核内部的调试数据;每个锁定占据一行,且具有一个惟一的编号;如下输出信息中每行的第二列表示当前锁定使用的锁定类别,POSIX表示目前较新类型的文件锁,由lockf系统调用产生,FLOCK是传统的UNIX文件锁,由flock系统调用产生;第三列也通常由两种类型,ADVISORY表示不允许其他用户锁定此文件,但允许读取,MANDATORY表示此文件锁定期间不允许其他用户任何形式的访问;
  • /proc/meminfo 系统中关于当前内存的利用状况等的信息,常由free命令使用;可以使用文件查看命令直接读取此文件,其内容显示为两列,前者为统计属性,后者为对应的值

    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
    root@c27f523688ea:/proc# cat meminfo
    MemTotal: 1020096 kB
    MemFree: 746572 kB
    MemAvailable: 802920 kB
    Buffers: 41512 kB
    Cached: 139888 kB
    SwapCached: 0 kB
    Active: 135888 kB
    Inactive: 92792 kB
    Active(anon): 83496 kB
    Inactive(anon): 90648 kB
    Active(file): 52392 kB
    Inactive(file): 2144 kB
    Unevictable: 0 kB
    Mlocked: 0 kB
    SwapTotal: 1182172 kB
    SwapFree: 1182172 kB
    Dirty: 8 kB
    Writeback: 0 kB
    AnonPages: 47276 kB
    Mapped: 29256 kB
    Shmem: 126868 kB
    Slab: 28464 kB
    SReclaimable: 16392 kB
    SUnreclaim: 12072 kB
    KernelStack: 2432 kB
    PageTables: 1616 kB
    NFS_Unstable: 0 kB
    Bounce: 0 kB
    WritebackTmp: 0 kB
    CommitLimit: 1692220 kB
    Committed_AS: 371556 kB
    VmallocTotal: 34359738367 kB
    VmallocUsed: 8908 kB
    VmallocChunk: 34359699820 kB
    AnonHugePages: 32768 kB
    HugePages_Total: 0
    HugePages_Free: 0
    HugePages_Rsvd: 0
    HugePages_Surp: 0
    Hugepagesize: 2048 kB
    DirectMap4k: 47040 kB
    DirectMap2M: 1001472 kB
  • /proc/mounts 在内核2.4.29版本以前,此文件的内容为系统当前挂载的所有文件系统,在2.4.19以后的内核中引进了每个进程使用独立挂载名称空间的方式,此文件则随之变成了指向/proc/self/mounts(每个进程自身挂载名称空间中的所有挂载点列表)文件的符号链接;/proc/self是一个独特的目录,后文中会对此目录进行介绍

1
2
root@c27f523688ea:/proc# ls -al mounts
lrwxrwxrwx 1 root root 11 Feb 15 06:18 mounts -> self/mounts

其中第一列表示挂载的设备,第二列表示在当前目录树中的挂载点,第三点表示当前文件系统的类型,第四列表示挂载属性(ro或者rw),第五列和第六列用来匹配/etc/mtab文件中的转储(dump)属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
root@c27f523688ea:/proc# more mounts
none / aufs rw,relatime,si=6bfa28fca2bd64f1,dio,dirperm1 0 0
proc /proc proc rw,nosuid,nodev,noexec,relatime 0 0
tmpfs /dev tmpfs rw,nosuid,mode=755 0 0
devpts /dev/pts devpts rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=666 0 0
sysfs /sys sysfs ro,nosuid,nodev,noexec,relatime 0 0
tmpfs /sys/fs/cgroup tmpfs ro,nosuid,nodev,noexec,relatime,mode=755 0 0
cgroup /sys/fs/cgroup/cpuset cgroup ro,nosuid,nodev,noexec,relatime,cpuset 0 0
cgroup /sys/fs/cgroup/cpu cgroup ro,nosuid,nodev,noexec,relatime,cpu 0 0
cgroup /sys/fs/cgroup/cpuacct cgroup ro,nosuid,nodev,noexec,relatime,cpuacct 0 0
cgroup /sys/fs/cgroup/blkio cgroup ro,nosuid,nodev,noexec,relatime,blkio 0 0
cgroup /sys/fs/cgroup/memory cgroup ro,nosuid,nodev,noexec,relatime,memory 0 0
cgroup /sys/fs/cgroup/devices cgroup ro,nosuid,nodev,noexec,relatime,devices 0 0
cgroup /sys/fs/cgroup/freezer cgroup ro,nosuid,nodev,noexec,relatime,freezer 0 0
cgroup /sys/fs/cgroup/net_cls cgroup ro,nosuid,nodev,noexec,relatime,net_cls 0 0
cgroup /sys/fs/cgroup/perf_event cgroup ro,nosuid,nodev,noexec,relatime,perf_event 0 0
cgroup /sys/fs/cgroup/net_prio cgroup ro,nosuid,nodev,noexec,relatime,net_prio 0 0
cgroup /sys/fs/cgroup/hugetlb cgroup ro,nosuid,nodev,noexec,relatime,hugetlb 0 0

可以看到/proc 使用的是proc文件系统,大多数的ubuntu镜像使用的是aufs文件系统,当然可以在启动镜像时的时候指定storage driver

  • /proc/modules 当前装入内核的所有模块名称列表,可以由lsmod命令使用,也可以直接查看;如下所示,其中第一列表示模块名,第二列表示此模块占用内存空间大小,第三列表示此模块有多少实例被装入,第四列表示此模块依赖于其它哪些模块,第五列表示此模块的装载状态(Live:已经装入;Loading:正在装入;Unloading:正在卸载),第六列表示此模块在内核内存(kernel memory)中的偏移量

    1
    2
    3
    4
    5
    root@c27f523688ea:/proc# cat modules
    veth 16384 0 - Live 0xffffffffa0173000
    xt_conntrack 16384 1 - Live 0xffffffffa0152000
    ipt_MASQUERADE 16384 1 - Live 0xffffffffa014d000
    nf_nat_masquerade_ipv4 16384 1 ipt_MASQUERADE, Live 0xffffffffa01a5000
  • proc/partitions 块设备每个分区的主设备号(major)和次设备号(minor)等信息,同时包括每个分区所包含的块(block)数目(如下面输出中第三列所示)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    root@c27f523688ea:/proc# more partitions
    major minor #blocks name
    1 0 65535 ram0
    1 1 65535 ram1
    1 2 65535 ram2
    1 3 65535 ram3
    1 4 65535 ram4
    1 5 65535 ram5
    1 6 65535 ram6
    1 7 65535 ram7
    250 0 194216 zram0
    11 0 30720 sr0
    8 0 20480000 sda
    8 1 19486845 sda1
    8 2 987966 sda2
  • /proc/pci
    内核初始化时发现的所有PCI设备及其配置信息列表,其配置信息多为某PCI设备相关IRQ信息,可读性不高,可以用“/sbin/lspci –vb”命令获得较易理解的相关信息;在2.6内核以后,此文件已为/proc/bus/pci目录及其下的文件代替

    • /proc/slabinfo 在内核中频繁使用的对象(如inode、dentry等)都有自己的cache,即slab pool,而/proc/slabinfo文件列出了这些对象相关slap的信息;详情可以参见内核文档中slapinfo的手册页
    • /proc/stat
      实时追踪自系统上次启动以来的多种统计信息;如下所示,其中

      “cpu”行后的八个值分别表示以1/100(jiffies)秒为单位的统计值(包括系统运行于用户模式、低优先级用户模式,运系统模式、空闲模式、I/O等待模式的时间等)

      “intr”行给出中断的信息,第一个为自系统启动以来,发生的所有的中断的次数;然后每个数对应一个特定的中断自系统启动以来所发生的次数;

      “ctxt”给出了自系统启动以来CPU发生的上下文交换的次数。

      “btime”给出了从系统启动到现在为止的时间,单位为秒;

      “processes (total_forks) 自系统启动以来所创建的任务的个数目;

      “procs_running”:当前运行队列的任务的数目;

      “procs_blocked”:当前被阻塞的任务的数目;

    • /proc/swaps 当前系统上的交换分区及其空间利用信息,如果有多个交换分区的话,则会每个交换分区的信息分别存储于/proc/swap目录中的单独文件中,而其优先级数字越低,被使用到的可能性越大
1
2
3
4
root@c27f523688ea:/proc# cat swaps
Filename Type Size Used Priority
/dev/zram0 partition 194212 0 -1
/dev/sda2 partition 987960 0 -2
  • /proc/uptime 系统上次启动以来的运行时间,如下所示,其第一个数字表示系统运行时间,第二个数字表示系统空闲时间,单位是秒;
1
2
root@c27f523688ea:/proc# cat uptime
70344.11 70304.83
  • /proc/version
    当前系统运行的内核版本号,还会显示系统安装的gcc版本,如下所示;
1
2
root@c27f523688ea:/proc# cat version
Linux version 4.1.13-boot2docker (root@11aafb97cfeb) (gcc version 4.9.2 (Debian 4.9.2-10) ) #1 SMP Fri Nov 20 19:05:50 UTC 2015
  • /proc/vmstat
    当前系统虚拟内存的多种统计数据,信息量可能会比较大,这因系统而有所不同,可读性较好
  • /proc/zoneinfo 内存区域(zone)的详细信息列表,信息量较大

/proc/sys目录详解

与 /proc下其它文件的“只读”属性不同的是,管理员可对/proc/sys子目录中的许多文件内容进行修改以更改内核的运行特性,事先可以使用“ls -l”命令查看某文件是否“可写入”。写入操作通常使用类似于“echo DATA > /path/to/your/filename”的格式进行。需要注意的是,即使文件可写,其一般也不可以使用编辑器进行编辑。

  • /proc/sys/debug 子目录
    此目录通常是一空目录;

  • /proc/sys/dev 子目录
    为系统上特殊设备提供参数信息文件的目录,其不同设备的信息文件分别存储于不同的子目录中,如大多数系统上都会具有的/proc/sys/dev /cdrom和/proc/sys/dev/raid(如果内核编译时开启了支持raid的功能) 目录,其内存储的通常是系统上cdrom和raid的相关参数信息文件。

照着搬过来很多内容,了解了一些,一些还是不懂,写博客的好处,就是可以温故而知新

最近找了本docker的书Using Docker
读了大半部分了,整理了一下一些很有用的Docker相关笔记,顺便把以前做的一些问题修改了一下,整套脚本构建放到了github上。

  • docker rm $(docker ps -aq)
    可以清除所有停止的docker 容器,运行中的不会被清理,同理,docker rmi $(docker images -aq)也可以清理,但是请谨慎使用,因为会把很多有用的‘基础’容器删除,构建image时还要重新下载。所以这里可以使用–filter 和awk grep等命令协同处理(因为有时候docker产生的无用image太多了)
  • 尽量不要指定HOST_DIR 挂载,因为这会影响到宿主机的文件
    如果开发dev环境,可以指定宿主机挂载,注意container的运行权限,尽量不要对挂载目录有写的权限,运行时会产生一些缓存文件,这些缓存目录可以设置成777 并在git config里忽略文件的权限
  • CTRL P +CTRL Q
    这个命令是attach到任何容器后不想退出容器,而是进行detach
  • 对于简单的复制,可以使用copy 替代 add,add可以对文件进行解压缩,实际上是对流进行操作
  • 不要使用root运行 因为有时候你可能会使用挂载,而root有权限对挂载目录进行写操作,从而造成一些不必要的问题
  • .DOCKERIGNORE 类似gitignore,你可以添加一个 .dockerignore 文件到你的 Dockerfile , Docker 将会在发送构建上下文到守护进程时忽略在 .dockerignore 中指定的文件和目录
  • 数据尽量使用单独的image,不要混合使用
  • docker save 和 export的区别是save是多layer export只有一个layer (个人使用较少,export 还会丢失一些环境变量)
  • RUN 使用&&减少layer的数量,这样在同一层使用某文件后并删除可以控制image的大小。这也是我的构建脚本从900MB减少的500MB的原因,而且可以继续减少(去除apt安装的一些编译头文件)
  • compose 的yml可以使用extends 子yml里的配置会覆盖父yml的配置,links 和 volumes-from 不会被继承(docker-compose 个人不是很喜欢,个人比较喜欢shell,方便可控)
  • CMD和ENTRYPOINT的区别
    CMD和ENTRYPOINT是在运行container 时会运行的指令, 都只能写一条, 如果写了多条, 则最后一条生效.
    CMD在运行时会被command覆盖, ENTRYPOINT不会被运行时的command覆盖, 但是也可以指定.Docker RUN CMD 和ENTRYPOINT 我对ENTRYPOINT的使用也不是很多
  • 不要把key放到docker里
    安全原因,可以把它写到shell里,运行时通过shell添加;也可以通过配置中心获取,比如consul等
  • docker 日志输出
    logspout 可以将docker 产生的log 输出到任何地方,可以使用elk将这些收集整理 最近闲暇时间也学习了一下elk,也搭建了一套本地elk环境,以后该考虑一下应用到生产环境
  • 监控docker cpu等信息
    docker stats $(docker inspect -f {{.Name}} $(docker ps -q))
    

继续说说docker laravel环境遇到的一些问题,因为需要将ecshop的代码迁移到laravel里,也就意味着 php版本也要做相应的升级,问题不少,所以搭建了一套laravel的也搭建了一套ecshop的,但是ecshop的nginx涉及到私有问题,就不传出来了。思想都是一样的。最后说说一个折腾了很久的问题吧,搭建docker的环境,主要是为了分离各服务,也就是所谓的微服务microservice,但是使用时,nginx对php-fpm进行link和volumn,发现端口并不能被转发到PHP-fpm的socket端口,nginx一直报502.由于nginx使用的是官方提供的版本,系统精简到ps,netstat,telnet等一些常用的命令都没有,加大了调试难度,在调试过程中个人也从中学到了很多linux系统的一些知识。最后在php-fpm容器中使用netstat发现Local Address是127.0.0.1:9000,也就是说只会监听本地的端口,遂查看php-fpm的config,将127.0.0.1:9000替换为9000,使其可以被外网访问到,当然容器中才可以这样使用,因为容器本身并没有把端口暴露给外部,而是暴露给使用了link该容器的容器。

文章转自服务器TIME_WAIT和CLOSE_WAIT详解和解决办法
在服务器的日常维护过程中,会经常用到下面的命令:

1
netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'

它会显示例如下面的信息:
TIME_WAIT 814
CLOSE_WAIT 1
FIN_WAIT1 1
ESTABLISHED 634
SYN_RECV 2
LAST_ACK 1
常用的三个状态是:ESTABLISHED 表示正在通信,TIME_WAIT 表示主动关闭,CLOSE_WAIT 表示被动关闭。
具体每种状态什么意思,其实无需多说,看看下面这种图就明白了,注意这里提到的服务器应该是业务请求接受处理的一方:
image
这么多状态不用都记住,只要了解到我上面提到的最常见的三种状态的意义就可以了。一般不到万不得已的情况也不会去查看网络状态,如果服务器出了异常,百分之八九十都是下面两种情况:

1.服务器保持了大量TIME_WAIT状态
2.服务器保持了大量CLOSE_WAIT状态

因为linux分配给一个用户的文件句柄是有限的(可以参考这篇文章,而TIME_WAIT和CLOSE_WAIT两种状态如果一直被保持,那么意味着对应数目的通道就一直被占着,而且是“占着茅坑不使劲”,一旦达到句柄数上限,新的请求就无法被处理了,接着就是大量Too Many Open Files异常,tomcat崩溃。。。

下面来讨论下这两种情况的处理方法,网上有很多资料把这两种情况的处理方法混为一谈,以为优化系统内核参数就可以解决问题,其实是不恰当的,优化系统内核参 数解决TIME_WAIT可能很容易,但是应对CLOSE_WAIT的情况还是需要从程序本身出发。现在来分别说说这两种情况的处理方法:

1.服务器保持了大量TIME_WAIT状态
这种情况比较常见,一些爬虫服务器或者WEB服务器(如果网管在安装的时候没有做内核参数优化的话)上经常会遇到这个问题,这个问题是怎么产生的呢?

从上面的示意图可以看得出来,TIME_WAIT是主动关闭连接的一方保持的状态,对于爬虫服务器来说他本身就是“客户端”,在完成一个爬取任务之后,他就 会发起主动关闭连接,从而进入TIME_WAIT的状态,然后在保持这个状态2MSL(max segment lifetime,RFC 793中规定MSL为2分钟,实际应用中常用的是30秒,1分钟和2分钟等)时间之后,彻底关闭回收资源。为什么要这么做?明明就已经主动关闭连接了为啥还要保持资源一段时间呢?这个是TCP/IP的设计者规定 的,主要出于以下两个方面的考虑:

1.防止上一次连接中的包,迷路后重新出现,影响新连接(经过2MSL,上一次连接中所有的重复包都会消失)

  1. 可靠的关闭TCP连接。在主动关闭方发送的最后一个 ack(fin) ,有可能丢失,这时被动方会重新发fin, 如果这时主动方处于 CLOSED 状态 ,就会响应 rst 而不是 ack。所以主动方要处于 TIME_WAIT 状态,而不能是 CLOSED 。另外这么设计TIME_WAIT 会定时的回收资源,并不会占用很大资源的,除非短时间内接受大量请求或者受到攻击。

关于MSL引用下面一段话:

1
2
3
4
5
MSL 為 一個 TCP Segment (某一塊 TCP 網路封包) 從來源送到目的之間可續存的時間 (也就是一個網路封
包在網路上傳輸時能存活的時間),由 於 RFC 793 TCP 傳輸協定是在 1981 年定義的,當時的網路速度不像
現在的網際網路那樣發達,你可以想像你從瀏覽器輸入網址等到第一 個 byte 出現要等 4 分鐘嗎?在現在的網
路環境下幾乎不可能有這種事情發生,因此我們大可將 TIME_WAIT 狀態的續存時間大幅調低,好 讓 連線埠
(Ports) 能更快空出來給其他連線使用。

再引用网络资源的一段话:

1
2
3
4
5
6
7
值得一说的是,对于基于TCP的HTTP协议,关闭TCP连接的是Server端,这样,Server端会进入TIME_WAIT状
态,可 想而知,对于访 问量大的Web Server,会存在大量的TIME_WAIT状态,假如server一秒钟接收1000
个请求,那么就会积压 240*1000=240000个 TIME_WAIT的记录,维护这些状态给Server带来负担。当然现
代操作系统都会用快速的查找算法来管理这些 TIME_WAIT,所以对于新的 TCP连接请求,判断是否hit中一个
TIME_WAIT不会太费时间,但是有这么多状态要维护总是不好。
HTTP协议1.1版规定default行为是Keep-Alive,也就是会重用TCP连接传输多个 request/response,一
个主要原因就是发现了这个问题。

也就是说HTTP的交互跟上面画的那个图是不一样的,关闭连接的不是客户端,而是服务器,所以web服务器也是会出现大量的TIME_WAIT的情况的。

现在来说如何来解决这个问题。

解决思路很简单,就是让服务器能够快速回收和重用那些TIME_WAIT的资源。

下面来看一下我们网管对/etc/sysctl.conf文件的修改:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#对于一个新建连接,内核要发送多少个 SYN 连接请求才决定放弃,不应该大于255,默认值是5,对应于180秒左右时间
net.ipv4.tcp_syn_retries=2
#net.ipv4.tcp_synack_retries=2
#表示当keepalive起用的时候,TCP发送keepalive消息的频度。缺省是2小时,改为300秒
net.ipv4.tcp_keepalive_time=1200
net.ipv4.tcp_orphan_retries=3
#表示如果套接字由本端要求关闭,这个参数决定了它保持在FIN-WAIT-2状态的时间
net.ipv4.tcp_fin_timeout=30
#表示SYN队列的长度,默认为1024,加大队列长度为8192,可以容纳更多等待连接的网络连接数。
net.ipv4.tcp_max_syn_backlog = 4096
#表示开启SYN Cookies。当出现SYN等待队列溢出时,启用cookies来处理,可防范少量SYN攻击,默认为0,表示关闭
net.ipv4.tcp_syncookies = 1
#表示开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭
net.ipv4.tcp_tw_reuse = 1
#表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭
net.ipv4.tcp_tw_recycle = 1
##减少超时前的探测次数
net.ipv4.tcp_keepalive_probes=5
##优化网络设备接收队列
net.core.netdev_max_backlog=3000

修改完之后执行/sbin/sysctl -p让参数生效。

这里头主要注意到的是

1
2
3
4
net.ipv4.tcp_tw_reuse
net.ipv4.tcp_tw_recycle
net.ipv4.tcp_fin_timeout
net.ipv4.tcp_keepalive_*

这几个参数。
net.ipv4.tcp_tw_reuse和net.ipv4.tcp_tw_recycle的开启都是为了回收处于TIME_WAIT状态的资源。
net.ipv4.tcp_fin_timeout这个时间可以减少在异常情况下服务器从FIN-WAIT-2转到TIME_WAIT的时间。
net.ipv4.tcpkeepalive*一系列参数,是用来设置服务器检测连接存活的相关配置。
关于keepalive的用途可以参考这篇

2.服务器保持了大量CLOSE_WAIT状态
休息一下,喘口气,一开始只是打算说说TIME_WAIT和CLOSE_WAIT的区别,没想到越挖越深,这也是写博客总结的好处,总可以有意外的收获。

TIME_WAIT状态可以通过优化服务器参数得到解决,因为发生TIME_WAIT的情况是服务器自己可控的,要么就是对方连接的异常,要么就是自己没有迅速回收资源,总之不是由于自己程序错误导致的。

是CLOSE_WAIT就不一样了,从上面的图可以看出来,如果一直保持在CLOSE_WAIT状态,那么只有一种情况,就是在对方关闭连接之后服务器程
序自己没有进一步发出ack信号。换句话说,就是在对方连接关闭之后,程序里没有检测到,或者程序压根就忘记了这个时候需要关闭连接,于是这个资源就一直
被程序占着。个人觉得这种情况,通过服务器内核参数也没办法解决,服务器对于程序抢占的资源没有主动回收的权利,除非终止程序运行。

如果你使用的是HttpClient并且你遇到了大量CLOSE_WAIT的情况,那么这篇日志也许对你有用:http://blog.csdn.net/shootyou/article/details/6615051
在那边日志里头我举了个场景,来说明CLOSE_WAIT和TIME_WAIT的区别,这里重新描述一下:
服 务器A是一台爬虫服务器,它使用简单的HttpClient去请求资源服务器B上面的apache获取文件资源,正常情况下,如果请求成功,那么在抓取完 资源后,服务器A会主动发出关闭连接的请求,这个时候就是主动关闭连接,服务器A的连接状态我们可以看到是TIME_WAIT。如果一旦发生异常呢?假设 请求的资源服务器B上并不存在,那么这个时候就会由服务器B发出关闭连接的请求,服务器A就是被动的关闭了连接,如果服务器A被动关闭连接之后程序员忘了 让HttpClient释放连接,那就会造成CLOSE_WAIT的状态了。

所以如果将大量CLOSE_WAIT的解决办法总结为一句话那就是:查代码。因为问题出在服务器程序里头啊。

参考资料:
1.windows下的TIME_WAIT的处理可以参加这位大侠的日志:http://blog.miniasp.com/post/2010/11/17/How-to-deal-with-TIME_WAIT-problem-under-Windows.aspx
2.WebSphere的服务器优化有一定参考价值:http://publib.boulder.ibm.com/infocenter/wasinfo/v6r0/index.jsp?topic=/com.ibm.websphere.express.doc/info/exp/ae/tprf_tunelinux.html
3.各种内核参数的含义:http://haka.sharera.com/blog/BlogTopic/32309.htm
4.linux服务器历险之sysctl优化linux网络:http://blog.csdn.net/chinalinuxzend/article/details/1792184

文章转自Linux strace命令

简介


strace常用来跟踪进程执行时的系统调用和所接收的信号。 在Linux世界,进程不能直接访问硬件设备,当进程需要访问硬件设备(比如读取磁盘文件,接收网络数据等等)时,必须由用户态模式切换至内核态模式,通 过系统调用访问硬件设备。strace可以跟踪到一个进程产生的系统调用,包括参数,返回值,执行消耗的时间。

输出参数含义


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
root@ubuntu:/usr# strace cat /dev/null
execve("/bin/cat", ["cat", "/dev/null"], [/* 22 vars */]) = 0
brk(0) = 0xab1000
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f29379a7000
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
...
brk(0) = 0xab1000
brk(0xad2000) = 0xad2000
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), ...}) = 0
open("/dev/null", O_RDONLY) = 3
fstat(3, {st_mode=S_IFCHR|0666, st_rdev=makedev(1, 3), ...}) = 0
read(3, "", 32768) = 0
close(3) = 0
close(1) = 0
close(2) = 0
exit_group(0) = ?

每一行都是一条系统调用,等号左边是系统调用的函数名及其参数,右边是该调用的返回值。
strace 显示这些调用的参数并返回符号形式的值。strace 从内核接收信息,而且不需要以任何特殊的方式来构建内核。

strace参数

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
-c 统计每一系统调用的所执行的时间,次数和出错的次数等.
-d 输出strace关于标准错误的调试信息.
-f 跟踪由fork调用所产生的子进程.
-ff 如果提供-o filename,则所有进程的跟踪结果输出到相应的filename.pid中,pid是各进程的进程号.
-F 尝试跟踪vfork调用.在-f时,vfork不被跟踪.
-h 输出简要的帮助信息.
-i 输出系统调用的入口指针.
-q 禁止输出关于脱离的消息.
-r 打印出相对时间关于,,每一个系统调用.
-t 在输出中的每一行前加上时间信息.
-tt 在输出中的每一行前加上时间信息,微秒级.
-ttt 微秒级输出,以秒了表示时间.
-T 显示每一调用所耗的时间.
-v 输出所有的系统调用.一些调用关于环境变量,状态,输入输出等调用由于使用频繁,默认不输出.
-V 输出strace的版本信息.
-x 以十六进制形式输出非标准字符串
-xx 所有字符串以十六进制形式输出.
-a column
设置返回值的输出位置.默认 为40.
-e expr
指定一个表达式,用来控制如何跟踪.格式如下:
[qualifier=][!]value1[,value2]...
qualifier只能是 trace,abbrev,verbose,raw,signal,read,write其中之一.value是用来限定的符号或数字.默认的 qualifier是 trace.感叹号是否定符号.例如:
-eopen等价于 -e trace=open,表示只跟踪open调用.而-etrace!=open表示跟踪除了open以外的其他调用.有两个特殊的符号 all 和 none.
注意有些shell使用!来执行历史记录里的命令,所以要使用\\.
-e trace=set
只跟踪指定的系统 调用.例如:-e trace=open,close,rean,write表示只跟踪这四个系统调用.默认的为set=all.
-e trace=file
只跟踪有关文件操作的系统调用.
-e trace=process
只跟踪有关进程控制的系统调用.
-e trace=network
跟踪与网络有关的所有系统调用.
-e strace=signal
跟踪所有与系统信号有关的 系统调用
-e trace=ipc
跟踪所有与进程通讯有关的系统调用
-e abbrev=set
设定 strace输出的系统调用的结果集.-v 等与 abbrev=none.默认为abbrev=all.
-e raw=set
将指 定的系统调用的参数以十六进制显示.
-e signal=set
指定跟踪的系统信号.默认为all.如 signal=!SIGIO(或者signal=!io),表示不跟踪SIGIO信号.
-e read=set
输出从指定文件中读出 的数据.例如:
-e read=3,5
-e write=set
输出写入到指定文件中的数据.
-o filename
将strace的输出写入文件filename
-p pid
跟踪指定的进程pid.
-s strsize
指定输出的字符串的最大长度.默认为32.文件名一直全部输出.
-u username
以username 的UID和GID执行被跟踪的命令

命令实例


通用的完整用法:

strace -o output.txt -T -tt -e trace=all -p 28979
上面的含义是 跟踪28979进程的所有系统调用(-e trace=all),并统计系统调用的花费时间,以及开始时间(并以可视化的时分秒格式显示),最后将记录结果存在output.txt文件里面。

strace案例


用strace调试程序
在理想世界里,每当一个程序不能正常执行一个功能时,它就会给出一个有用的错误提示,告诉你在足够的改正错误的线索。但遗憾的是,我们不是生活在理想世界 里,起码不总是生活在理想世界里。有时候一个程序出现了问题,你无法找到原因。
这就是调试程序出现的原因。strace是一个必不可少的 调试工具,strace用来监视系统调用。你不仅可以调试一个新开始的程序,也可以调试一个已经在运行的程序(把strace绑定到一个已有的PID上 面)。
首先让我们看一个真实的例子:启动KDE时出现问题
前一段时间,我在 启动KDE的时候出了问题,KDE的错误信息无法给我任何有帮助的线索。

1
2
3
_KDE_IceTransSocketCreateListener: failed to bind listener
_KDE_IceTransSocketUNIXCreateListener: ...SocketCreateListener() failed
_KDE_IceTransMakeAllCOTSServerListeners: failed to create listener for local

Cannot establish any listening sockets DCOPServer self-test failed.
对 我来说这个错误信息没有太多意义,只是一个对KDE来说至关重要的负责进程间通信的程序无法启动。我还可以知道这个错误和ICE协议(Inter Client Exchange)有关,除此之外,我不知道什么是KDE启动出错的原因。

我决定采用strace看一下在启动 dcopserver时到底程序做了什么:

strace -f -F -o ~/dcop-strace.txt dcopserver
这里 -f -F选项告诉strace同时跟踪fork和vfork出来的进程,-o选项把所有strace输出写到~/dcop-strace.txt里 面,dcopserver是要启动和调试的程序。

再次出现错误之后,我检查了错误输出文件dcop-strace.txt,文件里有很多 系统调用的记录。在程序运行出错前的有关记录如下:

1
2
3
4
5
6
7
8
9
10
11
27207 mkdir("/tmp/.ICE-unix", 0777) = -1 EEXIST (File exists)
27207 lstat64("/tmp/.ICE-unix", {st_mode=S_IFDIR|S_ISVTX|0755, st_size=4096, ...}) = 0
27207 unlink("/tmp/.ICE-unix/dcop27207-1066844596") = -1 ENOENT (No such file or directory)
27207 bind(3, {sin_family=AF_UNIX, path="/tmp/.ICE-unix/dcop27207-1066844596"}, 38) = -1 EACCES (Permission denied)
27207 write(2, "_KDE_IceTrans", 13) = 13
27207 write(2, "SocketCreateListener: failed to "..., 46) = 46
27207 close(3) = 0 27207 write(2, "_KDE_IceTrans", 13) = 13
27207 write(2, "SocketUNIXCreateListener: ...Soc"..., 59) = 59
27207 umask(0) = 0 27207 write(2, "_KDE_IceTrans", 13) = 13
27207 write(2, "MakeAllCOTSServerListeners: fail"..., 64) = 64
27207 write(2, "Cannot establish any listening s"..., 39) = 39

其中第一行显示程序试图创建/tmp/.ICE-unix目录,权限为0777,这个操作因为目录已经存在而失败了。第二个系统调用(lstat64)检查 了目录状态,并显示这个目录的权限是0755,这里出现了第一个程序运行错误的线索:程序试图创建属性为0777的目录,但是已经存在了一个属性为 0755的目录。第三个系统调用(unlink)试图删除一个文件,但是这个文件并不存在。这并不奇怪,因为这个操作只是试图删掉可能存在的老文件。

但是,第四行确认了错误所在。他试图绑定到/tmp/.ICE-unix/dcop27207-1066844596,但是出现了拒绝访问错误。. ICE_unix目录的用户和组都是root,并且只有所有者具有写权限。一个非root用户无法在这个目录下面建立文件,如果把目录属性改成0777, 则前面的操作有可能可以执行,而这正是第一步错误出现时进行过的操作。

所以我运行了chmod 0777 /tmp/.ICE-unix之后KDE就可以正常启动了,问题解决了,用strace进行跟踪调试只需要花很短的几分钟时间跟踪程序运行,然后检查并分 析输出文件。

说明:运行chmod 0777只是一个测试,一般不要把一个目录设置成所有用户可读写,同时不设置粘滞位(sticky bit)。给目录设置粘滞位可以阻止一个用户随意删除可写目录下面其他人的文件。一般你会发现/tmp目录因为这个原因设置了粘滞位。KDE可以正常启动 之后,运行chmod +t /tmp/.ICE-unix给.ICE_unix设置粘滞位。

解决库依赖问题
starce 的另一个用处是解决和动态库相关的问题。当对一个可执行文件运行ldd时,它会告诉你程序使用的动态库和找到动态库的位置。但是如果你正在使用一个比较老 的glibc版本(2.2或更早),你可能会有一个有bug的ldd程序,它可能会报告在一个目录下发现一个动态库,但是真正运行程序时动态连接程序 (/lib/ld-linux.so.2)却可能到另外一个目录去找动态连接库。这通常因为/etc/ld.so.conf和 /etc/ld.so.cache文件不一致,或者/etc/ld.so.cache被破坏。在glibc 2.3.2版本上这个错误不会出现,可能ld-linux的这个bug已经被解决了。

尽管这样,ldd并不能把所有程序依赖的动态库列出 来,系统调用dlopen可以在需要的时候自动调入需要的动态库,而这些库可能不会被ldd列出来。作为glibc的一部分的NSS(Name Server Switch)库就是一个典型的例子,NSS的一个作用就是告诉应用程序到哪里去寻找系统帐号数据库。应用程序不会直接连接到NSS库,glibc则会通 过dlopen自动调入NSS库。如果这样的库偶然丢失,你不会被告知存在库依赖问题,但这样的程序就无法通过用户名解析得到用户ID了。让我们看一个例子:
whoami程序会给出你自己的用户名,这个程序在一些需要知道运行程序的真正用户的脚本程序里面非常有用,whoami的一个示例 输出如下:

1
2
$ whoami
root

假设因为某种原因在升 级glibc的过程中负责用户名和用户ID转换的库NSS丢失,我们可以通过把nss库改名来模拟这个环境:

1
2
3
$ mv /lib/libnss_files.so.2 /lib/libnss_files.so.2.backup
whoami
whoami: cannot find username for UID 0

这里你可以看到,运行whoami时出现了错误,ldd程序的输出不会提供有用的帮助:

1
2
3
$ ldd /usr/bin/whoami
libc.so.6 => /lib/libc.so.6 (0x4001f000)
/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)

你只会看到whoami依赖Libc.so.6和ld-linux.so.2,它没有给出运行whoami所必须的其他库。这里时用strace跟踪 whoami时的输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
strace -o whoami-strace.txt whoami
open("/lib/libnss_files.so.2", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/lib/i686/mmx/libnss_files.so.2", O_RDONLY) = -1 ENOENT (No such file or directory)
stat64("/lib/i686/mmx", 0xbffff190) = -1 ENOENT (No such file or directory)
open("/lib/i686/libnss_files.so.2", O_RDONLY) = -1 ENOENT (No such file or directory)
stat64("/lib/i686", 0xbffff190) = -1 ENOENT (No such file or directory)
open("/lib/mmx/libnss_files.so.2", O_RDONLY) = -1 ENOENT (No such file or directory)
stat64("/lib/mmx", 0xbffff190) = -1 ENOENT (No such file or directory)
open("/lib/libnss_files.so.2", O_RDONLY) = -1 ENOENT (No such file or directory)
stat64("/lib", {st_mode=S_IFDIR|0755, st_size=2352, ...}) = 0
open("/usr/lib/i686/mmx/libnss_files.so.2", O_RDONLY) = -1 ENOENT (No such file or directory)
stat64("/usr/lib/i686/mmx", 0xbffff190) = -1 ENOENT (No such file or directory)
open("/usr/lib/i686/libnss_files.so.2", O_RDONLY) = -1 ENOENT (No such file or directory)

你可以发现在不同目录下面查找libnss.so.2的尝试,但是都失败了。如果没有strace这样的工具,很难发现这个错误是由于缺少动态库造成的。现 在只需要找到libnss.so.2并把它放回到正确的位置就可以了。 

限制strace只跟踪特定的系统调用


如果你已经知道你要找什么,你可以让strace只跟踪一些类型的系统调用。例如,你需要看看在configure脚本里面执行的程序,你需要监视的系统调 用就是execve。让strace只记录execve的调用用这个命令:

strace -f -o configure-strace.txt -e execve ./configure

看到这篇记一次TIME_WAIT网络故障,想起之前处理线上故障。
当时的情况是公司用的分布式job服务器load过高,杀掉所有的脚本,load就降下去了,但是再启动这些load又上去,重启服务,发现竟然无法启动了,原因是端口被占用了
当时也不知道如何去分析,使用

1
2
netstat -ant | awk '
{++s[$NF]} END {for(k in s) print k,s[k]}

查看,再查看sysctl net.ipv4.ip_local_port_range=”min max”
TIMEWAIT的数量 大约等于max-min,
原因很明显,有脚本大量地占用端口,导致端口没有释放,重启服务端口被占用的原因是因为分布式脚本监控服务有一个监听端口,需要,同时也要发送心跳包到服务器上,而其重启时,由于是高端口,导致被占用,无法释放,服务也无法启动了。
但是并不知道是哪个脚本产生的问题,当时也不知道strace这类命令,用tcpdump去抓包,发现请求solr的http请求特别多,于是去查看代码,原来是shell一个常驻脚本,调用php脚本去执行查询solr服务,做一些简单逻辑后就退出,这样一直循环,因为启动和结束速度非常快,所以load也会飙升上去了。
解决方法,当时也只是在代码层面做了些sleep。系统方面因为不是很熟悉,也没有做优化。如今看了这篇,收货颇丰

这个命令 真是相见恨晚
原文链接
早些年,如果你知道有个 strace 命令,就很牛了,而现在大家基本都知道 strace 了,如果你遇到性能问题求助别人,十有八九会建议你用 strace 挂上去看看,不过当你挂上去了,看着满屏翻滚的字符,却十有八九看不出个所以然。本文通过一个简单的案例,向你展示一下在用 strace 诊断问题时的一些套路。
如下真实案例,如有雷同,实属必然!让我们看一台高负载服务器的 top 结果:
image
技巧:运行 top 时,按「1」打开 CPU 列表,按「shift+p」以 CPU 排序。

在本例中大家很容易发现 CPU 主要是被若干个 PHP 进程占用了,同时 PHP 进程占用的比较多的内存,不过系统内存尚有结余,SWAP 也不严重,这并不是问题主因。

不过在 CPU 列表中能看到 CPU 主要消耗在内核态「sy」,而不是用户态「us」,和我们的经验不符。Linux 操作系统有很多用来跟踪程序行为的工具,内核态的函数调用跟踪用「strace」,用户态的函数调用跟踪用「ltrace」,所以这里我们应该用「strace」:

1
shell> strace -p <PID>

不过如果直接用 strace 跟踪某个进程的话,那么等待你的往往是满屏翻滚的字符,想从这里看出问题的症结并不是一件容易的事情,好在 strace 可以按操作汇总时间:

1
shell> strace -cp <PID>

通过「c」选项用来汇总各个操作的总耗时,运行后的结果大概如下图所示:
image
很明显,我们能看到 CPU 主要被 clone 操作消耗了,还可以单独跟踪一下 clone:

1
shell> strace -T -e clone -p <PID>

通过「T」选项可以获取操作实际消耗的时间,通过「e」选项可以跟踪某个操作:
image

很明显,一个 clone 操作需要几百毫秒,至于 clone 的含义,参考 man 文档:

1
2
3
clone() creates a new process, in a manner similar to fork(2). It is actually a library function layered on top of the underlying clone() system call, hereinafter referred to as sys_clone. A description of sys_clone is given towards the end of this page.
Unlike fork(2), these calls allow the child process to share parts of its execution context with the calling process, such as the memory space, the table of file descriptors, and the table of signal handlers. (Note that on this manual page, “calling process” normally corresponds to “parent process”. But see the description of CLONE_PARENT below.)

简单来说,就是创建一个新进程。那么在 PHP 里什么时候会出现此类系统调用呢?查询业务代码看到了 exec 函数,通过如下命令验证它确实会导致 clone 系统调用:

shell> strace -eclone php -r ‘exec(“ls”);’
最后再考大家一个题:如果我们用 strace 跟踪一个进程,输出结果很少,是不是说明进程很空闲?其实试试 ltrace,可能会发现别有洞天。记住有内核态和用户态之分。

mbp上搭建了docker环境,就完完整整的写个docker for laravel的dev环境吧
(鉴于问题很多 就不贴具体代码了)
有docker环境的直接安装应该就可以了。
需要配置下本地的laravel目录,这样就可以把laravel目录挂载到docker 里了,就可以在本机里修改代码,docker中也会更新。
使用过程也碰到一些坑:

  • docker-machine重启vm后,vm内所有的东西全都没了!!这是最坑的,本来已经写好了,结果重启了一下,什么都没了,2天白干了,索性又花了一天时间去重写了一遍,效率还可以。
  • docker的挂载有些问题,挂载后的目录在docker里看的怪怪的,比如我挂载了一个/data目录,ls -al /data data的用户竟然是10000,让我很迷茫,后来发现,实际上是docker-machine里docker用户的uid
  • 默认的ubuntu 14.04里包很少,而我使用的编译安装,所以很多时候会找不到依赖,然后就要重新build。以前写的时候并没有很在意,最近发现,其实是有技巧的,docker是有类似缓存的,只要在dockerfile最下新增内容,实际上之前build成功的地方是有commit的,直接使用cache

从前并不认为经验很重要,这几天感悟很深,2天写的东西丢失之后,用了仅仅半天就搞定了。由此可见,做过和没做过是有差别的,用心做和做过也是有差别的。
这个环境有很多需要改进的地方,比如脚本路径有些问题;docker的基础环境,个人觉得应该是一层一层build出来的,而不是简单的像我这样,一次性安装完;

花了点时间看书,收获很大,扩展也是真正从这里开始学习的。
豆瓣读书
mobi格式下载
这本书的知识面还是很广的,但是深度还是不够,毕竟也不能全部深入介绍。
有些地方还有错误,细心去实践时候会发现问题,比如sql优化部分,like %xxx 这类sql是无法优化的,作者竟然还列举了优化方法。。。
但是回顾整部书,作为想在php方向突破瓶颈的人,还是值得一看的