Linux快速开发之makefile编译、以服务形式运行、抓包、调试、分析dump

Linux快速开发之makefile编译、以服务形式运行、抓包、调试、分析dump

1       Makefile编写... 1

1.1        实例解析... 1

1.1.1         makefile文件... 1

1.1.2         包含的makefile.global 3

1.2        使用makefile文件... 5

2       Linux编译... 5

2.1        Linux编译步骤... 5

3       Linux调试gdb. 6

4       以服务的形式在linux上运行程序... 7

4.1        执行步骤... 7

4.2        安装服务脚本ivmsservice.sh. 8

5       linux抓包tcpdump与wireshark. 18

5.1        安装卸载tcpdump. 18

5.2        tcpdump抓包到文件... 18

5.3        分析抓包文件... 19

6       分析dump文件... 19


 

1          Makefile编写

Makefile 是一种linux编译程序的命令集合;通过makefile对程序进行编译和链接;

1.1          实例解析

1.1.1          makefile文件

下面是一个完整的makefile的实例;

#定义C语言文件编译命令变量,gcc是编译命令 –o0表示禁用优化,后面是参数

CC = gcc -o0

#定义C++文件编译命令变量,g++是编译命令 –o0表示禁用优化,后面是参数

CPP = g++ -o0

#定义make的变量

MAKE = make

#定义去除字符串首尾空格的变量

STRIP = strip

#定义根目录的变量,..表示makefile文件所在目录的上一级

TOP_DIR = ..

#包含文件,像是c++里的头文件,里面有一些定义

include Makefile_cmsws_ass_stub_linux.global

#定义静态库的变量,$(LD_STATIC_LIB)在global中的变量

LD_STATIC_LIB := $(LD_STATIC_LIB)

#定义动态库的变量,$( LDLIBS)在global中的变量

LDLIBS := $(LDLIBS)   -lgsoap -lhlog -liconv -lssl -lcrypto

#定义生成目标文件,名称和路径

TARGET = $(LIB_DIR)/libcmsws_ass_stub.so

#定义变量生成中间对象文件,

OBJ_DIR := $(TOP_DIR)/obj

#定义创建对象文件夹的命令,shell语句用来判断是否存在,并创建对象文件夹

MAKE_OBJ_DIR := $(shell if [ ! -d $(OBJ_DIR) ]; then mkdir $(OBJ_DIR) ; fi )

#定义创建对象文件夹的子文件的命令,

MAKE_OBJ_SUBDIR := $(shell for sub_dir in $(VPATH); do if [ ! -d $(OBJ_DIR)/$$sub_dir ]; then mkdir $(OBJ_DIR)/$$sub_dir; fi ; done; )

 

#定义源文件变量,$(VPATH)是程序目录,*意思是获取程序目录下的所有文件名,最后得到的就是所有文件名组成的字符串,以空格隔开;

SOURCES := $(foreach dir, $(VPATH), $(wildcard $(dir)/*))

#filet是过滤出所有的c文件名称,筛选出.c文件,也就是C语言文件

C_SRCS  := $(filter %.c, $(SOURCES))

#filet是过滤出所有的c++文件,筛选出C++文件

CPP_SRCS := $(filter %.cpp, $(SOURCES))

#定义源文件变量,所有的C文件和C++文件

 SRCS := $(C_SRCS) $(CPP_SRCS)

#定义对象文件变量,包含所有的对象文件,patsubst是字符串替换函数,将文件名字的后缀都改成.o,表示生成的对象文件的名称patsubst是命令,%.c表示要替换的文件名通配符,%.o表示要替换后的文件名通配符;

OBJS := $(patsubst %, $(OBJ_DIR)/%, $(patsubst %.c, %.o, $(patsubst %.cpp, %.o, $(SRCS))))

#定义编译规则,目标:源文件  编译命令 ;生成c语言的对象文件,$(CC)是gcc编译命令;格式为  目标($(OBJ_DIR)/%.o):生成目标的源文件(%.c)  编译命令($(CC)) 编译参数(-g3 表示加入调试信息,$(CFLAGS)包含一些参数和头文件);如果头文件或者源文件比.o文件要新,则会重新编译;

$(OBJ_DIR)/%.o:%.c

         $(CC)  -g3 -c $(CFLAGS) $< -o $@

#定义编译规则,目标:源文件  编译命令 ;生成c++的对象文件

$(OBJ_DIR)/%.o:%.cpp

         $(CPP)   -g3 -c $(CPPFLAGS) $< -o $@

#生成目标文件,包含所有的对象文件,动静态库

all: $(OBJS)       

         $(CPP)  -g -shared $(OBJS) -fPIC $(LDFLAGS) $(LDLIBS)  $(LD_STATIC_LIB) -o $(TARGET) 

#生成伪目标文件,用来清除文件不生成真的文件,执行 make –f  Makefilename clean 就会清除生成的目标文件和中间对象文件;

 clean:

         -rm -f $(TARGET) $(OBJS)

1.1.2          包含的makefile.global

CC = gcc

CPP = g++

MAKE = make

STRIP = strip

 

##define inc_dir

#定义工程根目录变量

PROJ_BASE = $(TOP_DIR)

#定义头文件目录变量

INC_DIR   = $(TOP_DIR)/cmsws_alarm_server_service_stub\

         $(TOP_DIR)/cmsws_lib/cmsws  \

         $(TOP_DIR)/cmsws_lib/hlog  \

         $(TOP_DIR)/cmsws_lib/iconv   \

         $(TOP_DIR)/cmsws_platform/windows  \

         $(TOP_DIR)/cmsws_lib/cmsws_ass_stub\

         $(TOP_DIR)/cmsws_alarm_server_service_stub/service_stub \

         $(TOP_DIR)/cmsws_alarm_server_service_stub/ass_stub_interface \

#源文件路径

VPATH+=$(TOP_DIR)/cmsws_platform/windows  $(TOP_DIR)/cmsws_alarm_server_service_stub/service_stub  $(TOP_DIR)/cmsws_alarm_server_service_stub/ass_stub_interface  $(TOP_DIR)/cmsws_alarm_server_service_stub

#set so compile options

#定义so文件的编译的参数变量

SOFLAGS = -shared -fPIC

#-fstack-check -fvisibility=hidden

#定义C文件的编译的参数变量

CFLAGS := $(CFLAGS) -m64 $(SOFLAGS) $(patsubst %, -I%, $(INC_DIR))

#定义C++文件的编译的参数变量

CPPFLAGS := $(CFLAGS) -fPIC -ftemplate-depth-128

#定义动态库的目录变量

LIB_DIR = $(TOP_DIR)/linux_lib_x64

#定义静态库的目录变量

STATIC_LIB_DIR = $(TOP_DIR)/linux_lib_x64

#定义动静态库的编译的参数变量

LDFLAGS := $(LDFLAGS) $(SOFLAGS) -Wl,--hash-style=both -Wl,-Bsymbolic -Wl,--enable-new-dtags -Wl,-rpath,./

LDLIBS := $(LDLIBS) -lpthread -lstdc++ -lrt $(patsubst %, -L%, $(LIB_DIR))

LD_STATIC_LIB := $(LD_STATIC_LIB)  $(patsubst %, -L%, $(STATIC_LIB_DIR))

1.2          使用makefile文件

使用上述makefile文件作为模板,修改源文件目录、头文件目录,生成的目标名称,包含的动静态库,动静态库路径;就可以使用这个模板轻松编译linux程序;

 

 

2          Linux编译

2.1          Linux编译步骤

(1)采用secureCRT软件,登陆linux服务器,进入makefile所在文件夹,执行makefile文件。如下图所示;编译中出现错误则根据提示修改;

                       

(2)编译成功后,程序不一定能够执行;需要执行ldd –r libalarm_logic_unit.so,查看目标文件libalarm_logic_unit.so是否缺少库文件或者未定义的函数;相当于window下的depend工具;

 

根据提示添加缺少的库文件,修改错误,未识别的符号有的是一些乱码,可用下列命令来查看具体的原因;c++filt _ZNSbIhSt11char_traitsIhESaIhEE4_Rep20_S_empty_rep_storageE

 

(3)软连接,一些开发者提供的库文件后缀一般是.so.1.0.0的格式,这样makefile无法识别,需要通过ln –s libssl.so.1.0.0 libssl.so  创建软连接libssl.so 指向libssl.so.1.0.0;软连接相当于C++中引用的概念,是个别称;

 

 

 

3          Linux调试gdb

(1)       gdb alarm_logic_unit 。    gdb加程序名称,表示调试该程序;

(2)       b 。加函数名称或者 源文件名称:行号,来加断点;

(3)       r 。运行程序进行调试;执行到断点处会停住;

(4)       p 。变量名称      来查看变量的值;

(5)       c 。继续执行到下一个断点;

(6)       n 。单步执行;

(7)       l 。查看下面要执行的几行代码;

(8)       q退出gdb;

 

 

 

4           以服务的形式在linux上运行程序

4.1         执行步骤

(1)复制文件工具

WinSCP是一款可视化的文件复制工具,可以方便的将windows系统中的文件复制到linux系统中;如下图所示,现在windows系统中登陆linux系统,然后通过拖动的方式将文件来回复制;

 

(2)编写脚本程序

编写六个脚本文件*.sh文件,文件的内容如下;./ivmsservice.sh是安装卸载服务的文件,待会介绍;

安装服务脚本install.sh:./ivmsservice.sh install alarm_logic_unit

卸载服务脚本uninstall.sh:./ivmsservice.sh uninstall alarm_logic_unit

启动服务脚本start.sh:service alarm_logic_unit start

停止服务脚本stop.sh:service alarm_logic_unit stop

服务状态脚本status.sh:service alarm_logic_unit status

重启服务脚本restart.sh:service alarm_logic_unit restart

(3)转换文件格式,在windows环境下编写的文件在linux下执行会因为换行符的不同而无法执行,window下是/n/r,linux下是/n,所以需要进行格式转换;先用命令:sudo yum install dos2unix 下载安装dos2unix程序;然后在用dos2unix *.sh命令转换所有的sh文件格式;

(4)先执行安装脚本,在执行启动脚本,再用命令ps –ef 显示电脑的进程,看是否都安装启动成功;执行statu.sh脚本查看状态,执行stop.sh 停止服务,执行uninstall.sh卸载服务;如果无法停止服务,可以用kill 进程id(12897)命令,通过进程id号杀死进程;

 

4.2         安装服务脚本ivmsservice.sh

将程序安装位服务,实际上是在/etc/init.d路径下加入一个执行脚本,执行install.sh脚本后,会在/etc/init.d中加入一个脚本文件,如下图所示;文件的名称和服务程序的名称相同;内容实际就是将ivmsservice.sh文件的内容替换掉服务名称、服务文件路径等;

 

下面将详细介绍服务安装脚本的内容:

#第一行表示shell脚本采用的解释器,是在bin路径下的bash解释器

#!/bin/bash

# chkconfig: 2345 58 74

# description: IVMS_SERVICE_NAME is a iVMS8200 Service

# common function,表示引用/etc/init.d/functions这个文件中的函数

. /etc/init.d/functions

# service bash flag  标识符,用于标记是否安装服务;0表示未安装;

IVMS_SERVICE=0

#设置dump文件路径

CORE_DUMP_DIR=/var/ivms8200_core

DAEMON_COREFILE_LIMIT=unlimited

#设置服务安装文件路径

SVC_SYS_DIR=/etc/init.d

#设置服务执行文件路径

SVC_PROG=IVMS_SERVICE_PROG

#设置服务的名字

SVC_NAME=IVMS_SERVICE_NAME

#设置服务文件的路径

SVC_DIR=IVMS_SERVICE_DIR

#设置锁文件的路径

SVC_LOCK_FILE=/var/lock/subsys/$SVC_PROG

SVC_PID_FILE=/var/run/$SVC_NAME.pid

# 安装服务函数install service, $1 参数1,service name, $2参数2,executable name

InstallSvc()

{       #判断参数1是否存在

         if [ -z "$1" ]; then

                   echo $"service name is needed by ivmsservice."

                   return -1

         fi

 

         #将参数1设置为变量SVC_NAME的值

SVC_NAME=$1

#将参数2 和服务名称的组合${2:-$SVC_NAME}作为执行变量的值

         SVC_PROG=${2:-$SVC_NAME}

#将当前路径设置为服务的路径

         SVC_DIR=`pwd`

         #将系统服务目录和文件名字组成的路径作为系统服务文件

         SVC_SYS_FILE=$SVC_SYS_DIR/$SVC_NAME

         #如果$SVC_DIR/$SVC_PROG不为常规文件,则报错返回

         if [ ! -f $SVC_DIR/$SVC_PROG ]; then

                   echo $"$SVC_DIR/$SVC_PROG does not exist."

                   return -1

         fi

         #提示正在安装

         echo $"installing $SVC_NAME service, executable file $SVC_DIR/$SVC_PROG ..."

         #将配置文件中suid_dumpable的内容设置为1,表示允许产生dump文件

         # set suid_dumpable on

         if [ -e /proc/sys/kernel/suid_dumpable ]; then

                   echo 1 > /proc/sys/kernel/suid_dumpable

         else

                   echo 1 > /proc/sys/fs/suid_dumpable

         fi

         #判断$CORE_DUMP_DIR文件夹是否存在,不存在则创建,并设置dump文件名格式,%e-%p-%t分别表示文件名称加入执行文件名称,添加进程的pid,添加时间;最后生成的文件名字形如:core-alarm_logic_unit-12149-20170820

         # create core directory

         if [ -d $CORE_DUMP_DIR ]; then

                   echo $"$CORE_DUMP_DIR/core-%e-%p-%t" > /proc/sys/kernel/core_pattern

         else

                   mkdir $CORE_DUMP_DIR

                   echo $"$CORE_DUMP_DIR/core-%e-%p-%t" > /proc/sys/kernel/core_pattern

         fi

 

         # config sysctl to enable core设置系统允许产生dump文件$?表示上一条命令的返回值>>表示以追加的方式在/etc/sysctl.conf中加入两个变量,表示允许删除dump的bool值和dump文件的文件名格式;

         grep "$CORE_DUMP_DIR/core-%e-%p-%t" /etc/sysctl.conf

         if [ $? -ne 0 ]; then

                   echo $"fs.suid_dumpable = 1" >> /etc/sysctl.conf

                   echo $"kernel.core_pattern = $CORE_DUMP_DIR/core-%e-%p-%t" >> /etc/sysctl.conf

         fi

#下面这段代码是将本脚本本身替换掉一些东西后,在输入到$SVC_SYS_FILE文件中,即在SVC_SYS_DIR=/etc/init.d目录中创建一个与服务名字相同的名称的脚本文件,内容为该脚本替换后的脚本文件;

         # create service bash

#$0表示参数0,即ivmsservice.sh本身,第一句是读取ivmsservice.sh中的数据到缓冲区,sed是文本编辑命令,将文件中的数据读取到缓冲区进行编辑;%作为分界符;-e表示多行编辑;冒号中s表示字符替换,g表示全部替换;>表示将缓冲区中的内容保存到文件中;第一行意思是读取文件中的数据,将IVMS_SERVICE=0全部替换成IVMS_SERVICE=1,这里只是字符替换;第二行是将变量$SVC_PROG全部替换掉IVMS_SERVICE_PROG,第四行是先替换,然后将缓冲区中的数据输入到变量$SVC_SYS_FILE标记的文件中;用VMS_SERVICE=1替换掉IVMS_SERVICE=0 ;

         sed -e "s%IVMS_SERVICE=0%IVMS_SERVICE=1%g" $0 | \

         sed -e "s%IVMS_SERVICE_PROG%$SVC_PROG%g" | \

         sed -e "s%IVMS_SERVICE_NAME%$SVC_NAME%g" | \

         sed -e "s%IVMS_SERVICE_DIR%$SVC_DIR%g" > $SVC_SYS_FILE

         #chmod给文件赋予权限

         chmod u+x $SVC_SYS_FILE

         chmod u+x $SVC_DIR/$SVC_PROG

#chkconfig 的作用是注册服务

         chkconfig --add $SVC_NAME

         echo $"install $SVC_NAME service successfully."

         return 0

}

# uninstall service, $1 service name

UninstallSvc()

{

#判断参数1是否存在

         if [ -z "$1" ]; then

                   echo $"service name is needed by ivmsservice."

                   return -1

         fi

         SVC_NAME=$1

         SVC_SYS_FILE=$SVC_SYS_DIR/$SVC_NAME

         echo $"uninstalling $SVC_NAME service ..."

         # rm service bash注销服务,删除服务脚本,;

         if [ -f $SVC_SYS_FILE ]; then

                   chkconfig --del $SVC_NAME

                   rm -f $SVC_SYS_FILE

         else

                   warning $"$SVC_NAME service does not exist."

         fi

         echo $"uninstall $SVC_NAME service successfully."

         return 0

}

# start service启动服务

StartSvc()

{

         echo $"starting $SVC_NAME service ..."

         # set core unlimited, replaced by DAEMON_COREFILE_LIMIT

         #ulimit -c unlimited

         # create lockfile, run program

         touch $SVC_LOCK_FILE

         cd $SVC_DIR

         daemon --pidfile=$SVC_PID_FILE $SVC_DIR/$SVC_PROG -service

         if [ $? -eq 0 ]; then

                   # created by the program

                   #pidof $SVC_DIR/$SVC_PROG > $SVC_PID_FILE

                   echo $"start $SVC_NAME service successfully."

                   return 0

         else

                   echo $"start $SVC_NAME service failure."

                   return -1

         fi

}

# get service status

IsSvcRunning()

{

         local pid

         __pids_var_run $SVC_NAME $SVC_PID_FILE

         [ -n "$pid" ] && return 0 || return 1

}

# stop service

StopSvc()

{

         echo $"stopping $SVC_NAME service ..."

         # remove lockfile删除lockfile文件,然后停止服务

         rm -f $SVC_LOCK_FILE

         # wait for exit

         local i RC

         for (( i = 0; i < 10; i++ )); do

                   if IsSvcRunning; then

                            sleep 1

                   else

                            break

                   fi

         done

#如果没有停止,则强制杀死进程

         if [ $i -eq 10 ] && IsSvcRunning; then

                   killproc -p $SVC_PID_FILE $SVC_NAME

                   RC=$?

         else

                   rm -f $SVC_PID_FILE

                   RC=0

         fi

         if [ $RC -eq 0 ]; then

                   echo $"stop $SVC_NAME service successfully."

                   return 0

         else

                   echo $"stop $SVC_NAME service failure."

                   return -1

         fi

}

# restart service

RestartSvc()

{

         echo $"restarting $SVC_NAME service ..."

         StopSvc

         if [ $? -eq 0 ]; then

                   StartSvc

                   if [ $? -eq 0 ]; then

                            echo $"restart $SVC_NAME service successfully."

                            return 0

                   fi

         fi

         echo $"restart $SVC_NAME service failure."

         return -1

}

RETVAL=0

if [ $IVMS_SERVICE -eq 0 ]; then

         # setup

         case $1 in

                   install | i)

                            InstallSvc $2 $3

                            RETVAL=$?

                            ;;

                   uninstall | u)

                            UninstallSvc $2

                            RETVAL=$?

                            ;;

                   *)

                            echo $"Usage: $0 {i, install NAME [EXEC] | u, uninstall NAME}"

                            ;;

         esac

else

         # service

         case $1 in

                   start | r)

                            StartSvc

                            RETVAL=$?

                            ;;

                   stop | p)

                            StopSvc

                            RETVAL=$?

                            ;;

                  restart | e)

                            RestartSvc

                            RETVAL=$?

                            ;;

                   status | s)

                            status -p $SVC_PID_FILE $SVC_NAME

                            RETVAL=$?

                            ;;

                   status2)

                            IsSvcRunning && echo $"$SVC_NAME service is running." || echo $"$SVC_NAME service has been stopped."

                            ;;

                   *)

                            echo $$"Usage: $0 {start|stop|status|restart}"

                            ;;

         esac

fi

exit $RETVAL

 

 

5          linux抓包tcpdump与wireshark

5.1         安装卸载tcpdump

安装tcpdump 命令:sudo yum install tcpdump

移除tcpdump命令:sudo yum remove tcpdump

5.2         tcpdump抓包到文件

Wireshark(以前是ethereal)是Windows下非常简单易用的抓包工具。但在Linux下很难找到一个好用的图形化抓包工具。还好有Tcpdump。我们可以用Tcpdump + Wireshark 的完美组合实现:在 Linux 里抓包,然后在Windows 里分析包。

tcpdump tcp -i eth1 -t -s 0 -c 100 and dst port ! 22 and src net 192.168.1.0/24 -w ./target.cap

(1)tcp: ip icmp arp rarp 和 tcp、udp、icmp这些选项等都要放到第一个参数的位置,用来过滤数据报的类型;

(2)-i eth1 : 只抓经过接口eth1的包;

(3)-t : 不显示时间戳;

(4)-s 0 : 抓取数据包时默认抓取长度为68字节。加上-S 0 后可以抓到完整的数据包;

(5)-c 100 : 只抓取100个数据包;

(6)dst port ! 22 : 不抓取目标端口是22的数据包;

(7)src net 192.168.1.0/24 : 数据包的源网络地址为192.168.1.0/24;

(8)-w ./target.cap : 保存成cap文件,方便用ethereal(即wireshark)分析;

例如抓取tcp端口61616的数据,保存到mq.cap文件,命令如下:

tcpdump tcp port 61616 -w ./mq.cap

5.3         分析抓包文件

直接将mq.cap文件复制到window系统中,然后用wireshark工具打开文件即可;如linux系统有图形化界面,可以直接在linux系统中,安装linux版本的wireshark软件;直接抓包分析,无需用tcpdump;

6          分析dump文件

发生core dump之后, 用gdb进行查看core文件的内容, 以定位文件中引发core dump的行.

gdb [exec file] [core file]

如: gdb ./test test.core

使用gdb 调试方法,首先要在gcc编译时加入-g选项。

调试core文件,在Linux命令行下:gdb pname corefile。

这样进入了gdb core调试模式。

追踪产生segmenttation fault的位置及代码函数调用情况:

gdb>bt

这样,一般就可以看到出错的代码是哪一句了,还可以打印出相应变量的数值,进行进一步分析。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章