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

這樣,一般就可以看到出錯的代碼是哪一句了,還可以打印出相應變量的數值,進行進一步分析。

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