linux編程快速入門學習筆記

ubuntu開發環境安裝gcc/make/gdb

更新安裝源
apt-get update
安裝gcc和c++的開發庫,安裝的時候自動包含其它依賴庫
apt-get install g++
調試工具
apt-get install gdb
apt-get install make
遠程連接工具
apt-get install openssh-server
編輯工具
apt-get install vim

使用gcc/g++編譯程序詳解

gcc(g++)

預處理,頭文件include、導入庫、宏
編譯,檢測語法等錯誤,將.c/.cpp等文件轉化爲.o或.obj
彙編
鏈接,.o/.so或.obj/.dll等文件轉化爲exe

gcc [ 選項 ] <文件名>

gcc常用選項

選項 含義
-o file 將經過gcc處理過的結果存爲文件file,這個結果文件可能是預 處理文件、彙編文件、目標文件或者最終的可執行文件。假設 被處理的源文件爲source.suffix,如果這個選項被省略了,那 麼生成的可執行文件默認名稱爲a.out;目標文件默認名爲 source.o;彙編文件默認名爲source.s;生成的預處理文件則 發送到標準輸出設備。
-c 僅對源文件進行編譯,不鏈接生成可執行文件。在對源文件 進行查錯時,或只需產生目標文件時可以使用該選項。
-g[gdb] 在可執行文件中加入調試信息,方便進行程序的調試。如果 使用中括號中的選項,表示加入gdb擴展的調試信息,方便 使用gdb來進行調試
-O[0、1、2、3] 對生成的代碼使用優化,中括號中的部分爲優化級別,缺省 的情況爲2級優化,0爲不進行優化。注意,採用更高級的優 化並不一定得到效率更高的代碼。
-Dname[=definition] 將名爲name的宏定義爲definition,如果中括號中的部分缺 省,則宏被定義爲1
-Idir 在編譯源程序時增加一個搜索頭文件的額外目錄——dir,即include增加一 個搜索的額外目錄。
-Ldir 在編譯源文件時增加一個搜索庫文件的額外目錄——dir
-llibrary 在編譯鏈接文件時增加一個額外的庫,庫名爲liblibrary.so
-w 禁止所有警告
-Wwarning 允許產生warning類型的警告,warning可以是:main、unused等很多取值, 最常用是-Wall,表示產生所有警告。如果warning取值爲error,其含義是將 所有警告作爲錯誤(error),即出現警告就停止編譯。

gcc文件擴展名規範

擴展名 類型 可進行的操作方式
.c c語言源程序 預處理、編譯、彙編、 鏈接
.C,.cc,.cp,.cpp,. c++,.cxx c++語言源程序 預處理、編譯、彙編、 鏈接
.i 預處理後的c語言源程序 編譯、彙編、鏈接
.ii 預處理後的c++語言源程序 編譯、彙編、鏈接
.s 預處理後的彙編程序 彙編、鏈接
.S 未預處理的彙編程序 預處理、彙編、鏈接
.h 頭文件 不進行任何操作
.o 目標文件 鏈接

g++ 分步編譯

gdb調試工具使用和coredump

#1 預處理 (Preprocessing)
g++ -E test.cpp >test.ii
#2 編譯 (Compilation)
g++ -S test.ii
#3 彙編 (Assembly)
g++ -c test.s
#4 鏈接 (Linking)
g++ test.o -o test
#5 運行
./test

gdb調試器

gdb概述

Linux下的gdb調試器,是一款GNU組織開發併發布的UNIX/Linux下的程 序調試工具。它沒有圖形化的友好界面,但功能強大。

gdb指令參數

命令格式 作用
list <行號>|<函數名> 查看指定位置的程序源代碼
break 行號|函數名<條件表達式> 設置斷點
info break 顯示斷點信息
run 運行程序
print 表達式|變量 查看程序運行時對應表達式和變量的值
next 單步恢復程序運行,但不進入函數調用
step 單步恢復程序運行,且進入函數調 用
continue 繼續執行函數,直到函數結束或遇到新斷點

coredump

vim test.cpp
#include<iostream>
using namespace std;
#define NAME "NAME=cpp"
int main(int argc,char *argv[])
{
        cout<<"test g++ makefile"<<endl;
        int *c = 0;
        *c = 1000;//error
        cout<<NAME<<endl;
        return 0;
}

按esc,輸入wq保存退出

生成帶調試信息的可執行文件test,文件會大一些

g++ test.cpp -o test -g

這是在調試狀態下發現的錯誤

gdb test
run
Program received signal SIGSEGV, Segmentation fault.
0x000000000040087d in main (argc=1, argv=0x7fffffffe1c8) at test.cpp:8
8               *c = 1000;//error

隨着項目代碼複雜度加大,有時候我們想在運行程序之後查看程序是在哪一行dump掉的。可以查看一下coredump,默認是關閉的。

(caffe2_env) zhoujianwen@zhoujianwen-System:~$ ulimit -c
0

啓動一下

(caffe2_env) zhoujianwen@zhoujianwen-System:~$ ulimit -c unlimited

再次查看coredump狀態

(caffe2_env) zhoujianwen@zhoujianwen-System:~$ ulimit -c
unlimited

執行test

(caffe2_env) zhoujianwen@zhoujianwen-System:~$ ./test
test g++ makefile
段錯誤 (核心已轉儲)

報錯信息存儲在core文件

(caffe2_env) zhoujianwen@zhoujianwen-System:~$ ls -a | grep core
core

gdb調試core文件,輸出錯誤信息,這樣我們就能知道現場環境的程序代碼是在哪裏dump掉的。

(caffe2_env) zhoujianwen@zhoujianwen-System:~$ gdb test core
warning: exec file is newer than core file.
[New LWP 8480]
Core was generated by `./test'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0  0x000000000040087d in main (argc=1, argv=0x7ffdbf9b7a58) at test.cpp:8
8               *c = 1000;//error

後面的調試當中,除了coredump會使用到,應用程序以調試狀態運行過程中出現問題,也能查看coredump發現問題,特別是系統在做熔斷的時候,我們生成兩次coredump作比較差異發現問題。

gcc編譯動態鏈接庫

-fPIC 編譯選項 -fPIC If supported for the target machine, emit position- independent code, suitable for dynamic linking,even if branches need large displacements. 產生位置無關代碼(PIC),一般創建共享庫時用到。 在x86上,PIC的代碼的符號引用都是通過ebx進行操作的。
-shared
示例

g++ -shared -fPIC mylib.cpp -o libmylib.so
g++ test.cpp -lmylib -L/root/cpp

#!/bin/bash 
LD_LIBRARY_PATH=./; 
export LD_LIBRARY_PATH
./testlib

Makefile中 -I -L -l區別
我們用gcc編譯程序時,可能會用到“-I”(大寫i),“-L”(大寫l),“-l”(小寫l)等參數,下面做個記錄:

例:
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靜態庫文件)

創建目錄

mkdir code
cd code
pwd
mkdir src
mkdir lib
mkdir bin
mkdir include
mkdir doc
(caffe2_env) zhoujianwen@zhoujianwen-System:~/code$ ls
bin  doc  include  lib  src
(caffe2_env) zhoujianwen@zhoujianwen-System:~/code$ cd src
(caffe2_env) zhoujianwen@zhoujianwen-System:~/code/src$ mkdir testlib
(caffe2_env) zhoujianwen@zhoujianwen-System:~/code/src$ cd testlib

創建文件,一個頭文件對應一個庫文件

 vim testlib.cpp
#include<iostream>
#include "test_lib.h"
using namespace std;
int main(int argc,char *argv[])
{
        TestLib t;
        return 0;
}
 vim test_lib.h
#ifndef TEST_LIB_H
#define TEST_LIB_H
class TestLib
{
public:
        TestLib();
};

#endif
 vim test_lib.cpp
#include "test_lib.h"
#include <iostream>
using namespace std;
TestLib::TestLib()
{
        std::cout<<"TestLib Create"<<std::endl;
}

生成xx.so動態鏈接庫

(caffe2_env) zhoujianwen@zhoujianwen-System:~/code/src/testlib$ (caffe2_env) zhoujianwen@zhoujianwen-System:~/code/src/testlib$ ls
test_lib.cpp  testlib.cpp  test_lib.h
(caffe2_env) zhoujianwen@zhoujianwen-System:~/code/src/testlib$ g++ -fPIC -shared test_lib.cpp -o libtestlib.so
(caffe2_env) zhoujianwen@zhoujianwen-System:~/code/src/testlib$ ls
libtestlib.so test_lib.cpp  testlib.cpp  test_lib.h

編譯testlib.cpp和-ltestlib,-ltestlib其實就是libtestlib.so文件,這是格式規範,指定共享庫路徑是在./當前路徑下查找,生成可執行文件testlib
注意:-l(小寫l),-L(大寫l)
-ltestlib表示在上面的lib的路徑中尋找libtestlib.so動態庫文件(如果gcc編譯選項中加入了“-static”表示尋找libtestlib.a靜態庫文件)

-L ./表示將~/code/src/testlib目錄作爲第一個尋找庫文件的目錄,尋找的順序是:~/code/src/testlib–>/lib–>/usr/lib–>/usr/local/lib

首先庫文件名(庫文件名不等於庫名)的命名規則爲lib+<庫名>+.so,如果有一個testlib庫, 那麼相應的庫文件爲 libtestlib.so 爲了在執行編譯命令的時候鏈接指定的庫,我們需要用到-L(大寫l)和-l(小寫l)命令.

如果在路徑 ~/code/src/testlib下面有一個庫文件叫做 libtestlib.so, 我想在編譯的時候鏈接它, 只需加上−ltestlib −L ~/code/src/testlib或−ltestlib −L ./命令即可。對於那些在/lib, /usr/lib 和 /usr/local/lib 路徑下的庫, 我們可以使用更加簡單的命令 −l(小寫l) 而無需添加庫文件目錄路徑

(caffe2_env) zhoujianwen@zhoujianwen-System:~/code/src/testlib$ g++ testlib.cpp -ltestlib -L./ -o testlib
(caffe2_env) zhoujianwen@zhoujianwen-System:~/code/src/testlib$ ls ./
libtestlib.so  testlib  test_lib.cpp  testlib.cpp  test_lib.h

當你執行./testlib就會提示找不到動態鏈接庫,主要是環境變量LD_LIBRARY_PATH沒有指定庫加載的正確路徑

(caffe2_env) zhoujianwen@zhoujianwen-System:~/code/src/testlib$ ./testlib
./testlib: error while loading shared libraries: libtestlib.so: cannot open shared object file: No such file or directory

查詢一下當前環境變量LD_LIBRARY_PATH的路徑是/usr/local/cuda-10.0/lib64,顯然不是我們剛纔的測試路徑~/code/src/testlib

(caffe2_env) zhoujianwen@zhoujianwen-System:~/code/src/testlib$ echo $LD_LIBRARY_PATH
/usr/local/cuda-10.0/lib64

解決共享庫沒法找到的問題,方法有幾種,最簡單是在剛纔目錄的當前路徑下創建一個run.sh,但是export LD_LIBRARY_PATH它只對當次run.sh執行操作有效。

vim run.sh
#!/bin/bash
LD_LIBRARY_PATH=./; 
export LD_LIBRARY_PATH
./test

按下esc,wq保存退出
修改run.sh執行權限

chmod +x run.sh
(caffe2_env) zhoujianwen@zhoujianwen-System:~/code/src/testlib$ ./run.sh
TestLib Create

makefile

GNU make用來構建和管理自己的工程
Makefile 文件描述了整個工程的編譯、 連接等規則

makefile介紹和第一個項目示例

(caffe2_env) zhoujianwen@zhoujianwen-System:~/code/src$ mkdir testmake
(caffe2_env) zhoujianwen@zhoujianwen-System:~/code/src$ cd testmake/
(caffe2_env) zhoujianwen@zhoujianwen-System:~/code/src/testmake$
vim testmake.cpp
#include <iostream>
using namespace std;
int main(int argc,char *argv[])
{
	std::cout<<"test makefile"<<std::endl;
	return 0;
}

按esc,保存退出wq

vim makefile

編輯如下內容

testmake:testmake.cpp
	g++ testmake.cpp -o testmake
	./testmake

按esc,保存退出wq,在保存之前確保tab格式輸入正確。
在這裏插入圖片描述

(caffe2_env) zhoujianwen@zhoujianwen-System:~/code/src/testmake$ ls
makefile  testmake  testmake.cpp
(caffe2_env) zhoujianwen@zhoujianwen-System:~/code/src/testmake$ make
g++ testmake.cpp -o testmake
./testmake
test makefile
(caffe2_env) zhoujianwen@zhoujianwen-System:~/code/src/testmake$ ./testmake
test makefile

通過執行語句,把依賴的文件生成目標。爲什麼不直接使用g++ testmake.cpp -o testmake ./testmake,而是另外新建一個testmake來管理呢?因爲當項目要編譯的文件數量比較多的時候,使用make可以更有效地管理和構建項目的工程代碼。下面只是編譯一個文件,如果有多個,或者是分開部署的呢,而且還要知道哪一部分編譯出錯了,這樣的管理效率就不一樣了。
在這裏插入圖片描述

Makefile中常見預定義變量

Makefile中常見預定義變量

makefile變量的使用

在這裏插入圖片描述

make和makefile

makefile文件主要包含了5部分內容:

  1. 顯式規則。說明了如何生成一個或多個目標文件。由makefile文件的創作者指出,包括要生成的文件、文件的依賴文件、生成的命令。
  2. 隱式規則。由於make有自動推導的功能,所以隱式的規則可以比較粗糙地簡略書寫makefile文件,這是由make所支持的。
  3. 變量定義。在makefile文件中要定義一系列的變量,變量一般都是字符串, 這與C語言中的宏有些類似。當makefile文件執行時,其中的變量都會擴展到相應的引用位置上。
  4. 文件指示。其包括3個部分,一個是在一個makefile文件中引用另一個 makefile文件;另一個是指根據某些情況指定makefile文件中的有效部分; 還有就是定義一個多行的命令。
  5. 註釋。makefile文件中只有行註釋,其註釋用“#”字符。如果要在 makefile文件中使用“#”字符,可以用反斜框進行轉義,如:“#”。

makefile變量使用和規則分析

在上面的例子中,先讓我們看看edit的規則:
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
我們可以看到[.o]文件的字符串被重複了兩次,如果我們的工程需要加入一個新的[.o]文件,那麼我們需要在兩個地方加(應該是三個地方,還有一個地方在clean 中)。當然,我們的makefile並不複雜,所以在兩個地方加也不累,但如果makefile變得複雜,那麼我們就有可能會忘掉一個需要加入的地方,而導致編譯失敗。所以,爲了makefile的易維護,在 makefile中我們可以使用變量。makefile的變量也就是一個字符串,理解成C語言中的宏可能會更好。
我們在makefile 一開始就這樣定義:
objects = main.o kbd.o command.o display.o
insert.o search.o files.o utils.o
於是,我們就可以很方便地在我們的makefile中以“$(objects)” 的方式來使用這個變量了,於是我們的改良版makefile就變成下面這個樣子:
edit : $(objects)
cc -o edit $(objects)

引入文件

include

解決方案配置_多項目依賴_配置管理器

(caffe2_env) zhoujianwen@zhoujianwen-System:~/code$ ls
bin  doc  include  lib  src
(caffe2_env) zhoujianwen@zhoujianwen-System:~/code$ cd src
(caffe2_env) zhoujianwen@zhoujianwen-System:~/code/src$ ls
testlib  testmake
(caffe2_env) zhoujianwen@zhoujianwen-System:~/code/src$ cd testlib
(caffe2_env) zhoujianwen@zhoujianwen-System:~/code/src/testlib$ ls
libtestlib.so  run.sh  testlib  test_lib.cpp  testlib.cpp  test_lib.h  test_lib.h.gch  test_lib.o  testlib.o
(caffe2_env) zhoujianwen@zhoujianwen-System:~/code/src/testlib$ vim makefile

編輯makefile文件

testlib.so:test_lib.cpp test_lib.h
        g++ $+ -o $@ -shared -fPIC
clean:
        rm -rf *.o *.so

按esc退出編輯模式,輸入wq保存退出。

make

(caffe2_env) zhoujianwen@zhoujianwen-System:~/code/src/testlib$ make
g++ test_lib.cpp test_lib.h -o testlib.so -shared -fPIC
(caffe2_env) zhoujianwen@zhoujianwen-System:~/code/src/testlib$ ls
libtestlib.so  run.sh   test_lib.cpp  test_lib.h   test_lib.o  testlib.so

make clean

(caffe2_env) zhoujianwen@zhoujianwen-System:~/code/src/testlib$ make clean
rm -rf *.o *.so
(caffe2_env) zhoujianwen@zhoujianwen-System:~/code/src/testlib$ ls
makefile  run.sh  testlib  test_lib.cpp  testlib.cpp  test_lib.h  test_lib.h.gch

修改makefile文件內容
rm -rf *.o *.so前添加@可隱藏rm -rf *.o *.so顯示語句的執行

testlib.so:test_lib.cpp test_lib.h
        g++ $+ -o $@ -shared -fPIC
clean:
        @echo "begin clean..."
        @rm -rf *.o *.so
        @echo "end clean."
(caffe2_env) zhoujianwen@zhoujianwen-System:~/code/src/testlib$ (caffe2_env) zhoujianwen@zhoujianwen-System:~/code/src/testlib$ make clean
begin clean...
end clean.

添加install

testlib.so:test_lib.cpp test_lib.h
        g++ $+ -o $@ -shared -fPIC
clean:
        @echo "begin clean..."
        @rm -rf *.o *.so
        @echo "end clean."
install:
		cp *.so /usr/lib

按esc退出編輯模式,輸入wq保存退出。

make生成*.so文件,make install把*.so文件拷貝到/usr/lib

make
(caffe2_env) zhoujianwen@zhoujianwen-System:~/code/src/testlib$ sudo make install
[sudo] zhoujianwen 的密碼:
g++ test_lib.cpp test_lib.h -o testlib.so -shared -fPIC
cp *.so /usr/lib
(caffe2_env) zhoujianwen@zhoujianwen-System:~/code/src/testlib$ ls /usr/lib | grep testlib
testlib.so

構建依賴關係
在install後面添加testlib.so,這樣在執行make install之前可以確保testlib.so文件已經生成,再把*.so拷貝到/usr/lib目錄下。

testlib.so:test_lib.cpp test_lib.h
        g++ $+ -o $@ -shared -fPIC
clean:
        @echo "begin clean..."
        @rm -rf *.o *.so
        @echo "end clean."
install:testlib.so
        cp *.so /usr/lib

這樣執行一條命令語句就可以了

sudo make install

構建多文件項目
目錄結構
~/code/src/testlib/
test_lib.h
test_lib.cpp

~/code/src/testlib/testmutimake
main.cpp include test_lib.h
person.h
person.cpp include test_lib.h

cd ..
ls
mkdir testmutimake
cd testmutimake
vim main.cpp

main.cpp

#include <iostream>
#include "person.h"
#include "test_lib.h"
using namespace std;
int main(int argc,char *argv[])
{
	TestLib t;
	Person p;
	return 0;
}

person.h

```cpp
#ifndef PERSON_H
#define PERSON_H
class Person
{
pbulic:
		Person();
}
#endif

person.cpp

#include "person.h"
#include "test_lib.h"
#include <iostream>
using namespace std;
Person::Person(){
	TestLib t;
	std::cout<<"Person Create"<<std::endl;
};
vim makefile

自動推導和手動配置,雙結合的做法。

TARGET=main
OBJS = main.o person.o
CPPFLAGES=-I../testlib
LIBS = -ltestlib
$(TARGET):$(OBJS)
        g++ $+ -o $@  $(LIBS)     #這一步是鏈接
 main.o:main.cpp
 		g++ -c $+ $(CPPFLAGS)
 clean:
 		rm -rf $(OBJS) $(TARGET)
g++ $+ -o $@ -I../testlib -ltestlib  #這一步是鏈接

本來-ltestlib後面是要添加-L …/testlib,不用添加是因爲之前已經將testlib.so文件拷貝到/usr/lib目錄下,這樣就會默認搜索/usr/lib目錄

編譯彙編怎麼做呢?是使用gcc -c指令來做自動推導的,自動推導出來就出現一個問題,就是頭文件的路徑沒有添加。第一步我們不要讓它自動推導,而是自己編寫。

 main.o:main.cpp
 		g++ -c $+ -I../testlib
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章