一、前言
Win下C/C++开发,单就环境来说,熟悉VS环境就差不多了,也许最好熟悉MFC、DotNet等微软特定的库及平台。
Linux下C/C++开发,就环境来说,要熟悉这4大件
- Linux常用命令。(只列一下git吧,其它略过。)
- gcc、g++、make套件命令使用。
- 能处理Makefile内容。
- 能使用gdb调试。
我做了5年以上Linux下的C、C++(gcc)开发,也做了5年以上Windows下的C++ MFC(VS)开发。我也做过3年以上Python开发(Python 3.6以上,Linux、Windows都做)及Python-C++混编项目。
以前最熟悉的是Linux gcc环境。最近几年,反而Win VS用的多。甚至最近这些东西都没怎么碰了一段时间几个月了吧。
Linux下g++、gcc、make等应用,有时简单称为gcc环境开发。
二、Linux常用命令
这块内容本文就基本就略过了。其实也不仅仅常见命令,如果真做过项目,对于Linux发行版的安装,硬盘分区,编译内核,裁剪内核,调试驱动小模块,及裁剪文件系统,网络远程登陆,搭建服务器应用,整bootloader,无所不涉及过。
所以,尽量收拢一下。
sed、grep、find、ssh、scp、tar,apt-get或yum、rpm、readelf、ldd这些常用吧。cp、ls这些则真不提了。
vi、vim,那真必须会,而且是信仰。我在Win VS中都要装个vim插件。
以上都不展开了。
另外,shell脚本常用的命令,比如:
#!/bin/bash
MEM_TOTAL=$(free -m | awk '/Mem:/ {print $2}')
除了sed,awk常用吧。cut常用吧。
这样吧,列一下常见git代码仓库使用算了。
三、git命令的基本使用
Git 管理代码仓库时,文件流转的三个工作区域:Git 的工作区,暂存区域,以及本地仓库。
还要加一个远程仓库。
假定git已经安装了,而且假设要连远程仓库。
git config --global user.name 你的用户名
git config --global user.email 你的邮箱地址
这是你在一台Linux上要做的第一件事。
ssh-keygen -t ed25519 -C “comments-X”
这是第二件事,.ssh/id_ed25519.pub公钥拿去贴进远程仓库界面。
这样后面使用git命令时和远程交互就免密了。
接着在仓库的本地工作目录,执行git init。
从远程仓库拉代码到本地,用git clone。
git add ./
如果新加文件,那就执行git add。
或者修改哪个代码文件后。
用git status查看,本地修改或加了哪些文件。
git commit -m "代码提交信息"
用git commit,提交到本地仓库。
用git diff,你可以设置调用vimdiff查看有什么代码修改冲突需要解决的。冲突解决了就commit。
仅提交到本地不行,还需要推送到远程仓库。
git push origin master
因此这条就是推送到远程仓库master分支了。
前面提交前,最好执行git pull获得远程仓库最新的代码版本。
常用的还有git checkout和git revert命令。
当然也少不了git log命令查看操作记录。
Linux下git这些命令最常用了。
不过如图,我在Win下时,用的是小乌龟git的GUI。从SVN年代就开始用小乌龟了。
四、Makefile的基本使用
一两个代码文件,使用gcc命令就够了,但如果项目文件一多,或者嫌弃命令不好记,或者老是敲一长串命令麻烦,那就用Makefile。
有了Makefile,执行make时,自动会去调用里面指定的命令。
all:
g++ -O2 -o app main.cpp foo.cpp
clean:
rm -f app
这是一个最简单的Makefile例子。
g++前面缩进空格,不能真是空格,一定要是TAB按键出来的缩进,这是很脑残的。现今应该还是这样吧。
这个例子,使用了两个.cpp源代码文件,而且是编译链接一步到位产生二进制可执行的app程序。
all: main.o foo.o
g++ -o app main.o foo.o
main.o:
g++ -o main.o -c main.cpp
也可以如此拆开成两步,第一步是g++ -o main.o -c main.cpp先编译,然后再all下面的链接build。
项目稍大后,会有库API,静态库、动态库。
libtest1.a : test1.o
ar -cr libtest1.a test1.o
test1.o : test1.cpp
g++ -c -o test1.o test1.cpp
这是Makefile里调用ar做一个静态库的例子。
export LD_LIBRARY_PATH:=$LD_LIBRARY_PATH:/func
test : test.cpp func/libfunc.so
g++ -o test test.cpp -L./func -lfunc
func/libfunc.so : func/func.cpp func/func.h
g++ -fPIC -shared -o func/libfunc.so func/func.cpp
而这g++ -fPIC -shared -o func/libfunc.so func/func.cpp是动态库例子。
今天略过cmake。
五、Makefile和gcc命令的进一步使用
进一步使用,就要用到大量的宏变量了,而且是和Linux shell命令结合。
FILES := $(wildcard $(SRCDIR)/*.cpp)
OBJS := $(foreach file, $(FILES), $(file:.cpp=.o))
这样的Makefile,就把某目录下的所有.cpp文件当作源文件,不需要手动往里面一个一个的加。
CC = gcc
CXX = g++
CFLAGS = -Wall -O2
CXXFLAGS = -Wall -O2 -I$(LIBMTN_CFG_PATH)/include
LDFLAGS = -lssl -lcrypto -lcrypt
CC、CXX宏变量,可以是gcc、g++,也可以是gcc-arm之类的跨平台使用,并且真跨平台时,gcc或gcc-arm也可以去自动获得。
如果需要gdb去调试,CXXFLAGS可以加上-g。-I是指定外部库的头文件包含路径。
具体来说,LDFLAGS的-lssl 表示链接 OpenSSL 的 SSL 库,-lcrypto 表示链接 OpenSSL 的 Crypto 库。
$(OBJDIR)/%.o: $(SRCDIR)/%.cpp
$(CXX) $(CXXFLAGS) -c -o $@ $<
有了宏变量,写编译的那两行就可以变成这样。
关于Makefile宏变量内容其实挺多,这里粗糙一点,简单提一两句就算了。
今天略过m4
宏处理器。
六、使用gdb调试程序
gcc build程序时开启了调试信息保留,后续就可以使用gdb调试了。
调试的内容就是去查看程序的状态,包括断点、变量值、函数调用栈、及哪个线程的信息等。
第1个常用命令就是list/l,列出源代码的第几行开始的片段。或者列出哪个函数。
设置断点是第2个常用命令,break(b) 行号或函数名。
(gdb) b 25
在gdb中,敲b 25就是在当前代码的第25行设置断点。
断点也可以设置在某个函数。
info(i) break(或breakpoints) :查看各断点信息。i break。
disable breakpoints n:禁用序号为n的断点。dis break n。
enable breakpoints n:启用序号为n的断点。e break n。
delete(d) breakpoints n:删除序号为n的断点。del break n。不指定n则删除所有断点。
r或run:当没有断点时运行程序,当有断点时运行到断点处。
n 或 next:单步调试,当使用n命令进行单步调试时,gdb会执行下一行代码,如果下一行是执行某个函数,不会进入函数,而是就此执行函数了。
s或step:单步调试,当使用s命令进行单步调试时,gdb同样会执行下一行代码。但是,如果下一行代码是一个函数调用,s命令会进入这个函数内部,并从函数的第一条语句开始逐行执行。
finish:执行到当前函数返回,然后停下来等待后续命令。
print ( p ):打印一个表达式的值,表达式可以由变量构成。
sum = a + b;
(gdb) p sum
p 变量:打印某个变量的值。比如,sum = a + b行运行后,用p查看sum的结果。
print(p) /x 地址,显示为16进制值。
set var:修改某个变量的值。
continue(或c):继续执行被调试的程序,直到遇到下一个断点、程序结束或遇到其他导致程序停止的事件。
display 变量名:跟踪查看一个变量,每次停下来都显示它的值。
undisplay n:取消对序号为n的变量的跟踪。
until :主要作用是继续执行程序,直到当前栈帧(通常是当前函数)的返回点,或者遇到下一个断点。
until n:n为某一行代码的行号,该命令会使程序运行至第n行代码处停止。
breaktrace(或bt):查看各级函数调用及参数。就是查看函数调用栈,即stack frames。
frame n(f n)n为对应的堆栈。
info (i) locals:查看当前栈帧局部变量的值。
info all-registers:查看所有寄存器情况。
quit (q):退出gdb。
info threads 命令可以查询被调试程序中的线程的信息。
thread n 命令可以切换当前线程,如thread 2把线程2切换为当前线程。
指定某个线程或所有线程执行:
- thread apply all bt:打印所有线程的调用栈信息
- thread apply 3 bt:打印线程3的调用栈信息
- thread apply 2-3 bt:打印线程2和线程3的调用栈信息
针对特定线程设置断点
break 5 thread 3 # 在第5行设置断点,仅对线程3生效
break 5 thread 3 if <condition> # 在第5行设置条件断点,仅对线程3生效
还有一个命令是,指定源文件,因为有时不知怎的,会发生gdb就没找到源文件的情况。
directory(dir) path添加一个源文件路径到当前路径前,可以多个源文件路径。
GNU gdb (GDB) 7.1-ubuntu
(gdb) list main
1192 ls.c: No such file or directory.
in ls.c
(gdb) directory ~/src/coreutils-7.4/src/
Source directories searched: /home/hchen/src/coreutils-7.4:$cdir:$cwd
(gdb) list main
1192 }
1193 }
1194
1195 int
1196 main (int argc, char **argv)
1197 {
今天文章就先这样吧。等有时间再完善内容。