linux-编译gcc调试gdb与安装make与core

时间:2022-07-10 16:41:23

1,工具gcc:编译C文件

1.1. 无选项编译链接

用法:#gcc test.c
作用:将test.c预处理、汇编、编译并链接形成可执行文件。这里未指定输出文件,默认输出为a.out。

1.2. 选项 -o

用法:#gcc test.c -o test
作用:将test.c预处理、汇编、编译并链接形成可执行文件test。-o选项用来指定输出文件的文件名。

1.3. 选项 -E

用法:#gcc -E test.c -o test.i
作用:将test.c预处理输出test.i文件。

1.4. 选项 -S

用法:#gcc -S test.i
作用:将预处理输出文件test.i汇编成test.s文件。

1.5. 选项 -c

用法:#gcc -c test.s
作用:将汇编输出文件test.s编译输出test.o文件。

1.6. 无选项链接

用法:#gcc test.o -o test
作用:将编译输出文件test.o链接成最终可执行文件test。

1.7. 选项-O

用法:#gcc -O1 test.c -o test
作用:使用编译优化级别1编译程序。级别为1~3,级别越大优化效果越好,但编译时间越长。

1.8. 选项-Dmacro

用法:#gcc -DTEST_CONFIGURATION test.c -o test
作用:打开宏TEST_CONFIGURATION

1.9. 选项-Umacro

用法:#gcc -UTEST_CONFIGURATION test.c -o test
作用:关闭宏TEST_CONFIGURATION

1.10. 选项-Wall

用法:#gcc -Wall test.c -o test
作用:打开警告信息

1.11 单文件编译

$gcc -c hello.c         #只编译不链接,生成hello.o
$gcc -o hello hello.o   #输出可执行文件hello

1.12 多文件编译

$gcc -c thanks.c thanks2.c        #thanks.c调用了thanks2.c定义的函数
$gcc -o thanks thanks.o thanks2.o #最终文件thanks
$./thanks

1.13 指定头文件和lib库参数

$gcc sin.c -lm -L/lib -L/usr/lib -I/usr/include # 还是会有警告,估计是因为libm.so是标准函数库
# l表示library,加入某函数库;m表示libm.so函数库,lib与扩展名(.a或.so)不需要写
# L/path表示library的搜索路径,Linux默认library搜索路径为/lib和/usr/lib
# I/path表示include的搜索路径,Linux默认include搜索路径为/usr/include

举例说明:

例:
gcc -o hello hello.c -I/home/hello/include -L/home/hello/lib -lworld

上面这句表示在编译hello.c时:
-I /home/hello/include表示将/home/hello/include目录作为第一个寻找头文件的目录,寻找的顺序是:/home/hello/include-->/usr/include-->/usr/local/include
-L /home/hello/lib表示将/home/hello/lib目录作为第一个寻找库文件的目录,寻找的顺序是:/home/hello/lib-->/lib-->/usr/lib-->/usr/local/lib
 -lworld表示在上面的lib的路径中寻找libworld.so动态库文件(如果gcc编译选项中加入了“-static”表示寻找libworld.a静态库文件)

或者如我编译的:

gcc -I/usr/include/mysql connect.c -L/usr/lib/mysql -lmysqlclient  -lz -o connect
-lz表示链接压缩库

2,makefile基本写法

2.1 目标单文件示例

simple_write : simple_write.o
        gcc -o simple_write simple_write.o 
simple_write.o : simple_write.c
        gcc -c simple_write.c
clean : 
        rm -rf *.o
do :
        ./simple_write

2.2 通用模板

文件Makefile

include ./xxx.mk

#变量定义
TBUS_TEST_CFILE=$(wildcard *.c)
TBUS_TEST_OBJ=$(TBUS_TEST_CFILE:.c=.o)
TEST_TOOL=test


.PHONY:all clean

all: $(TEST_TOOL)
    
$(TEST_TOOL): $(TBUS_TEST_OBJ)
    $(CC)    $^ $(LDPATH) ${LIBS} -o $@

%.o: %.c
    $(CC) -c $< $(CFLAGS) ${CINC} -o $@

clean:
    $(RM) core* *.o  
    $(RM) $(TEST_TOOL)
文件xxx.mk

#--------------work directories----------------------
XXX_HOME=/usr/local/XXX-2.5.2.28879_X86_64_Release
XXX_INC  = $(XXX_HOME)/include

XXX_SERVICES = $(XXX_HOME)/services
XXX_SERVICESRC = $(XXX_HOME)/services_src

XXX_UNITTESTS = $(XXX_HOME)/unittests
XXX_UNITTESTSRC = $(XXX_HOME)/unittests_src

XXX_DEPS = $(XXX_HOME)/deps

XXX_LIB  = $(XXX_HOME)/lib
XXX_LIBSRC  = $(XXX_HOME)/lib_src

XXX_TOOLS = $(XXX_HOME)/tools
XXX_TOOLSRC  = $(XXX_HOME)/tools_src

XXX_PACKLIBDIR  = $(XXX_LIBSRC)/tmp

XXX_UNITTEST = $(XXX_HOME)/unittests

XXX_SERVICES = $(XXX_HOME)/services
XXX_APPS_INC=$(XXX_INC)/apps


#----------------libraris --------------------------
#XXX的库目录
LDPATH = -L$(XXX_LIB)/
#LDPATH += -L/usr/lib/ -L/usr/local/lib/ -L./
#XXX的库文件
LIBS = -lXXX

#XXX的include,对外使用CFLAGS
CINC  =  -I$(XXX_INC)/ 
CFLAGS= -Wall -g
CXXFLAGS=-Wall

CC = gcc
CXX = g++
RM = /bin/rm -f

2.3 目标多文件示例

.PHONY : main
main : server client
server : server.o
        gcc -g -o server server.o 
client : client.o
        gcc -g -o client client.o 
server.o : server.c
        gcc -g -c server.c
client.o : client.c
        gcc -g -c client.c
clean : 
        rm -rf *.o
ser :
        ./server
cli :
        ./client

3,工具make:执行makefile

1,make是一个程序,它会寻找一个叫Makefile的文件,根据这个文件执行相关的编译操作(需要相关的编译工具如gcc)。

2,那么Makefile是怎么来的?通常软件开发商会写一个叫configure的检测程序,去检测用户的操作环境,根据操作环境生成相应的编译规则,并把它们保存在Makefile文件中。特别的,configure检测程序会检测是否有相应的编译工具(如gcc)、是否有依赖的函数库或软件、是否适合当前的操作系统等等,如果发现不妥,并不会产生相应的Makefile文件。

3.1 C文件makefile示例

3.1.1 最基本makefile

3个头文件,8个c文件:

注意1:文件名:Makefile或makefile,如果要指定特定的Makefile,你可以使用make的“-f”和“--file”参数,如:make -f Make.Linux或make --file Make.AIX。

注意2:命令一定要以Tab键开始

edit : main.o kbd.o command.o display.o insert.o search.o files.o utils.o
	cc -o edit main.o kbd.o command.o display.o insert.o search.o files.o utils.o
main.o : main.c defs.h
	cc -c main.c
kbd.o : kbd.c defs.h command.h
	cc -c kbd.c
command.o : command.c defs.h command.h
	cc -c command.c
display.o : display.c defs.h buffer.h
	cc -c display.c
insert.o : insert.c defs.h buffer.h
	cc -c insert.c
search.o : search.c defs.h buffer.h
	cc -c search.c
files.o : files.c defs.h buffer.h command.h
	cc -c files.c
utils.o : utils.c defs.h
	cc -c utils.c
clean :
	rm edit main.o kbd.o command.o display.o insert.o search.o files.o utils.o

3.1.2 改进版1:使用变量

objects = main.o kbd.o command.o display.o insert.o search.o files.o utils.o
edit : $(objects)
	cc -o edit $(objects)
main.o : main.c defs.h
	cc -c main.c
kbd.o : kbd.c defs.h command.h
	cc -c kbd.c
command.o : command.c defs.h command.h
	cc -c command.c
display.o : display.c defs.h buffer.h
	cc -c display.c
insert.o : insert.c defs.h buffer.h
	cc -c insert.c
search.o : search.c defs.h buffer.h
	cc -c search.c
files.o : files.c defs.h buffer.h command.h
	cc -c files.c
utils.o : utils.c defs.h
	cc -c utils.c
clean :
	rm edit $(objects)

3.1.3 改进版2:Makefile自动推导

自动推导:只要make看到一个[.o]文件,它就会自动的把[.c]文件加在依赖关系中:如果make找到一个whatever.o,那么whatever.c,就会是whatever.o的依赖文件。并且 cc -c whatever.c 也会被推导出来。

注意1:“.PHONY”表示,clean是个伪目标文件。

注意2:rm命令前面加了一个小减号的意思就是,也许某些文件出现问题,但不要管,继续做后面的事。

objects = main.o kbd.o command.o display.o insert.o search.o files.o utils.o

edit : $(objects)
	cc -o edit $(objects)
main.o : defs.h
kbd.o : defs.h command.h
command.o : defs.h command.h
display.o : defs.h buffer.h
insert.o : defs.h buffer.h
search.o : defs.h buffer.h
files.o : defs.h buffer.h command.h
utils.o : defs.h

.PHONY : clean
clean :
	-rm edit $(objects)

3.1.4 改进版3:另类风格的Makefile(建议不用)

objects = main.o kbd.o command.o display.o insert.o search.o files.o utils.o

edit : $(objects)
	cc -o edit $(objects)

$(objects) : defs.h
kbd.o command.o files.o : command.h
display.o insert.o search.o files.o : buffer.h

.PHONY : clean
clean :
	rm edit $(objects)
这种风格不利于理清依赖关系,如果有新文件加入,会显得非常复杂。不易维护。

3.2 C文件Makefile规则

3.2.1 显示规则

显式规则说明了,如何生成一个或多的的目标文件。这是由Makefile的书写者明显指出,要生成的文件,文件的依赖文件,生成的命令。

3.2.2 隐晦规则

由于我们的make有自动推导的功能,所以隐晦的规则可以让我们比较粗糙地简略地书写Makefile,这是由make所支持的。

3.2.3 变量定义

在Makefile中我们要定义一系列的变量,变量一般都是字符串,这个有点你C语言中的宏,当Makefile被执行时,其中的变量都会被扩展到相应的引用位置上。

1,自动化变量
其实有很多个,比如$@ $< $? $^等
常用的还是$@ $< $^这三个,所以这里整理下这三个的用法:
  1 $@
         比如对于依赖关系   test.o : a.c b.c c.c   
         $@表示的是当前依赖关系的目标集合,这里就是test.o
   2 $<
         如上例中,就是后面依赖集合中的第一个a.c
 而对于  %.o : %.c这样的%匹配,实际上是会展开成一条一条,比如test.o : test.c,所以其$<表示的是test.c
   3 $^
  如上例中,就是后面依赖集合中的所有的a.c b.c c.c

2,支持的通配符

make支持三各通配符:“*”,“?”和“[...]”

用在命令中:

clean:
	rm -f *.o
用在依赖中:

print: *.c
	lpr -p $?
	touch print
用在变量定义中:
#不对,*不会展开
objects = *.o
#正确用法,会展开*
objects := $(wildcard *.o)

3.2.4 文件指示

1,在一个Makefile中引用另一个Makefile

#filename可以是当前操作系统Shell的文件模式(可以保含路径和通配符)
include <filename>   
#-include表示无论include过程中出现什么错误,都不要报错继续执行。
-include <filename>  

示例:include foo.make *.mk $(bar)

filename可以是当前操作系统Shell的文件模式(可以保含路径和通配符)

另一个是指根据某些情况指定Makefile中的有效部分,就像C语言中的预编译#if一样;

还有就是定义一个多行的命令。有关这一部分的内容,我会在后续的部分中讲述。

3.2.5 注释

注释用"#"

3.2 C plus plus文件makefile规则

A.h:

#include <iostream>
using namespace std;

#ifndef A_H
#define A_H
class A
{
	int a;
public:
	A();
	A(int a);
	void printA();
	};
#endif

A.cpp:
/*
 * A.cpp
 *
 *  Created on: 2012-2-28
 *      Author: administrator
 */

#include "A.h"

A::A()
{
	a=9;
	}
A::A(int a)
{
	this->a=a;
	}
void A::printA()
{
	cout<<a<<endl;
	}

main.cpp:

//============================================================================
// Name        : Test1.cpp
// Author      : gexing
// Version     :
// Copyright   : Your copyright notice
// Description : Hello World in C++, Ansi-style
//============================================================================

#include <iostream>
using namespace std;
#include "A.h"
int main()
{
	A a1;
	A a2(5);
	a1.printA();
	a2.printA();
	return 0;
}

makefile:

main   :   main.o   A.o
	g++   -o  main A.o   main.o   
main.o   :   main.cpp A.h
	g++   -c   main.cpp
A.o   :   A.cpp	A.h
	g++   -c   A.cpp 
clean: 
	rm -rf *.o
do:
	./test

执行命令:

#make
#make clean
#./main或者make do


4,工具gdb:源代码级调试工具

4.1 编译

为了使 gdb 正常工作, 你必须使你的程序在编译时包含调试信息. 调试信息包含你程序里的每个变量的类型和在可执行文件里的地址映射以及源代码的行号.  gdb 利用这些信息使源代码和机器码相关联.
在编译时用 -g 选项打开调试选项. 

gcc -g eg.c -o eg

4.2 gdb外部参数

参数 说明
-h help,帮助
-c File 把File作为core dump来执行。
-n 不从任何.gdbinit初始化文件中执行命令。
通常情况下,这些文件中的命令是在所有命令选项和参数处理完后才执行。
-batch 运行batch模式。
在处理完所有用'-x'选项指定的命令文件(还有'.gdbi-nit',如果没禁用)后退出,并返回状态码0
-x File 从File中执行GDB命令。
-p pid 绑定进程号
gdb -p 1758 -x gdbcmd -n -batch 这里直接让gdb:
不交互(-batch)
不执行.gdbinit(-n)
挂在进程下(-p pid)
执行命令脚本(-x gdbcmd)并退出

4.3 gdb内部命令

命令 简写 描述
file   装入想要调试的可执行文件
list l 列出源代码(10行)
list          #显示接上次之后的10行
list func #显示func之后10行
list -       #上次显示程序代码的前面的十行
list 1      #跳转到第1行
print p 打印变量
p b            #打印变量,结果b$1 = 15.
p b = 100 #修改变量
p sizeof(b)#打印b长度
set   设置变量
set a = 4
display   显示变量
display a #在每个断点或是每执行一步时显示该叙述值
run r 执行当前被调试的程序
next n 执行一行源代码但不进入函数内部
step s 执行一行源代码而且进入函数内部
continue c 继续运行
finish   执行现行程序到回到上一层程序为止
quit q 终止 gdb
watch   监控变量,变量变化时打印
watch   #一量表达式值有变化时,马上停住程序。
rwatch  #当表达式(变量)expr被读时,停住程序。
awatch #当表达式(变量)的值被读或被写时,停住程序。
info watchpoints #列出当前所设置了的所有观察点。
break b 设置断点
b 23               #对当前文件23行中断
b func             #对函数func进行中断
b filename.c:23    #对文件filename.c中指定行23行中断
b filename.c:func  #对文件filename.c中指定func位置中断
b                  #在下一个将被执行的命令设定断点
b ... if cond      #只有当 cond(C语言) 成立的时候才中断,break if i=100,表示当i为100时停住程序
clear   清除断点,格式同 break 
clear filename.c:30
disable   禁止断点或显示
disable break 1
disable display 1
delete d 清除断点,NUM 是在 info break 显示出来的断点编号。
delete NUM
call   调用函数
call func(args)
kill   终止正在调试的程序
make   使你能不退出 gdb 就可以重新产生可执行文件
shell   使你能不离开 gdb 就执行 UNIX shell 命令
catch   catch event
当event发生时,停住程序。event可以是下面的内容:
1、throw 一个C++抛出的异常。(throw为关键字)
2、catch 一个C++捕捉到的异常。(catch为关键字)
3、exec 调用系统调用exec时。(exec为关键字,目前此功能只在HP-UX下有用)
4、fork 调用系统调用fork时。(fork为关键字,目前此功能只在HP-UX下有用)
5、vfork 调用系统调用vfork时。(vfork为关键字,目前此功能只在HP-UX下有用)
6、load 或 load 载入共享库(动态链接库)时。(load为关键字,目前此功能只在HP-UX下有用)
7、unload 或 unload 卸载共享库(动态链接库)时。(unload为关键字,目前此功能只在HP-UX下有用)

tcatch event
只设置一次捕捉点,当程序停住以后,应点被自动删除。。
info i 查询信息,如info break
backtrace bt 查看函数调用栈信息
使用up down命令来查看上下语句
使用frame 5 来查看栈中第5条
frame f 进入函数的某层栈
使用frame 5 来查看栈中第5条
否则会出现No symbol "role" in current context.的报错
help h 帮助信息
examine x 查看内存:x/<n/f/u> <addr>
参数 含义 例子
n 内存长度 3
f 显示格式 x [hex]按十六进制格式显示变量。
d [decimal]按十进制格式显示变量。
u [unsigned decimal]按无符号整型。
o [octal]按八进制格式显示变量。
t [binary]按二进制格式显示变量。
a [address]按地址格式显示变量。
c [char]按字符格式显示变量。
f [float]按浮点数格式显示变量。
s [string]按字符串格式显示变量。
u 字节数 b表示单字节,
h表示双字节,
w表示四字节,
g表示八字节。

generate-core-file g 主动产生core文件
set follow-fork-mode [parent|child]   调试fork父子进程,默认进入父进程



dis  :失效所有断点

enable 5:有效5号断点

until 700 :跳出循环,直接到700行

b item_serialize_to_db if src->type ==16726  :条件断点

b scene_recv_client_package if (xypkg.stHeader.nMsgid==8042 && xypkg->stBody.stLogin_pkg.nSelector==8042)

4.4 gdb调试core文件

core dump又叫核心转储, 当程序运行过程中发生异常, 程序异常退出时, 由操作系统把程序当前的内存状况存储在一个core文件中, 叫core dump。类unix操作系统中使用efi格式保存coredump文件。

4.4.1 core文件位置


core文件生成的位置一般于运行程序的路径相同。

echo "/corefile/core-%e-%p-%t" > core_pattern,可以将core文件统一生成到/corefile目录下,产生的文件名为core-命令名-pid-时间戳。

4.4.2 core文件名称

文件名一般为core.进程号

4.4.3 系统设置是否产生core dump

命令ulimit:
选项 [options] 含义例子
-H 设置硬资源限制,一旦设置不能增加。 
ulimit – Hs 64;限制硬资源,线程栈大小为 64K。
-S 设置软资源限制,设置后可以增加,但是不能超过硬资源设置。 
ulimit – Sn 32;限制软资源,32 个文件描述符。
-a 显示当前所有的 limit 信息。 
ulimit – a;显示当前所有的 limit 信息。
-c 最大的 core 文件的大小, 以 blocks 为单位。 
ulimit – c unlimited; 对生成的 core 文件的大小不进行限制。
-d 进程最大的数据段的大小,以 Kbytes 为单位。 
ulimit -d unlimited;对进程的数据段大小不进行限制。
-f 进程可以创建文件的最大值,以 blocks 为单位。 
ulimit – f 2048;限制进程可以创建的最大文件大小为 2048 blocks。
-l 最大可加锁内存大小,以 Kbytes 为单位。 
ulimit – l 32;限制最大可加锁内存大小为 32 Kbytes。
-m 最大内存大小,以 Kbytes 为单位。 
ulimit – m unlimited;对最大内存不进行限制。
-n 可以打开最大文件描述符的数量。 
ulimit – n 128;限制最大可以使用 128 个文件描述符。
-p 管道缓冲区的大小,以 Kbytes 为单位。 
ulimit – p 512;限制管道缓冲区的大小为 512 Kbytes。
-s 线程栈大小,以 Kbytes 为单位。 
ulimit – s 512;限制线程栈的大小为 512 Kbytes。
-t 最大的 CPU 占用时间,以秒为单位。 
ulimit – t unlimited;对最大的 CPU 占用时间不进行限制。
-u 用户最大可用的进程数。 
ulimit – u 64;限制用户最多可以使用 64 个进程。
-v 进程最大可用的虚拟内存,以 Kbytes 为单位。 
ulimit – v 200000;限制最大可用的虚拟内存为 200000 Kbytes。

#ulimit -c 1024       #设置core文件大小,这里的size的单位是blocks,一般1block=512bytes
#ulimit -c unlimited  #设置core文件可以产生
-c是显示:core file size          (blocks, -c)

/proc/sys/kernel/core_uses_pid决定core文件的文件名中是否添加pid作为扩展(设置1或0)。
/proc/sys/kernel/core_pattern决定core文件的文件名格式和位置(当前为core_%p,表示命名格式)。

1,core dump文件可以配置到/data/corefile/目录下, 但是不好,因为这样所有的core(包括程序员的)都会导入到这个位置。
2,core dump文件可以使用ulimit来设置文件大小,但是最好设置为0或unlimited。0表示不生成,unlimited表示都生成,其他数字如100表示小于100的才产生。

3,如果想让修改永久生效,则需要修改配置文件,如 .bash_profile、/etc/profile或/etc/security/limits.conf。

4.4.4 core输出位置

一般默认是当前目录,可以在/proc/sys/kernel中找到core-user-pid,通过
1.使core文件名加上pid号

如果这个文件的内容被配置成1,那么即使core_pattern中没有设置%p,最后生成的core dump文件名仍会加上进程ID。

echo "1" > /proc/sys/kernel/core_uses_pid
2.控制core文件保存位置和文件名格式

在进程当前工作目录的下创建。通常与程序在相同的路可能改变了当前工作目录。这时core文件创建在chdir指定的路径下。有好多程序崩溃了,我们却找不到core文件放在什么位置。和chdir函数就有关系。当然程序崩溃了不一定都产生core文件。

vi /proc/sys/kernel/core_pattern

通过下面的命令可以更改coredump文件的存储位置,若你希望把core文件生成到/data/coredump/core目录下(如果在上述文件名中包含目录分隔符“/”,那么所生成的core文件将会被放到指定的目录中):

echo "/data/coredump/core"> /proc/sys/kernel/core_pattern  <span style="font-family: Arial, Helvetica, sans-serif; font-size: 12px; background-color: rgb(255, 255, 255);">   </span>

如果是默认输出在本地目录下:

echo "core_%e_%p_%t"> /proc/sys/kernel/core_pattern   

以下是参数列表:

    %p - insert pid into filename 添加pid
    %u - insert current uid into filename 添加当前uid
    %g - insert current gid into filename 添加当前gid
    %s - insert signal that caused the coredump into the filename 添加导致产生core的信号
    %t - insert UNIX time that the coredump occurred into filename 添加core文件生成时的unix时间
    %h - insert hostname where the coredump happened into filename 添加主机名
    %e - insert coredumping executable name into filename 添加命令名

4.4.5 core文件产生原因

关于Core产生的原因很多,比如过去一些Unix的版本不支持现代Linux上这种GDB直接附着到进程上进行调试的机制,需要先向进程发送终止信号,然后用工具阅读core文件。在Linux上,我们就可以使用kill向一个指定的进程发送信号或者使用gcore命令来使其主动出Core并退出。如果从浅层次的原因上来讲,出Core意味着当前进程存在BUG,需要程序员修复从深层次的原因上讲,是当前进程触犯了某些OS层级的保护机制,逼迫OS向当前进程发送诸如SIGSEGV(即signal 11)之类的信号, 例如访问空指针或数组越界出Core,实际上是触犯了OS的内存管理,访问了非当前进程的内存空间,OS需要通过出Core来进行警示,这就好像一个人身体内存在病毒,免疫系统就会通过发热来警示,并导致人体发烧是一个道理(有意思的是,并不是每次数组越界都会出Core,这和OS的内存管理中虚拟页面分配大小和边界有关,即使不出Core,也很有可能读到脏数据,引起后续程序行为紊乱,这是一种很难追查的BUG)。

4.4.6 例子test.c

例子还是比较难找的,比如除0等会再编译阶段检测到报错。

/*test.c*/
#include <stdio.h>
void a()
{
    char *p = NULL;
    printf("%d/n", *p);
}

int main()
{
    a();
    return 0;
}
编译:

gcc -g -o test test.c
运行:

$ ./test
结果:

Segmentation fault (core dumped)

并生成core文件:core.25805

调试:

gdb [exec file] [core file]

gdb ./test core.25805
可以使用backtrace或where方法来检查程序运行到哪里

[New LWP 25805]
Core was generated by `./core_dump_test'.
Program terminated with signal 11, Segmentation fault.
#0  0x00000000004004d8 in a () at core_dump_test.c:6
6           printf("%d/n", *p);
Missing separate debuginfos, use: debuginfo-install glibc-2.14.90-24.fc16.9.x86_64
(gdb) backtrace
#0  0x00000000004004d8 in a () at core_dump_test.c:6
#1  0x0000000000400502 in main () at core_dump_test.c:11
(gdb)

#系统生成的core文件
gdb -c core_8122
可以查看到什么命令生成了这个core,等信息。

4.5 gdb调试带参数程序

程序运行参数设置:
set args 可指定运行时参数。(如:set args 10 20 30 40 50) 
show args 命令可以查看设置好的运行参数。 

4.6 gdb调试正在运行的进程

4.6.1 先gdb后起进程

#gdb server
>r

4.6.2 先起进程后gdb

这种情况一般在于该脚本是开机启动的,没办法先gdb后启动进程。

#gdb server 5566  #5566是进程号
>b 58
>c
>detach

#gdb -p 5566
>b 58
>c
>detach

#gdb
>attach 5566
>b 58 
>c
>detach

5 linux发行版本分类

http://zhoulifa.bokee.com/5444771.html

Linux 的发行版本可以大体分为三类,
1,一类是商业公司维护的发行版本【以著名的Redhat(RHEL)为代表】
Redhat(.rpm  [yum|zypper]),应该称为Redhat系列,包括
1,RHEL(Redhat Enterprise Linux,也就是所谓的Redhat Advance Server,收费版本)、
2,FedoraCore(由原来的Redhat桌面版本发展而来,免费版本)、CentOS(RHEL的社区克隆版本,免费)。
3,SUSELinux(使用zypper)
2,一类是社区组织维护的发行版本【以 Debian为代表】。
Debian(.deb  [apt-get|dpkg]),或者称Debian系列,包括Debian和Ubuntu 等。
1,Debian是社区类Linux的典范,是迄今为止最遵循GNU规范的Linux系统。
2,Ubuntu是基于 Debian的unstable版本加强而来

5 Linux下软件安装方法总结

5.1 rpm包安装方式步骤

rpm -ivh soft.version.rpm
rpm(命令)
-i  install
-v 查看安装信息
-h 以安装信息栏显示安装进度

5.2 deb包安装方式步骤

dpkg -i xxx.deb          //安装deb软件包
dpkg -r xxx.deb          //删除软件包
dpkg -r --purge xxx.deb  //连同配置文件一起删除
dpkg -info xxx.deb       //查看软件包信息
dpkg -L xxx.deb          //查看文件拷贝详情
dpkg -l                  //查看系统中已安装软件报信息
dpkg-reconfigure xxx     //重新配置软件包

5.3 tar.gz源代码包安装方式

1、找到相应的软件包,比如soft.tar.gz,下载到本机某个目录;
2、打开一个终端,su -成root用户;
3、cd soft.tar.gz所在的目录;
4、tar -xzvf soft.tar.gz //一般会生成一个soft目录
5、cd soft
6、你观察一下这个目录中包含了以下哪一个文件:configure、Makefile还是Imake。
1)如果是configure文件,就执行:
  #./configure 
  #make
  #make install
2)如果是Makefile文件,就执行:
  #make
  #make install
3)如果是Imake文件,就执行:
  #xmkmf
  #make
  #make install
7、删除安装时产生的临时文件:
  #make clean 
  卸载软件:
  #make uninstall

5.4 apt方式安装

sudo apt-cache search softname    //搜索软件包
sudo apt-get install softname     //安装软件
sudo apt-get remove softname      //卸载软件
sudo apt-get remove --purge softname //卸载并清除配置
sudo apt-get update               //更新软件信息数据库
【命令“sudo apt-get update”用来从软件源服务器获取最新的可用软件包的列表,在/etc/apt/sources.list文件中添加了某个软件源服务器的地址后,需要执行上面的命令来更新信息.】

5.5 yum方式安装

列出资源库中所有可以安装或更新的rpm包
# yum list

列出资源库中特定的可以安装或更新以及已经安装的rpm包的信息
# yum info perl           //列出perl 包信息
# yum info perl*          //列出perl 开头的所有包的信息

搜索匹配特定字符的rpm包
# yum search perl         //在包名称、包描述等中搜索

安装rpm包
# yum install perl        //安装perl 包
# yum install perl*       //安装perl 开头的包

删除rpm包,包括与该包有倚赖性的包
# yum remove perl*        //会删除perl-* 所有包

更新指定的rpm包,如更新perl
# yum update perl

5.6 zypper方式安装

设置好源以后,就需要先刷新源软件,请耐心等待,尤其是第一次使用的时候会需要较长时间。
zypper refresh
查询某个软件包
zypper search package_name
查看软件包信息
zypper info package_name
安装某个软件包
zypper install package_name
卸载某个软件包
zypper remove package_name
升级某个软件包
zypper update package_name

5.7 bin文件安装

如果你下载到的软件名是soft.bin,一般情况下是个可执行文件,安装方法如下:
1、打开一个终端,su -成root用户;
2、chmod +x soft.bin
3、./soft.bin //运行这个命令就可以安装软件了

5.8 不需要安装的软件

有了些软件,比如lumaqq,是不需要安装的,自带jre解压缩后可直接运行。假设下载的是lumaqq.tar.gz,使用方法如下:
1、打开一个终端,su -成root用户;
2、tar -xzvf lumaqq.tar.gz //这一步会生成一个叫LumaQQ的目录
3、cd LumaQQ
4、chmod +x lumaqq //设置lumaqq这个程序文件为可运行
5、此时就可以运行lumaqq了,用命令./lumaqq即可,但每次运行要输入全路径或切换到刚才生成的LumaQQ目录里
6、为了保证不设置路径就可以用,你可以在/bin目录下建立一个lumaqq的链接,用命令ln -s lumaqq /bin/ 即可,以后任何时候打开一个终端输入lumaqq就可以启动QQ聊天软件了
7、如果你要想lumaqq有个菜单项,使用菜单编辑工具,比如Alacarte Menu Editor,找到上面生成的LumaQQ目录里的lumaqq设置一个菜单项就可以了,当然你也可以直接到/usr/share/applications目录,按照里面其它*.desktop文件的格式生成一个自己的desktop文件即可。


建议开发者们不要用red hat/red flag/suse等第一代Linux操作系统,用这些除了方便(也就是说比较傻瓜型)、界面豪华点外没什么好。用debian/ubuntu等第二代吧,apt命令安装软件是最简单的。apt命令会自动解决软件安装过程中的依赖问题,会把没有的包安装上,会把版本低的包自动升级,当然,都是要经你确认一次的。
如果你使用Red Hat等第1代Linux系统,安装软件是比较麻烦的事,rpm -ivh softA.rpm是用来安装softA软件的,但通常情况下可能遇到的问题是提示说需要安装softB1, softB2, softB3等一堆软件,然后你安装softB1软件包时,可能又会提示你说需要安装softC1, softC2, softC3, softC4等一堆软件……这样一来你就只够时间到处去找这些软件包了。光盘上没有就得去网上下载,网上还得搜索半天,时间都花在搜索软件包了。而且就算找到这些软件包,还可能会遇到的问题是:softC2软件包必须在softC1软件包之前安装才可以,顺序错了也安装不成功。但这谁知道呢?难啊。所以,你没有时间来安装体验软件的功能了,更别说开发软件了。
建议你安装第2代Linux操作系统,典型的是Debian Linux和Ubuntu Linux,我之前在文章“如何在安装了Windows操作系统的电脑上安装Linux操作系统”里提到一个简明安装手册,你下载来照着操作就可以安装Ubuntu了。
第2代操作系统在安装软件方面相当简单:第一步,搜索你要的软件,比如你要找一个游戏软件,它的名称叫myward,这个游戏软件的说明是:my own war game。搜索这个软件包就只需要输入命令apt-cache search myward,或者输入软件名称的一部分apt-cache search war,或者你不知道软件名称,输入软件说明里的一部分文字apt-cache search game,都可以找到个软件,找到后进行第二步,只需要输入apt-get install myward,即后面跟上软件名称就可以安装了。


5.9 在Debian系统上安装RadHat系统的.rpm文件

1、获取正向rpm->deb软件alien
2、sudo apt-get install alien
3、然后就可以对rpm 格式的软件转换成deb 格式了:alien -d *.rpm
4、然后就可以用deb 的安装方式进行软件安装,也可以不需转换而直接对rpm 包进行安装:alien -i *.rpm,更多的alien 使用方法可以用-h 参数查看相应说明文

6 Linux中常用宏定义

6.1 防止一个头文件被重复包含

#ifndef COMDEF_H
#define COMDEF_H

//头文件内容 …
#endif

6.2 重新定义一些类型

防止由于各种平台和编译器的不同,而产生的类型字节数差异,方便移植。
typedef  unsigned long int  uint32;      /* Unsigned 32 bit value */

6.3 得到指定地址上的一个字节或字

#define  MEM_B( x )  ( *( (byte *) (x) ) )
#define  MEM_W( x )  ( *( (word *) (x) ) )

6.4 求最大值和最小值

#define  MAX( x, y )  ( ((x) > (y)) ? (x) : (y) )
#define  MIN( x, y )  ( ((x) < (y)) ? (x) : (y) )

6.5 得到一个field在结构体(struct)中的偏移量

#define FPOS( type, field )   ( (dword) &(( type *) 0)-> field )

6.6 得到一个结构体中field所占用的字节数

#define FSIZ( type, field ) sizeof( ((type *) 0)->field )

6.7 按照LSB格式把两个字节转化为一个word

#define  FLIPW( ray ) ( (((word) (ray)[0]) * 256) + (ray)[1] )

6.8 按照LSB格式把一个word转化为两个字节

#define  FLOPW( ray, val ) 
(ray)[0] = ((val) / 256); 
(ray)[1] = ((val) & 0xFF)

6.9 得到一个变量的地址(word宽度)

#define  B_PTR( var )  ( (byte *) (void *) &(var) )
#define  W_PTR( var )  ( (word *) (void *) &(var) )

6.10 得到一个字的高位和低位字节

#define  WORD_LO(xxx)  ((byte) ((word)(var) & 255))
#define  WORD_HI(xxx)  ((byte) ((word)(var) >> 8))

6.11 返回一个比X大的最接近的8的倍数

#define RND8( x )       ((((x) + 7) / 8 ) * 8 )

6.12 将一个字母转换为大写

#define  UPCASE( c ) ( ((c) >= ’a' && (c) <= ’z') ? ((c) - 0×20) : (c) )

6.13 判断字符是不是10进值的数字

#define  DECCHK( c ) ((c) >= ’0′ && (c) <= ’9′)

6.14 判断字符是不是16进值的数字

#define  HEXCHK( c ) ( ((c) >= ’0′ && (c) <= ’9′) ||
((c) >= ’A' && (c) <= ’F') ||
((c) >= ’a' && (c) <= ’f') )

6.15 防止溢出的一个方法

#define  INC_SAT( val )  (val = ((val)+1 > (val)) ? (val)+1 : (val))

6.16 返回数组元素的个数

#define  ARR_SIZE( a )  ( sizeof( (a) ) / sizeof( (a[0]) ) )

6.17 对于IO空间映射在存储空间的结构,输入输出处理

#define inp(port)         (*((volatile byte *) (port)))
#define inpw(port)        (*((volatile word *) (port)))
#define inpdw(port)       (*((volatile dword *)(port)))

#define outp(port, val)   (*((volatile byte *) (port)) = ((byte) (val)))
#define outpw(port, val)  (*((volatile word *) (port)) = ((word) (val)))
#define outpdw(port, val) (*((volatile dword *) (port)) = ((dword) (val)))

6.18 使用一些宏跟踪调试

ANSI标准说明了五个预定义的宏名。它们是:
__LINE__
__FILE__
__DATE__
__TIME__
__STDC__
如果编译不是标准的,则可能仅支持以上宏名中的几个,或根本不支持。记住编译程序也许还提供其它预定义的宏名。
__FILE__ 是内置宏 代表源文件的文件名
__LINE__ 是内置宏,代表该行代码的所在行号
__DATE__宏指令含有形式为月/日/年的串,表示源文件被翻译到代码时的日期。
__TIME__源代码翻译到目标代码的时间作为串包含在__TIME__ 中。串形式为时:分:秒。
__STDC__如果实现是标准的,则宏__STDC__含有十进制常量1。如果它含有任何其它数,则实现是非标准的。

可以定义宏,例如:
当定义了_DEBUG,输出数据信息和所在文件所在行
#ifdef _DEBUG
#define DEBUGMSG(msg,date) printf(msg);printf(“%d%d%d”,date,_LINE_,_FILE_)
#else
#define DEBUGMSG(msg,date)
#endif

6 运行库

6.1 静态库

.a文件是简单的把一些.o文件打包产生的。

6.2 动态库

6.2.1 动态库

.so文件,但和共享库不同,由程序自己决定什么时候加载/卸载
我的理解是这种是【动态库中动态加载方式】,在程序开始运行前,程序完全不了解这些库的细节,也不会加载,只有在运行到调用这个函数时,才会去动态加载这个库。

6.2.2 共享库

.so文件,在需要时由操作系统自动加载
我的理解是这种是【动态库中静态加载方式】,在程序开始运行前,就将加载这些库。

6.3 动态库与静态库区别

动态库.so是已经编译好的二进制文件,可以动态加载。这样不需要每个用户重新编译自己的所有文件(因为.a文件还是.o的集合,需要再生成为二进制,所以.a的改动会造成全部需要重新编)