linux makefile编写 dump分析 GDB调试 抓包
Linux快速开发
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
-
gdb alarm_logic_unit 。 gdb加程序名称,表示调试该程序;
-
b 。加函数名称或者 源文件名称:行号,来加断点;
-
r 。运行程序进行调试;执行到断点处会停住;
-
p 。变量名称 来查看变量的值;
-
c 。继续执行到下一个断点;
-
n 。单步执行;
-
l 。查看下面要执行的几行代码;
-
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
这样,一般就可以看到出错的代码是哪一句了,还可以打印出相应变量的数值,进行进一步分析。
if grep -Fxq "export NLS_LANG=S"
/etc/profile
then
echo "A=1 found"
else
sed -i '/\[mysqld\]/a A=1' my.cnf
fi
自己开发了一个股票智能分析软件,功能很强大,需要的点击下面的链接获取:
https://www.cnblogs.com/bclshuai/p/11380657.html