天天看點

Linux下工具的基本使用

序言

這個部落格算是其他工具的使用總結吧,裡面包含gdp,動靜态庫的簡單認識...其中gdp是我們今天的額難點,需要我們好好的了解一下.

gcc & g++ 編譯器

這兩個工具比較的簡單,其中gcc是C語言的編譯器的,g++是C++的編譯器,我們這裡用gcc來示範,它們的選項都是一樣的.

程式編譯

我們先來看看如何在Linux下運作一個C語言的代碼.我們看到目前目錄下有一個.c檔案

Linux下工具的基本使用

我們要做的就是編譯這個代碼.

[bit@Qkj 08_07]$ gcc main.c      
Linux下工具的基本使用

現在我們就可以看到了如何編譯一個代碼,可執行程式的檔案名預設是a.out,當然我們也可以修改.

[bit@Qkj 08_07]$ gcc main.c -o mybin      
Linux下工具的基本使用
我們都知道,一個代碼要變成可執行程式要經過以下若幹步驟,我先列出來,在Linux的環境下給大家示範,有些指令看不太懂沒有關系,就看檔案的變化就可以了
  • 預處理
  • 編譯
  • 彙編
  • 連結

預處理(預編譯)

預處理大緻包含四個方面,就是去注釋,宏替換,頭檔案展開和條件編譯.這個過程我們在之前就談過.

#include <stdio.h>      
#define M 100      
int main()      
{      
  // 列印一個宏      
  printf("宏 %d\n",M);      
      
#ifdef A      
  printf("hello A\n");      
#else                                                                                                    
  printf("對不起,你沒有定義 A\n");      
#endif      
  return 0;      
}      

我們希望得到預處理後的結果的,下面的選項就可以.

[bit@Qkj 08_07]$ gcc -E main.c -o main.i      
Linux下工具的基本使用

編譯

編譯器把代碼翻譯成彙編語言

[bit@Qkj 08_07]$ gcc -S main.i -o main.s      
Linux下工具的基本使用
Linux下工具的基本使用

彙編

将彙編檔案翻譯成可重定位二進制檔案,在Windows下是.obj檔案.這時候我們這個檔案還不能運作.

[bit@Qkj 08_07]$ gcc -c main.s -o main.o      
Linux下工具的基本使用
Linux下工具的基本使用

連結

這個才是我想和大家分享的,連結一直是我們比較難了解的.我們可以從這個角度了解,一旦我們調用了一個函數,如果這是别人實作的函數,例如printf函數,編譯器就會問,這個printf的函數在哪裡?我們使用的printf如何和C庫裡面的函數連結出來.

[bit@Qkj 08_07]$ ls /lib64/libc*      
Linux下工具的基本使用

動靜态庫

前面我們說了庫這個詞,這是我第一次正式接觸庫的概念,我們下載下傳編譯器的時候,會自動下載下傳相應的庫,我們之前包含的标準庫的頭檔案,為何編譯器可以知道頭檔案所在的路徑,原因就是編譯器下載下傳的時候,.像庫這些檔案的路徑已經預設确定好了.頭檔案是包含函數聲明,庫是函數的實作.

庫分為動态庫和靜态庫,等會我們感性的認識一下它們兩個的差別.在Linux, .so字尾就是動态庫,Windows下是.dll.靜态庫在Linux是.a,Windows下是.lib.

  • 動态庫 我們隻是包含頭檔案,遇到标準庫的函數,就去庫裡面找 動态連結
  • 靜态庫 編譯器遇到了庫裡面的函數,編譯器自動把這個函數給複制到你的可執行程式中 靜态連結
注意,Linux預設是連接配接動态庫,我們也推薦的是動态庫.如果我們想要靜态庫連接配接,下面也行,加上一個選項就可以了
[bit@Qkj 08_08]$ gcc main.c -o s_a.out -static      
Linux下工具的基本使用
上面的指令可能跑不過,原因就是Linux預設一般都是動态庫,我們需要自己下載下傳靜态庫,記住在root使用者下下載下傳,我們還沒有添加sudo這個東西,一個是C庫,一個是C++的
[root@Qkj ~]# yum install -y glibc-static        
[root@Qkj ~]# yum install -y libstdc++-static      

ldd 指令

ldd可以觀察可執行程式所依賴的庫,

[bit@Qkj 08_08]$ ldd a.out      
Linux下工具的基本使用

gdb 調試器

gcc/g++都是編譯器,gdp才是我們的調試器關于這個調試器,大家知道它的基本的用法就可以了,我們先來用簡單的指令.

debug & release

在Linux下,預設是release模式,這個模式不可以調試,而且編譯器還會做一定程度的優化.gcc 的release 版本是傳遞給使用者使用,debug裡面包含了調試資訊 ,體積上一定大于release模式.

使用下面的選項就可以得到debug模式的版本

Linux下工具的基本使用

至于多的那一部分,就是調試資訊

Linux下工具的基本使用

調試

我們先把代碼給放出來,先稍微的看兩眼.

#include <stdio.h>    
    
int add(int a)    
{    
  int sum = 0;    
  int i = 0;    
  for(i=0;i<a;i++)                                                                                       
  {                                                
    sum += i;                                      
  }                                                
                                                   
  return sum;                                      
}                                                  
                                                   
int main()                                         
{                                                  
  int x = 10;                                      
  int total = add(x);                              
                                                   
  printf("%d\n",total);                            
  return 0;                                        
}      

我們這裡開始,直接開始調試.這個還是比較簡單的,直接隻用這個格式的指令就可以了. gbd debug版本

[bit@Qkj 08_08]$ gdb mybin_g      
Linux下工具的基本使用
如果你不想調試了,可以直接q,退出調試.
Linux下工具的基本使用
顯示代碼,直接 l
Linux下工具的基本使用

它預設不是從第零行顯示,如果我們想要從某一行顯示,可以l後面跟上數字,注意,一般gdp會自動記錄上一個指令,你可以直接按回車執行上一條指令.

Linux下工具的基本使用
打斷點 b 行号,打斷點很重要,我們在第十五行打斷點
Linux下工具的基本使用
檢視斷點,info b
Linux下工具的基本使用
跳到下一個斷點 c
Linux下工具的基本使用
取消斷點 d 斷點編号
Linux下工具的基本使用
程式跑起來,他會停留在第一個遇到的斷點, 直接 r
Linux下工具的基本使用
檢視變量,p
Linux下工具的基本使用
到下一行 n,可以了解成 逐過程,類似F10,也就是遇到函數不進入.
Linux下工具的基本使用
逐語句,s 進入函數 類似F11
Linux下工具的基本使用
常顯示display對于下面我們總不能每次都p sum的值吧,這裡有常顯示的指令
Linux下工具的基本使用
取消常顯示,undisplay,這裡面用的是編号
Linux下工具的基本使用
檢視堆棧 bt
Linux下工具的基本使用
until 10 跳到指定行
Linux下工具的基本使用
finish 跳出目前函數
Linux下工具的基本使用

make和Makefile

我們在window系統下學習C語言,一般會使用VSCode、VS這些軟體,它們有一個統稱叫做內建開發環境 ,在Vs2013中我們很容易寫出幾個 .c 檔案,隻需要編譯一下就可以運作起來,那麼在Linux環境下該怎麼做?這就是需要make和makefile,這裡需要先說明一下,在公司裡面makefile是可以自動生成的,不需要我們手寫,但是這裡需要我們了解原理.

我這裡就先說一下,不做詳細的解釋
  • make 是一條指令
  • Makefile 是一個檔案

環境準備

先建立幾個檔案,裡面的内容就随便編寫了.
[bit@Qkj 08_07]$ touch main.c mytest.c mytest.h      
Linux下工具的基本使用
mytest.h
#ifndef __MYTEST_H__          //#ifndef __MYTEST_H__這個先不用管
   #define __MYTEST_H__  
   #include <stdio.h>
   
   void func();
                                                           
   #endif      
編寫mytest.c
#include "mytest.h"
void func()
{
    prinf("func()\n");
}      
編寫main.c
#include "mytest.h"
int main()
{
  show();                                                                
  return 0;
}      

Makefile/makefile

在談make之前,我們需要有談一下makefile,首先這是一個檔案,我們先建立一個這個檔案.注意Makefile和makefile都可以.

我們在本目錄下建立一個Makefile(或者makefile)檔案
[bit@Qkj 08_07]$ touch Makefile      
Linux下工具的基本使用

那麼這裡就開始需要談談這個檔案裡面的内容了,我們想是不是可以吧自己想要的指令寫道這個檔案裡面,然後通過某種方法可以簡便的做法.那麼在裡面我們可以這麼做.

mybin: main.c mytest.c
  gcc main.c mytest.c -o mybin      
Linux下工具的基本使用
這裡是我們今天的重點,我會好好的解釋的
Linux下工具的基本使用

依賴關系&依賴方法

我們首先要知道一點:一個Makefile檔案存在下面的東西,缺一不可

  • 依賴關系
  • 依賴方法

那麼這倆個究竟是什麼東西,我們先來舉一個例子.假設在學校裡面,你缺生活費了,你給你把打了一個電話,說我是你孩子,是以需要給我打錢.這時候就出現了這兩個東西.親情就是依賴關系,打錢就是依賴方法.

這裡面我們還可以這麼做,我們編譯程式的過程也可以控制出來.

Linux下工具的基本使用

make

make是一個指令,我們就是簡單make一下就可以執行編譯程式的功能.

我們隻需要用一下make指令
Linux下工具的基本使用

清了解決方案

我們在VS裡面看到過清了解決方案這種情況,在Linux中中也就是删除掉我們編譯出來的程式,我們就是下面的做法.

Linux下工具的基本使用

這個地方還算是比較好的,如果我們生成的臨時檔案很多呢?總不能每一次都可以保證自己删除時完全的正确的吧,是以我們也把這個清理的程式放到makefile中.

這裡面還有一點沒有談到的,不過先不要着急,這裡面都會和大家講.

Linux下工具的基本使用
Linux下工具的基本使用

make clean

現在就存在一個問題,為何事make clean,要知道上面我們編譯程式可是一個make就可以了,難道clean有什麼是特殊的?準确來說,是第一個依賴關系和依賴方法比較特殊,誰在第一個,make的就是誰,其餘的前面都是make+目标檔案.

Linux下工具的基本使用
Linux下工具的基本使用

僞目标

在現實生活中,存在一些孤兒院的孩子,它們不存父母,計算機中也存在相似的東西,這中稱之為為目标,所謂僞目标就是沒有依賴檔案的目标檔案.這裡需要注意的,僞目标也是目标,不要把僞軍不當軍隊.

Linux下工具的基本使用

總是可執行

現在還存在最後一個大問題,.PHONY是什麼,它是必須的的嗎?大家可以把**.PHONY**看作makefile裡面的一個關鍵字.

Linux下工具的基本使用

一般情況下,我們用.PHONY修飾僞目标,被它總是修飾的總是被執行,那麼什麼是總是被執行呢?我們先來看看什麼是總是不被執行的.

注意,現在我們的.PHONY隻是修飾的clean目标檔案,我們先看看什麼是總是不被執行的.

Linux下工具的基本使用

我們呢觀察到,我們make了多次,編譯器會告訴我們目前程式已經是最新的了,不需要執行了.這就是總是不被執行的.反之,如果我用.PHONY修飾編譯程式的那部分目标檔案,他就可以總是被執行了.不過我們不建議這麼做,主要後面公司裡面一個程式很大,明明已經是最新了的,為何還要重新編譯,沒必要.

Linux下工具的基本使用
一般我們學到基礎那裡就可以了,要是還要深入的學習,這裡可以稍微了解一下,我們可以使用一些符号來表示目标檔案和依賴檔案
目标檔案 :依賴檔案      
  • 冒号左邊 目标檔案 也就是 $@
  • 冒号左邊 依賴檔案 也就是 $^
要是我們想要簡省依賴方法就可以寫下面的指令
gcc $^ -o $@      
Linux下工具的基本使用
要是我們還想進一步省略,就要寫下面的指令,不過這裡我們就不建議了,畢竟以後的makefile又不是我們來寫的.
Linux下工具的基本使用
解釋一下
  • %.o 對應.c檔案生成的.o檔案
  • %.c 本目錄下所有的.c檔案
  • $< 所有的.c檔案一一展開在gcc下生成對應的.o檔案

stat 指令

這裡面我們需要在看看這個指令,我記得前面好像說過一嘴,沒說也沒有關系,這裡面在說說.statu可以看一下檔案的屬性,談這個關鍵字主要是想看看編譯器是如何知道你的代碼是最新的?這是一個重點.

[bit@Qkj 08_07]$ stat main.c      
Linux下工具的基本使用

在Linux中,檔案的屬性裡面一般包含三個時間,我們都知道,檔案=内容+屬性

  • Access: 進入或讀取檔案的最新時間
  • Modify: 修改檔案内容的最新時間
  • Change: 修改檔案權限的屬性的最新時間

也就是說,我們每執行一次操作,都會導緻這三個時間的變換.例如我們如果修改一下檔案的權限,這樣就會導緻Change時間被修改.

Linux下工具的基本使用
這裡我們需要注意的是,如果我們修改檔案的内容可能也導緻檔案的屬性被修改,主要檔案的大小也是屬性的一部分,這一點要記住了.還有就是Access: 時間,在比較新的Linux核心,我們如果讀取檔案的話可能不會修改Access: 這個時間,主要是這種操作太過頻繁,一般編譯器會在執行了十次左右樣的操作才會修改Access: 時間

現在我們就知道,總是不被執行的原理.在我們進行make的時候,編譯器會先比對它們的Modify:時間,看看源檔案時間是不是大于可執行程式的時間,大于就執行,否就不執行.如果别.PHONY修飾,那麼就會自動忽略這一過程.

Linux下工具的基本使用

進度條

今天我們實作一個進度條的小玩意兒,很簡單,我們先看看成果
Linux下工具的基本使用

緩沖區

在學習計算機時,你在很多地方有可能看到到緩沖區的字樣,那什麼是緩沖區啊?,我們先看一下現象

我是在Linux環境下示範,window環境下示範可能會沒有效果。

#include <stdio.h>    
      
  int main()    
  {    
    printf("你好,緩沖區");    
      
    sleep(3);  //在Linux環境下sleep的機關 秒                                                                                                                                                        
    return 0;    
  }      
Linux下工具的基本使用
你會發現當我們執行程式時,會有大概幾秒的延遲,可我們不是先執行的printf函數嗎,後面才是sleep,實際上這也是事實,隻不過當我執行printf後,我們想要輸出的内容被放到一個緩沖區裡面了,後面程式停留了3秒,當程序快要結束的時候,後面才會重新整理緩沖區.下面我将仔細的說一下什麼緩沖區

本質上 緩沖區就是一塊記憶體,我們将想要輸出的資料放到緩沖區中,當我們重新整理緩沖區的時候會把他列印出來,我們可以把資料當成一個水池,緩沖區看作打水的的木桶,當水桶滿了的時候或者我們就是想要一半的水,就把水倒出來。

什麼情況下緩沖區會重新整理
  • 全緩沖緩沖區滿了,會重新整理
  • 行緩沖我們使用換行符,緩沖區也會重新整理
  • 函數緩沖我們使用某個函數強制重新整理
  • 程式退出,自動重新整理

下面我們一一示範

全緩沖

我們示範一下,大家就懂了
#include <stdio.h>

int main()
{
    while(1)
    {
        printf("hello world");
        sleep(3);
    }
    return 0;
}      
Linux下工具的基本使用

由于時間有些長,我們就不等了,結果是當緩沖區滿了後,螢幕上會出現一螢幕的 hello world

行緩沖

我們這裡的換行是指的 \n,它可以是緩沖區重新整理

#include <stdio.h>

int main()
{
    while(1)
    {
        printf("hello world\n");
        sleep(3);
    }
    return 0;
}      
Linux下工具的基本使用

函數緩沖

在C語言中提供了一個函數,它可以強制重新整理緩沖區,這就是 ​​fflush()​​,它是stdio.h中的庫函數,在一個C程式中,編譯器會預設打開 3 個标準輸入輸出流,分别是下面的,這一點知道就可以了

  1. stdin
  2. stdout
  3. stderr
#include <stdio.h>

int main()
{
    while(1)
    {
        printf("hello world");
        fflush(stdout);
        sleep(3);
    }
    return 0;
}      
Linux下工具的基本使用

\n 和 \r

有很多人都搞不懂這兩個有什麼差別,我們今天重點說一下

  • \n叫做 換行
  • \r叫做 回車

我們舉一個寫文章的例子,當我們在“我叫張三,外号法外狂徒。”這句話再開一行時,看看他們之間的差別

Linux下工具的基本使用

有人可能會感到疑惑,這和我們用的不對啊,我們之前使用下面的代碼時,都是再下一行的開頭直接列印,實際上這是C語言的編譯器預設将換行回車這兩個濃縮到 \n了

printf("hello world\n");
printf("hello world\n");      

示範效果 \r

我們先示範一下效果

#include "ProcBar.h"    
      
  int main()    
  {    
    int count = 10;    
    while(count--)    
    {    
      printf("%d\r",count);    
      sleep(1);                                                                         
    }                                           
    printf("hello word\n");            
                                                
    return 0;                                   
  }      
Linux下工具的基本使用

注意看,我們沒有列印出9,8,7,6,5這樣的資料,原因是緩沖區的因素,我們使用函數重新整理一下緩沖區看看效果

#include "ProcBar.h"    
      
  int main()    
  {    
    int count = 10;    
    while(count--)    
    {    
      printf("%d\r",count);
      fflush(stdout);
      sleep(1);                                                                         
    }                                           
    printf("hello word\n");            
                                                
    return 0;                                   
  }      
Linux下工具的基本使用

進度條實作

有了上面的知識點,我們就可以寫出自己的進度條了,由于多檔案情況下閱讀的體驗不太好,我把代碼寫在一個檔案中
  • usleep() 和 sleep()的作用一樣,不過它的機關是 毫秒
  • memset() 是 string.h庫的一個函數,它可以把數組内部都初始化指定的 資料
  • %-100s 設定100的字段,-表示 左對齊
  • %d%% 是為了了列印 百分比 %是轉換說明,是以用兩個%代替一個% %% -> %詳細的請:point_right:​​轉換說明​​
#ifndef __INCLUDE_H__  //防止頭檔案被重複引用
#define __INCLUDE_H__

#include <stdio.h>
#include <string.h>

#endif

void proBar()
{
  const char* p = "|/-\\";
  int i = 0;
  char arr[101] = { 0 };
  memset(arr, '\0', sizeof(arr));
  while (i <= 100)
  {
    printf("[%-100s][%d%%][%c]\r", arr, i, p[i % 4]);
    fflush(stdout);
    arr[i++] = '#';
    usleep(80000);
  }
  printf("\n");
}

int main()
{
  proBar();
  return 0;
}      

繼續閱讀