Author: ephuizi

  • gcc 编译基础与make

    gcc 编译基础

    gcc common variable

    -l/path 是加入某个library,库的前缀lib和扩展名(.a或.so)不需要写

    -I/path 后面接的路径就是设置要去搜索相关的include文件的目录.

    -Wall 编译的时候输出警告信息

    -O 在编译的时候,依据操作系统环境,对程序进行优化

    gcc default

    • linux 默认是将函数库放置在/lib/usr/lib当中.gcc默认会链接这两个路径下的函数库.
    • 同理,gcc编译会默认会搜索/usr/include目录下的头文件.

    • 通常称-Wall或者-O这些非必要的参数为标志FLAGS,当使用c语言的时候,也可能简称这些标志为CFLAGS

    (more…)

  • my usual vim command

    command

    常用的命令

    shortcuts
    J 将下一行拼接到当前行
    i + ctrl + j 分割成两行
    y 复制到寄存器
    p 从寄存器粘贴
    yy 复制当前行到寄存器
    n+yy 复制从当前行开始的n行到寄存器
    x del
    X del before
    dd 删除当前行
    ^ 光标移动到行首
    $ 光标移动到行末
    gg 光标移动到文件头
    GG 光标移动到文件末行
    u undo
    ctrl+r redo
    :q quit
    :q! quit forcely
    :wq save and quit
    ZZ save and quit
    . replace last command

    command with num

    command with num
    ndd del n line
    d1G del words from current to the head of file
    dG del words from current to the end of file
    d$ del words form current to the end of line
    d0 del words form current to the head of line
    yy
    nyy
    y1G
    yG
    y$
    y0

    还可以在block mode和line mode的情况下使用这些命令.进而对选中块或者选中的行做操作.

    (more…)

  • linux 进程

    进程

    根据维基百科的定义,进程(Process)是计算机中已运行程序的实体。用户下达运行程序的命令后,就会产生进程。进程需要一些资源才能完成工作,如CPU使用时间、存储器、文件以及I/O设备,并且依序逐一进行.也就是每个CPU核心任何时间内仅能运行一项进程。

    PID=1

    有一个PID为1的进程是由内核创建的init进程,其他子进程都是由它衍生出来,进程号为1的进程并没有PPID

    创建

    操作系统通过POSIX定义的forkexec接口创建起一个POSIX进程,这个进程就可以使用通用的IPC、信号等机制。

    原来普通进程运行时默认会绑定TTY(虚拟终端),关闭终端后系统会给上面所有进程发送TERM信号,这时普通进程也就退出了。当然还有些进程不会退出,这就是后面将会提到的守护进程。
    Nohup 的原理也很简单,终端关闭后会给此终端下的每一个进程发送SIGHUP信号,而使用nohup运行的进程则会忽略这个信号,因此终端关闭后进程也不会退出。

    IPC全称Interprocess Communication,指进程间协作的各种方法,当然包括共享内存,信号量或Socket等。

    FD

    每个进程操作系统都会分配三个文件资源,分别是标准输入(STDIN)、标准输出(STDOUT)和错误输出(STDERR)。

    Linux中“一切皆文件”,进程的一切运行信息(占用CPU、内存等)都可以在文件系统找到

    go run

    通过 go run 每次都会启动一个新的Go虚拟机来执行进程。
    因为 go run 会启动进程重新编译、链接和运行程序,因此每次运行的进程名都不相同,而编译出来的程序有明确的名字,

    进程锁

    其实要实现一个进程锁很简单,通过文件就可以实现了。例如程序开始运行时去检查一个PID文件,如果文件存在就直接退出,如果文件不存在就创建一个,并把当前进程的PID写入文件中。

    特殊的进程

    1. 孤儿进程

      根据维基百科的解释,孤儿进程指的是在其父进程执行完成或被终止后仍继续运行的一类进程。

      在现实中用户可能刻意使进程成为孤儿进程,这样就可以让它与父进程会话脱钩,成为后面会介绍的守护进程。

    2. 僵死进程

      一个进程使用fork创建子进程,如果子进程退出,而父进程并没有调用wait或waitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵死进程。

    进程间通信

    1. 管道(Pipe)

      管道是进程间通信最简单的方式,任何进程的标准输出都可以作为其他进程的输入。

    2. 信号(Signal)

      信号Signal,注意信号只是告诉进程发生了什么事件,而不会传递任何数据。

    3. 消息队列(Message)

      和传统消息队列类似,但是在内核实现的。

    4. 共享内存(Shared Memory)

    5. 信号量(Semaphore)

      信号量本质上是一个整型计数器,调用wait时计数减一,减到零开始阻塞进程,从而达到进程、线程间协作的作用。

    6. 套接口(Socket)

      通过网络来通信,这也是最通用的IPC,不要求进程在同一台服务器上。

    信号

    进程发给进程,也可以是内核发给进程

    kill -l
    # HUP INT QUIT ILL TRAP ABRT EMT FPE KILL BUS SEGV SYS PIPE ALRM TERM URG STOP TSTP CONT CHLD TTIN TTOU IO XCPU XFSZ VTALRM PROF WINCH INFO USR1 USR2
    

    其中1至31的信号为传统UNIX支持的信号,是不可靠信号(非实时的),32到63的信号是后来扩充的,称做可靠信号(实时信号)。不可靠信号和可靠信号的区别在于前者不支持排队,可能会造成信号丢失,而后者不会。

    SIGINT 与字符ctrl+c关联,只能结束前台进程.

    SIGTERM KILL命令的默认不带参数发送的信号就是

    SIGTERM,可以被阻塞、处理和忽略.能让程序有好的退出。因为它可以被阻塞

    SIGKILL kill -9 进程号触发.不可被阻塞、忽略.保证进程退出.

    信号表

    index signal desc
    1 SIGHUP 本信号在用户终端连接(正常或非正常)结束时发出, 通常是在终端的控制进程结束时, 通知同一session内的各个作业, 这时它们与控制终端不再关联.
    2 SIGINT 程序终止(interrupt)信号, 在用户键入INTR字符(通常是Ctrl-C)时发出
    3 SIGQUIT 和SIGINT类似, 但由QUIT字符(通常是Ctrl-)来控制. 进程在因收到SIGQUIT退出时会产生core文件, 在这个意义上类似于一个程序错误信号.
    4 SIGILL 执行了非法指令. 通常是因为可执行文件本身出现错误, 或者试图执行数据段.堆栈溢出时也有可能产生这个信号.
    5 SIGTRAP 由断点指令或其它trap指令产生. 由debugger使用.
    6 SIGABRT 程序自己发现错误并调用abort时产生.
    6 SIGIOT 在PDP-11上由iot指令产生, 在其它机器上和SIGABRT一样.
    7 SIGBUS 非法地址, 包括内存地址对齐(alignment)出错. eg: 访问一个四个字长的整数, 但其地址不是4的倍数.
    8 SIGFPE 在发生致命的算术运算错误时发出. 不仅包括浮点运算错误, 还包括溢出及除数为0等其它所有的算术的错误.
    9 SIGKILL 用来立即结束程序的运行. 本信号不能被阻塞, 处理和忽略.
    10 SIGUSR1 留给用户使用
    11 SIGSEGV 试图访问未分配给自己的内存, 或试图往没有写权限的内存地址写数据.
    12 SIGUSR2 留给用户使用
    13 SIGPIPE Broken pipe
    14 SIGALRM 时钟定时信号, 计算的是实际的时间或时钟时间. alarm函数使用该信号.
    15 SIGTERM 程序结束(terminate)信号, 与SIGKILL不同的是该信号可以被阻塞和处理. 通常用来要求程序自己正常退出. shell命令kill缺省产生这个信号.
    17 SIGCHLD 子进程结束时, 父进程会收到这个信号.
    18 SIGCONT 让一个停止(stopped)的进程继续执行. 本信号不能被阻塞. 可以用一个handler来让程序在由stopped状态变为继续执行时完成特定的工作. 例如, 重新显示提示符
    19 SIGSTOP 停止(stopped)进程的执行. 注意它和terminate以及interrupt的区别:该进程还未结束, 只是暂停执行. 本信号不能被阻塞, 处理或忽略.
    20 SIGTSTP 停止进程的运行, 但该信号可以被处理和忽略. 用户键入SUSP字符时(通常是Ctrl-Z)发出这个信号
    21 SIGTTIN 当后台作业要从用户终端读数据时, 该作业中的所有进程会收到SIGTTIN信号. 缺省时这些进程会停止执行.
    22 SIGTTOU 类似于SIGTTIN, 但在写终端(或修改终端模式)时收到.
    23 SIGURG 有”紧急”数据或out-of-band数据到达socket时产生.
    24 SIGXCPU 超过CPU时间资源限制. 这个限制可以由getrlimit/setrlimit来读取/改变
    25 SIGXFSZ 超过文件大小资源限制.
    26 SIGVTALRM 虚拟时钟信号. 类似于SIGALRM, 但是计算的是该进程占用的CPU时间.
    27 SIGPROF 类似于SIGALRM/SIGVTALRM, 但包括该进程用的CPU时间以及系统调用的时间.
    28 SIGWINCH 窗口大小改变时发出.
    29 SIGIO 文件描述符准备就绪, 可以开始进行输入/输出操作.
    30 SIGPWR Power failure

    参考

    understanding linux process

  • 香港阿里云ip路由

    深圳阿里云到香港阿里云
    要经过美国线路…

    香港阿里云到深圳阿里云
    看起来像走的内网到浙江阿里云

    深圳阿里云到香港阿里云

    [root@xxxxx ~]# traceroute 47.52.xx.xx
    traceroute to 47.52.xx.xx (47.52.xx.xx), 30 hops max, 60 byte packets
     1  * * *
     2  11.219.126.105 (11.219.126.105)  6.572 ms 11.219.127.109 (11.219.127.109)  6.830 ms 11.219.126.105 (11.219.126.105)  6.833 ms (美国)
     3  11.219.127.154 (11.219.127.154)  5.981 ms 11.219.127.146 (11.219.127.146)  5.663 ms 11.219.126.154 (11.219.126.154)  5.612 ms
     4  11.177.232.122 (11.177.232.122)  1.963 ms 11.177.232.126 (11.177.232.126)  1.330 ms 11.177.232.114 (11.177.232.114)  2.005 ms
     5  119.38.215.14 (119.38.215.14)  3.413 ms 119.38.215.18 (119.38.215.18)  2.899 ms 119.38.215.6 (119.38.215.6)  2.430 ms (浙江省杭州市 阿里云)
     6  42.120.239.245 (42.120.239.245)  3.323 ms 116.251.113.157 (116.251.113.157)  2.477 ms 42.120.242.229 (42.120.242.229)  2.316 ms(阿里云 骨干网)
     7  183.2.180.25 (183.2.180.25)  3.161 ms 183.2.180.45 (183.2.180.45)  3.215 ms 183.2.180.113 (183.2.180.113)  3.800 ms (广东省深圳市 电信)
     8  183.2.182.113 (183.2.182.113)  4.680 ms 183.2.182.125 (183.2.182.125)  4.164 ms 183.2.182.117 (183.2.182.117)  4.074 ms
     9  119.147.219.253 (119.147.219.253)  8.991 ms 119.147.222.37 (119.147.222.37)  13.056 ms 119.147.223.113 (119.147.223.113)  8.243 ms  (广东省深圳市 电信)
    10  59.43.80.117 (59.43.80.117)  7.419 ms 59.43.80.21 (59.43.80.21)  5.270 ms 59.43.80.117 (59.43.80.117)  7.277 ms (中国 电信骨干网)
    11  59.43.130.122 (59.43.130.122)  7.914 ms 59.43.130.114 (59.43.130.114)  9.977 ms 59.43.130.122 (59.43.130.122)  7.434 ms
    12  59.43.187.146 (59.43.187.146)  7.971 ms 59.43.187.118 (59.43.187.118)  7.456 ms 59.43.187.146 (59.43.187.146)  7.999 ms
    13  59.43.250.82 (59.43.250.82)  14.291 ms 59.43.250.78 (59.43.250.78)  12.687 ms 59.43.187.182 (59.43.187.182)  14.584 ms
    14  59.43.248.146 (59.43.248.146)  14.918 ms 59.43.187.26 (59.43.187.26)  28.773 ms  28.281 ms
    15  203.100.48.130 (203.100.48.130)  21.166 ms 203.100.48.254 (203.100.48.254)  14.935 ms 203.100.48.130 (203.100.48.130)  21.271 ms (香港特别行政区 中国电信)
    16  116.251.72.189 (116.251.72.189)  16.336 ms  14.897 ms 116.251.72.197 (116.251.72.197)  21.698 ms (阿里云 骨干网)
    17  * * *
    18  * * *
    

    香港阿里云到深圳阿里云

    [root@xxxxx ~]# traceroute 120.78.xx.xx
    traceroute to 120.78.xx.xx (120.78.xx.xx), 30 hops max, 60 byte packets
     1  * * *
     2  10.48.253.133 (10.48.253.133)  6.241 ms  6.031 ms  6.475 ms (阿里云内网)
     3  10.48.250.34 (10.48.250.34)  1.744 ms 10.48.248.154 (10.48.248.154)  1.738 ms 10.48.250.26 (10.48.250.26)  25.086 ms
     4  116.251.66.154 (116.251.66.154)  2.129 ms 119.38.214.46 (119.38.214.46)  1.697 ms 116.251.75.78 (116.251.75.78)  1.305 ms (浙江省杭州市 阿里云)
     5  210.48.136.157 (210.48.136.157)  2.035 ms  1.952 ms 203.100.48.129 (203.100.48.129)  1.571 ms (香港特别行政区 中国电信)
     6  59.43.248.121 (59.43.248.121)  1.836 ms 59.43.181.189 (59.43.181.189)  2.189 ms 59.43.187.25 (59.43.187.25)  2.126 ms
     7  59.43.183.109 (59.43.183.109)  7.148 ms  7.397 ms 59.43.187.177 (59.43.187.177)  28.650 ms
     8  59.43.187.137 (59.43.187.137)  8.994 ms 59.43.187.149 (59.43.187.149)  9.453 ms 59.43.187.125 (59.43.187.125)  9.151 ms
     9  59.43.130.113 (59.43.130.113)  7.308 ms 202.97.94.105 (202.97.94.105)  7.856 ms 59.43.130.109 (59.43.130.109)  7.798 ms
    10  202.97.94.133 (202.97.94.133)  9.001 ms 59.43.80.118 (59.43.80.118)  12.044 ms 59.43.80.22 (59.43.80.22)  10.671 ms
    11  119.147.222.14 (119.147.222.14)  10.649 ms 119.147.219.242 (119.147.219.242)  12.039 ms 119.147.219.250 (119.147.219.250)  11.398 ms (中国电信)
    12  183.2.182.114 (183.2.182.114)  15.839 ms 183.2.182.118 (183.2.182.118)  13.557 ms 183.2.182.130 (183.2.182.130)  13.879 ms
    13  183.2.180.70 (183.2.180.70)  14.442 ms 183.2.180.206 (183.2.180.206)  14.536 ms 183.2.180.118 (183.2.180.118)  13.421 ms
    14  42.120.242.234 (42.120.242.234)  15.401 ms * * (阿里云)
    15  119.38.215.9 (119.38.215.9)  14.895 ms  14.363 ms *
    
  • 项目的jvm内存分析

    项目的jvm内存分析

    我们的项目是一个 java 写的高重删多副本二级存储,放在2U4节点服务器上跑,使用万兆交换机相连,写的速度能达到300M/s,但是读的速度只能达到30~40MB/s.分析,读速度之所以这么慢.主要是因为:为了实现重删,我们采用了数据变长分块,生成hash,最后利用全局hash库过滤重复数据,最后数据保存到当前正在使用的map文件.当多个文件同时写入的时候这个过程,把连续的数据变得离散. 使得读取数据的时候,硬盘每一次都是随机读.读性能相当差.为了提高读性能,我们对数据模型做了修改,对入口做了分组,把分组连续的数据做成定长1M块存储.

    重构后,读性能确实上升了一倍,但是程序的稳定性不行.连续写入数据几个小时后,程序性能就开始慢慢往下降,最后直接内存溢出.

    直接内存

    使用htop看程序内存占用,发现占用的内存确实比实际配置的高很多.造成这种原因,因为我们使用了java nio 提供的直接内存接口,这部分内存都不是从堆中分配的:

    1. 显式分配
      ByteBuffer.allocateDirect(int size)
      

      程序里一些循环里面的临时ByteBuffer是使用直接内存生成的.这一部分,都改过来:

      ByteBuffer.allocate
      
    2. 使用了nio内存映射:
          FileChannel.map(MapMode mode,long position, long size)
      

      我们的hash索引是保存在文件里,当布隆过滤器没有命中,使用内存映射来真实读取.因为设计上的原因,基本上不会释放.

    3. jvm参数配置有问题

      -XX:+DisableExplicitGC 
      

      我们使用了DisableExplicitGC,因为大量Direct ByteBuffer 所以最后导致OOM.看了很多文章都建议不要开启.

    DirectMemory 默认的大小是等同于JVM最大堆.所以最终看起来程序内存比我们配置的-Xmx 高很多.

    堆内内存

    1. 开启** G1 gc log**后,查看日志发现有大量的mix gc,感觉这部分影响了系统性能.
      -XX:+UseG1GC -XX:+PrintGCDetails-verbose:gc -Xloggc:gc.log
      
    2. 使用jmx内存监控
          jstatd -J-Djava.security.policy=jstatd.all.policy
      

      监测程序内存情况,发现写入数据的时候old eden会不断往上涨,然后引发mix gc,不断重复这个过程.而Survivor eden不怎么变.就觉得非常奇怪.为什么直接就在old eden分配呢?

      对比老版本,看看有没有相同的情况,老版本在相同的jvm 参数下,跑的时候内存比较稳定.都是在servivor eden 上分配的.这下更苦逼,基本可以说明是:我们改出来的.

    3. 使用jmap 把整个程序的堆内存dump下来,看了看占了堆内存90%的是long[]byte[]再仔细细分,前者主要是用在了布隆过滤器上,后者也多是数据传递时候的一些数组.看不出什么问题.

    4. 偶然,把老版本的堆内存参数-Xmx配置低一些,发现老版本也出现相识的问题.这下好了,原来问题一直有,只是我们现在分配的数据多了,大了,更容易暴露出来.
      重新观察新程序gc,发现刚开始的时候,其实新版本也是在servivor eden上分配的,但是分配的量太多,G1后面就直接在old eden上分配了.

      最后解决方案:把jvm堆内存配置大一倍

      再观察,果然,程序的内存变化正常多了都是在servivor eden上分配,old eden基本能保存不动.

    5. 后面做复制的时候,想提高性能,使用到一些3M的数组.程序有复制任务的时候servivor edenold eden都会涨.
      于是在jvm 启动参数里增加了两个参数,内存回收看起有比较稳定:

      -XX:G1HeapReginSize=16m -XX:G1HeapWastePercent=10
      

      jvm堆中的servivor edenold eden会划分成很多个regin,G1HeapReginSize可以配置每个regin的大小.值必须是2的幂次方,所以只能是2m,4m,8m,16m才会起效.G1HeapWastePercent则是允许一定的堆内存浪费,可回收的内存超过堆得10%时,G1才开始进行GC.

    打开文件数量过多.

    程序持续跑一段时间,系统还会出现打开文件数量过多
    使用命令来看

    ll /proc/pid/fd/ |wc -l
    

    打开使用channel打开的属性文件map,没有及时进行关闭.最终超过了系统允许打开的文件数量.