UNIX/LINUX C總結

Unix/linux  C(uc)與標準C語言最大的區別在於,標準C只是一種編程語言,而在此基礎上還依賴於具體的操作系統,脫離了unix/linux系統的uc在其他地方可能不被識別。

(1)Unix/Linux系統的基本概述

用gcc將源文件翻譯爲可執行文件過程:

  預處理:主要宏替換,頭文件替換。

宏替換:#define用一個自定義的量代替一個數,如果要修改這個量的值,只要在最初的定義處修改即可。同時也定義了一些預定義宏:

__FILE__  -主要用於獲取當前文件名%s
     __LINE__  -主要用於獲取當前宏所在的行號 %d
     __DATE__  -主要用於獲取日期信息  %s
     __TIME__  -主要用於獲取時間信息  %s
頭文件替換:#include “a.h”表示先從當前程序所在目錄開始尋找,然後去系統默認目錄,#include <a.h>表示表示從系統默認所在目錄開始尋找。

預處理指令:全部都以#開頭

#pragma GCC dependency 文件名
         =>表示當前文件依賴於指定的文件名,如果指定文件名的最後一次修改時間晚於當前文件,則產生警告信息

#pragma pack(整數n)超過4按4計算
       =>主要用於設置對齊和補齊方式
         #warining 字符串 
  => 表示產生一個警告信息
         #error 字符串
  => 表示產生一個錯誤信息
編譯:檢查語法的錯誤,同時將源文件翻譯爲彙編文件.s

           彙編:將彙編文件轉化爲目標文件.o

鏈接:若編譯時遇到調用其他文件的函數或變量時,會留下一個接口,鏈接部分則是找到這些接口要調用的函數或變量所在位置,將這些目標文件整合到一起,轉化爲可執行文件。

庫文件:

             一般爲了調用者或者使用者的方便,會將完成一個具體功能是所有.o文件打包成一個庫,調用或使用時只需要這個庫文件和其對應的頭文件即可。庫文件分爲兩種:靜態庫和動態共享庫。

              靜態庫:使用這個庫也就是鏈接時,如果要調用其他文件的函數或變量,會直接拷貝過來,所以目標文件會很大,但是運行時可以脫離靜態庫,不需要各種跳轉。

              動態共享庫:使用這個庫也就是鏈接時,如果要調用其他文件的函數或變量,會將要調用的函數或變量所在的地址拷貝過來,所以目標文件小,但是運行時不能脫離這個庫,要各種跳轉。可通過dlopen函數打開/加載共享庫,dlsym函數在共享庫中查找指定的函數地址信息。

函數出錯:

1.對於返回值爲int的函數,返回值是負數則代表出錯,所以如果int型函數要返回一個負數值一般通過一個指針帶出去,而返回值只表示是否出錯,-1爲出錯,0爲正常,返回值是指針則NULL表示出錯,正確則是要返回的地址。

2.#include<error.h> errno是一個int型的全局變量,當程序運行出錯會自動將出錯原因序號值賦給errno。可以通過sterror(errno)輸出對應errno的出錯原因,也可以直接用perror(char *s)打印出&s:最近一次出錯原因

         main函數的原型:
int main(int argc,char* argv[]) 可以外部設定運行這個程序時的命令行參數值和每一個參數的位置
(2)Unix/Linux系統下的內存管理技術

         在對文件的打開,讀,寫這些操作中,標C與uc相比效率更高,因爲標C有標準輸入輸出的緩衝區,對於uc是用文件描述符來進行讀寫,文件描述符是一個整數,它在內核裏對應着一個文件表,存儲了當前文件的各種信息,當close()時是將該整數與文件表關係脫離,如果該文件表不再對應任何一個文件描述符則刪除。

         文件描述符是從3開始,0,1,2分別代表標準輸入輸出和標準錯誤。

         利用fcntl函數可以對該文件描述符複製,獲取狀態,實現文件鎖。
(3)Unix/Linux系統下的進程管理

程序最開始只有一個進程,也就是父進程運行,當遇到fork()函數時會創建一個新的進程叫子進程,同時fork之後的程序會由父進程和子進程同時開始執行,但我們當然希望他們分工幹不同的事情,因此通過fork之後的返回值來判斷是父進程還是子進程,若爲0,則表示當前是子進程,若不爲0則表示當前是父進程,同時返回的值表示子進程的進程號,PID表示進程編號,用getpid函數獲取。

使用fork創建的子進程,會將父進程的內存區域除了代碼區複製一遍,因此子進程的運行是在fork之前的運行結果基礎之上開始的,但是之後子進程和父進程分別有各自的內存區域,除了代碼區會共享。

父進程可利用wait函數來掛起當前進程,直到一個子進程結束並返回子進程結束運行狀態。

Vfork()函數與fork()的區別是創建的子進程會直接佔用父進程的內存區域而不是複製,導致父進程暫時掛起,一般與exec系列的函數搭配使用讓子進程去執行一個別的程序,成爲一個全新的進程,同時解除父進程的阻塞狀態。如果用fork+exec則要複製一遍內存區域,而對於馬上執行其他程序的子進程來說是多此一舉。
(4)Unix/Linux系統下的信號處理

信號本質上來說就是一種軟件中斷,信號可以通過鍵盤(Ctrl+C等等)、程序出錯(段錯誤信號11 SIGSEGV)、特定發送信號的函數(kill(),sleep()等等)來發出,信號是異步的,對於程序而言並不知道什麼時候信號會到來,但一旦收到一個信號會立即進行處理。處理的方式有三種,默認處理(終止程序)、忽略處理、自定義處理(運行特定的函數),可通過signal函數進行設定。也可通過特定函數將所有信號屏蔽,待恢復之後再查看屏蔽期間收到過的信號。
(5)Unix/Linux系統下的進程間的通信

重點:

通信的主要方式

(1)文件

(2)信號

(3)管道

(4)共享內存

(5)消息隊列(重點)

(6)信號量集      

(7)網絡通信(重點)

1.文件

         多個進程可通過訪問同一個文件來進行通信

2.信號

         不同進程間可通過kill()函數等向其他進程發送信號

3.管道

         管道的本質還是文件,分爲有名管道(任意通信)和無名管道(父子進程間通信)

         有名管道:  mkfifo a.pipe(創建管道)

                                     echo hello > a.pipe(數據不能寫進去)

另外一箇中斷中輸入:

                                     cat a.pipe(數據傳遞到這裏)

                                因此管道實際上並不能存儲數據,它只是一個傳遞數據的通道

無名管道:int pipe(intpipefd[2])返回兩個文件描述符,其中pipefd[0]表示是讀端,pipefd[1]表示是寫端。

4.共享內存

                   本質上是內核維護的一塊內存區域,兩個進程可以共同使用這一塊內存區域來進行通信。過程開始與消息隊列類似要生成一個key值,通過key值創建或獲取共享內存地址,用shmat()函數將共享內存地址掛接到本進程地址空間上,使用完畢後要脫接。

5.消息隊列

                   將消息放到消息隊列中後,由其他進程進行讀取,這個消息隊列會一直持續到內核重啓,如何獲取消息隊列是通過key值,雙方通過約定一個文件路徑和ID號,用ftok()來生成一個相同key值,通過該key值就能用msgget()函數獲取到消息隊列號,類似於文件描述符,雙方可通過該消息隊列進行通信。

6.信號量集

                   信號量集本質上是一個計數器,多個進程要同時訪問一個共享資源時,可設定一個信號量集表示當前可訪問共享資源的進程數,每當有一個進程要訪問資源,該信號減1,當一個進程訪問完畢該信號加1,當該信號變爲0時表示訪問該資源的進程數已達最大值,要等待。

7.網絡通信

                   常用的網絡協議:

         TCP:傳輸控制協議,面向連接的協議,類似於打電話

         UDP:用戶數據報協議,非面向連接的協議,類似於發短信

IP:通過IP可以定位到具體的一臺電腦

端口:通過端口號可定位到這臺電腦上的一個具體運行的進程

Socket:參考上篇博客

(6)Unix/Linux系統下的多線程編程

                   線程隸屬於進程,進程是重量級單位,每當創建一個新的進程要新開闢一塊內存,每個進程有獨立的內存區域,而線程是輕量級單位,新創建的線程共享內存資源,但每個線程都有獨立的棧區。多個線程相互獨立又相互影響,當訪問同一個文件時一般採用互斥鎖。

                   

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