分类目录归档:Linux开发

linux下tcp keepalive相关参数调整测试

首先说明下面三个和keepalive相关的内核参数以及默认的值

# sysctl -a | grep keepalive
# 在会后一次发送数据包后多久向对方发起探测
net.ipv4.tcp_keepalive_time = 7200
# 在没有收到对方确认时,会按照这个时间间隔再次探测
net.ipv4.tcp_keepalive_intvl = 75
# 在没有收到对方确认时,进行探测的次数
net.ipv4.tcp_keepalive_probes = 9

下面通过在本地环境上测试这些参数,首先将本地的默认keepalive参数进行修改

# sysctl -a | grep keepalive
net.ipv4.tcp_keepalive_intvl = 5
net.ipv4.tcp_keepalive_probes = 9
net.ipv4.tcp_keepalive_time = 20

下载并编译带keepalive功能支持的netcat命令行工具

git clone https://github.com/cyberelf/netcat-keepalive.git
cd netcat-keepalive/
make linux

运行tcpdump进行抓包

tcpdump -iany port 18888

启动服务端监听

./nckl-linux -v4K -l 18888

使用nc去连接

nc -v -p55666 localhost 18888

可以看到抓包内容如下

root@debian:/home/blue# tcpdump -iany port 18888
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on any, link-type LINUX_SLL (Linux cooked), capture size 262144 bytes
15:42:49.840078 IP localhost.55666 > localhost.18888: Flags [S], seq 4147250120, win 65495, options [mss 65495,sackOK,TS val 3777822786 ecr 0,nop,wscale 7], length 0
15:42:49.840104 IP localhost.18888 > localhost.55666: Flags [S.], seq 2015925109, ack 4147250121, win 65483, options [mss 65495,sackOK,TS val 3777822786 ecr 3777822786,nop,wscale 7], length 0
15:42:49.840124 IP localhost.55666 > localhost.18888: Flags [.], ack 1, win 512, options [nop,nop,TS val 3777822786 ecr 3777822786], length 0
# 这里三次握手结束并且在客户端发送了一个字符
15:42:50.991399 IP localhost.55666 > localhost.18888: Flags [P.], seq 1:4, ack 1, win 512, options [nop,nop,TS val 3777823937 ecr 3777822786], length 3
15:42:50.991421 IP localhost.18888 > localhost.55666: Flags [.], ack 4, win 512, options [nop,nop,TS val 3777823937 ecr 3777823937], length 0
# 从这里开始下面是每隔20秒钟进行一次探测
15:43:11.005311 IP localhost.18888 > localhost.55666: Flags [.], ack 4, win 512, options [nop,nop,TS val 3777843951 ecr 3777823937], length 0
15:43:11.005343 IP localhost.55666 > localhost.18888: Flags [.], ack 1, win 512, options [nop,nop,TS val 3777843951 ecr 3777823937], length 0
15:43:31.101311 IP localhost.18888 > localhost.55666: Flags [.], ack 4, win 512, options [nop,nop,TS val 3777864047 ecr 3777843951], length 0
15:43:31.101346 IP localhost.55666 > localhost.18888: Flags [.], ack 1, win 512, options [nop,nop,TS val 3777864047 ecr 3777823937], length 0
15:43:51.325306 IP localhost.18888 > localhost.55666: Flags [.], ack 4, win 512, options [nop,nop,TS val 3777884271 ecr 3777864047], length 0
15:43:51.325328 IP localhost.55666 > localhost.18888: Flags [.], ack 1, win 512, options [nop,nop,TS val 3777884271 ecr 3777823937], length 0
# 在这里执行了一个iptables -A INPUT -p tcp --dport 55666 -j DROP命令,可以看到在没有收到对方应答的情况下会每隔5秒进行一次探测,连续9次没有收到应答,操作系统重置了这个连接
15:44:11.549314 IP localhost.18888 > localhost.55666: Flags [.], ack 4, win 512, options [nop,nop,TS val 3777904495 ecr 3777884271], length 0
15:44:16.669304 IP localhost.18888 > localhost.55666: Flags [.], ack 4, win 512, options [nop,nop,TS val 3777909615 ecr 3777884271], length 0
15:44:21.789312 IP localhost.18888 > localhost.55666: Flags [.], ack 4, win 512, options [nop,nop,TS val 3777914735 ecr 3777884271], length 0
15:44:26.909310 IP localhost.18888 > localhost.55666: Flags [.], ack 4, win 512, options [nop,nop,TS val 3777919855 ecr 3777884271], length 0
15:44:32.029305 IP localhost.18888 > localhost.55666: Flags [.], ack 4, win 512, options [nop,nop,TS val 3777924975 ecr 3777884271], length 0
15:44:37.149314 IP localhost.18888 > localhost.55666: Flags [.], ack 4, win 512, options [nop,nop,TS val 3777930095 ecr 3777884271], length 0
15:44:42.269309 IP localhost.18888 > localhost.55666: Flags [.], ack 4, win 512, options [nop,nop,TS val 3777935215 ecr 3777884271], length 0
15:44:47.389304 IP localhost.18888 > localhost.55666: Flags [.], ack 4, win 512, options [nop,nop,TS val 3777940335 ecr 3777884271], length 0
15:44:52.509319 IP localhost.18888 > localhost.55666: Flags [.], ack 4, win 512, options [nop,nop,TS val 3777945455 ecr 3777884271], length 0
15:44:57.629320 IP localhost.18888 > localhost.55666: Flags [R.], seq 1, ack 4, win 512, options [nop,nop,TS val 3777950575 ecr 3777884271], length 0

sed命令详解

tcpdump使用方法介绍

libvirt虚拟化开发简介

libvirt是一个虚拟化开发的工具库,提供了底层的kvm,xen,vmware esx等虚拟化的统一封装。可以通过libvirt提供的api对不同的底层实现进行同一的管理。除了原生的c api,还有c#, go, java, ocaml. perl, python, php, ruby的api,用户可自主选择自己熟悉的语言。

要使用libvirt首先要安装libvirtd,这是一个后台守护进程,负责管理虚拟机系统以及和客户端交互。libvirt还包含了virsh命令行工具,供用户通过shell手动管理。另外,还可以通过图形界面方式进行管理,该工具是virt-manager,如果您想要通过python的api进行开发,需要安装python3-libvirt或python-libvirt。在debian下,可通过下列指令安装这些包:

apt-get install libvirt-daemon libvirt-daemon-system libvirt-clients python3-libvirt virt-manager

启动libvirtd:

systemctl start libvirtd

接下来我们就可以通过virt-manager创建一个虚拟机。熟悉vmware或者virtualbox的同学,可以看到virt-manager和这些软件很类似,安装系统就不再介绍了。

系统安装完成之后,我们就可以通过virsh工具进行基本的操作了。运行virsh,回车,进入virsh的命令行,可通过help查看virsh支持的操作。

virsh # help
Grouped commands:

 Domain Management (help keyword 'domain'):
    attach-device                  attach device from an XML file
    attach-disk                    attach disk device
    attach-interface               attach network interface
    autostart                      autostart a domain
    blkdeviotune                   Set or query a block device I/O tuning parameters.
    blkiotune                      Get or set blkio parameters
    blockcommit                    Start a block commit operation.
    ......

通过查看这些命令,我们了解到libvirt提供了客户机的管理,客户机监控,主机监控,设备管理,网络管理,快照管理和存储管理等功能。由于有virt-manager工具,很多功能我们都可以通过图形化界面方式完成,但是我们可以通过virsh快速获取某个主机某项信息,方便开发时对比测试我们的代码是否正确。

libvirt中的一些基本概念:

  • 主机连接 主机连接是客户端和libvirtd之间的通道,该连接既可以访问运行在本地的libvirtd程序,也可以访问运行在网络上其他主机上的libvirtd。
  • 客户域 是一个运行着的虚拟机或者是可以生成一个虚拟机的配置文件。每个客户域都有一个唯一的ID,name和UUID
  • 虚拟网络 为客户机提供网络访问
  • 存储池 存储池用来组织和管理存储卷
  • 存储卷 用来为客户机提供存储设备(磁盘)
  • 主机设备 可以给虚拟机分配客户机上设备

libvirt api提供的api包括:

  • libvirt-common:通用的宏和枚举定义
  • libvirt-domain-snapshot:客户机快照管理
  • libvirt-domain:客户域管理
  • libvirt-event:事件管理
  • libvirt-host:宿主机信息查看
  • libvirt-interface:网络接口管理(网卡)
  • libvirt-network:网络管理
  • libvirt-nodedev:设备管理
  • libvirt-nwfilter:网络过滤
  • libvirt-secret:密钥管理
  • libvirt-storage:存储管理
  • libvirt-stream:数据流
  • virterror:错误处理

python api简要功能示例:

获取所有的客户机列表,详细参考

from __future__ import print_function
import sys
import libvirt

conn = libvirt.open('qemu:///system')
if conn == None:
    print('Failed to open connection to qemu:///system', file=sys.stderr)
    exit(1)

domainIDs = conn.listDomainsID()
if domainIDs == None:
    print('Failed to get a list of domain IDs', file=sys.stderr)

print("Active domain IDs:")
if len(domainIDs) == 0:
    print('  None')
else:
    for domainID in domainIDs:
        print('  '+str(domainID))

conn.close()
exit(0)

客户机内存信息统计,详细参考

from __future__ import print_function
import sys
import libvirt

domName = 'Fedora22-x86_64-1'

conn = libvirt.open('qemu:///system')
if conn == None:
    print('Failed to open connection to qemu:///system', file=sys.stderr)
    exit(1)

dom = conn.lookupByID(5)
if dom == None:
    print('Failed to find the domain '+domName, file=sys.stderr)
    exit(1)

stats  = dom.memoryStats()
print('memory used:')
for name in stats:
    print('  '+str(stats[name])+' ('+name+')')

conn.close()
exit(0)

官方有更多参考示例,这里不再过多介绍。我个人接触libvirt是因为要监控虚拟机的运行状态,性能统计,网络信息等的监控,所以对于快照,网络管理,存储管理等没有通过python接口进行过实践,对其了解的也不适特别多。下面是我认为一些有用的链接,可供参考:

  • virsh命令参考 详细讲解virsh的使用,如果对于xml的定义描述不确定时,可参考virsh的代码
  • XML格式 libvirt api使用的交互方式
  • API参考 libvirt提供的C接口,如果你需要的功能在这里找不到,那就是没有了

希望通过本片文章能让你对libvirt的开发有一个简单的认识。

Hello world的autoconf和国际化支持教程

本文将讲解如何将一个使用Makefile的Hello world程序国际化以及如何采用autotools进行配置和编译。

下面是大家所熟知的Hello world程序,将其命名为helloworld.c:

#include <stdio.h>

int main (void) {
  printf ("Hello, world!\n");
} 

1.首先我们创建源代码目录树:
    /                这是顶层目录
    /src           这是放置源代码的目录
将helloworld.c放入src/目录中。

2.如果你的程序还没有启用autoconf,你可以先创建configure.scan文件(),将其重命名为configure.ac:

autoscan
mv configure.scan configure.ac

编辑configure.ac,进行一些修改。你可以删除AC_INIT后面的所有内容。我们将采用AM_INIT_AUTOMAKE来传递变量。在AC_INIT后添加如下几行:

PACKAGE=helloworld
VERSION=0.0.1
AM_INIT_AUTOMAKE($PACKAGE, $VERSION)

并将AC_CONFIG_HEADER改变为AM_CONFIG_HEADER。如果你有一个空的AC_CONFIG_FILES 宏,注释掉它,否则下一步会出现错误。最后添加Makefile到AC_OUTPUT宏:

AC_OUTPUT(Makefile)

    注释:configure.ac以前叫configure.in

3.添加一些额外的文件:NEWS、README、AUTHORS、Changelog,这些文件不是automake工具所需要的,这是为了遵循GNU标准。在新建另外两个文件:config.h.in、Makefile.am,这两个文件是automake工具所必须的。我们将稍后创建Makefile.am。

4.添加GNU所要求但还不存在的文件(如CONPING、INSTALL等):

automake --add-missing --gnu

5.接下来,我们将添加国际化支持。同样,在顶层目录运行下面的命令:

intltoolize 

6.运行autoheader来创建config.h.in:

autoheader

7.打开configu.in进行一些修改:

IT_PROG_INTLTOOL(0.26)
AM_GNU_GETTEXT([external])      # 我们只需要这两个
AM_GLIB_GNU_GETTEXT         # 宏中的一个
ALL_LINGUAS="da nl"         # 国际化,意味着将为danish和dutch语言创建.po文件
AC_OUTPUT(
    Makefile
    src/Makefile
    intl/Makefile
    po/Makefile.in
)

IT_PROG_INTLTOOL将检查intltool工具的版本是否符合标准。

AM_GNU_GETTEXT添加本地语言支持到Makefile,同时带有一个编译选项。AM_GNU_GETTEXT将检查额外需要的函数和程序,并在configure过程中创建po/POTFILES。除了使用AM_GNU_GETTEXT,你也可以使用AM_GLIB_GNU_GETTEXT, which will do a few less things than AM_GNU_GETTEXT, but does more than enough for what intltool needs to work.你只需要使用这两个宏其中任一个就可以。

文本域通过PACKAGE来识别,我们将在helloworld.c添加一些函数,函数将使用这些预定义的变量。同样,这将是你翻译文件的基本文件名,确定你选择了一个独一的名称。

8.现在,添加所支持的语言到po/LINGUAS:

da nl

注释:过去,这个功能是通过configure.{in,ac}文件中的ALL_LINGUAS变量实现的,从gettext 0.11废弃。

9.运行
aclocal 
来检查autoconf和automake所必要的宏是否插入到aclocal.m4。

运行
autoconf
来创建configure脚本。

10.安装gettext.h头文件,并在程序中包含进去,我们定义了一个简单的宏_()来代替gettext():

#include "gettext.h"
#define _(String) gettext (String)

11.现在添加下列代码到helloworld.c中:

#include <locale.h>
#include "gettext.h"
#define _(String) gettext (String)
/* includes used by original program here */    

int main (void) 
{
    setlocale (LC_ALL, "");
    bindtextdomain (PACKAGE, LOCALEDIR);
    textdomain (PACKAGE);

    /* Original Helloworld code here */
    }

如果你使用的是gnome或是gtk+,那么我们不需要setlocale这条语句。现在我们将要被进行翻译的字符创替换为(“字符串”),那么printf(“Hello world”)将变成printf((“Hello world!\n”))。

12.创建src/Makefile.am(Makefile.in和Makefile文件将会基于Makefile.am产生):
INCLUDES = -I$(top_srcdir) -I$(includedir) -DLOCALEDIR=\””$(datadir)/locale”\”
bin_PROGRAMS = helloworld
helloworld_SOURCES = helloworld.c
noinst_HEADERS = i18n-support.h

  1. Now we create the following toplevel Makefile.am
    SUBDIRS = src po

  2. Go into the directory po/ and create POTFILES.in

    This file should contain a list of all the files in your distribution

    (starting from the top, one level above the po dir) that contain

    strings to be internationalized.

    For the helloworld sample, it would contain

    src/helloworld.c

    Run 

      intltool-update –pot

    Run

      intltool-update –maintain 

    to see if you are missing files that contain marked strings.  

    You should consider adding these to POTFILES.in

  1. Now we start making a Danish and Dutch translation

    msginit –locale=da

    msginit –locale=nl

    intltool-update da

    intltool-update nl

    edit and update da.po and nl.po

    (The respective translations are “Hej verden” and “Hallo wereld”)

    

  1. Now we can compile.  We will test it later, so we will install it in

    a temporary location.

    Close your eyes and type 

      ./configure –prefix=/tmp/helloworld && make 

    in the toplevel directory. 🙂

  1. To test if it works, you have to install the package.

    Run

      make install

    in the toplevel directory.

  1. Now set the environment variable LC_ALL to your preferred language :

      export LC_ALL=nl_NL

      /tmp/helloworld/bin/helloworld

      export LC_ALL=da_DK

      /tmp/helloworld/bin/helloworld

    And if all goes well, the string should be translated in the two languages.

  1. To finish it all up, run

      make dist

    to create a distributable tarball containing your internationalized

    program.

  1. Exercises :

    – add another language