閱讀源碼中的C語言積累 一

關於likely()與unlikely函數

例如,下面是一個條件選擇語句:
if (foo) {
    /* .. */
}
如果想要把這個選擇標記成絕少發生的分支:
/* 我們認爲foo絕大多數時間都會爲0.. */
if (unlikely(foo)) {
    /* .. */
}
相反,如果我們想把一個分支標記爲通常爲真的選擇:
/* 我們認爲foo通常都不會爲0 */
if  (likely(foo)) {
      /* .. */
}

Static inline 函數
內聯函數的代碼會被直接嵌入在它被調用的地方,調用幾次就嵌入幾次,沒有使用call指令。這樣省去了函數調用時的一些額外開銷,比如保存和恢複函數返回地址等,可以加快速度。不過調用次數多的話,會使可執行文件變大,這樣會降低速度。相比起宏來說,內核開發者一般更喜歡使用內聯函數。因爲內聯函數沒有長度限制,格式限制。編譯器還可以檢查函數調用方式,以防止其被誤用。
union的使用
在這裏插入圖片描述
在這裏插入圖片描述
enum枚舉類型:

如果不用枚舉
#define MON 1 
#define TUE 2 
#define WED 3 
#define THU 4 
#define FRI 5 
#define SAT 6
#define SUN 7
代碼量看起來非常多
enum DAY
{
      MON=1, TUE, WED, THU, FRI, SAT, SUN
};

C語言中可變參數的用法
在C語言程序編寫中我們使用最多的函數一定包括printf以及很多類似的變形體。這個函數包含在C庫函數中,定義爲 int printf( const char* format, …);

#include <iostream.h>
  void fun(int a, ...)
  {
    int *temp = &a;
    temp++;  //評論中反饋有問題,待改中
    for (int i = 0; i < a; ++i)
    {
      cout << *temp << endl;
      temp++;
    }
  }
  int main()
  {
    int a = 1;
    int b = 2;
    int c = 3;
    int d = 4;
    fun(4, a, b, c, d);
    system("pause");
    return 0;
  }
  Output::
  1
  2
  3
  4

attribute((unused)) 的含義
有些函數沒有使用或者變量沒有使用,告訴編譯器忽略此警告。

線程取消(pthread_cancel)
 pthread_cancel調用並不等待線程終止,它只提出請求。線程在取消請求(pthread_cancel)發出後會繼續運行,
 
linux 下的進程退出
1) SIGINT關聯ctrl+c
2)SIGINT只能結束前臺進程
2) 1)SIGTERM可以被阻塞、處理和忽略;因此有的進程不能按預期的結束

Linux下sigaction
sigaction函數的功能是檢查或修改與指定信號相關聯的處理動作(可同時兩種操作)
int sigaction(int signum,const struct sigaction *act ,struct sigaction *oldact)
函數說明:sigaction()會依參數signum指定的信號編號來設置該信號的處理函數
函數參數:
signum是指定信號的編號,除SIGKILL和SIGSTOP信號以外
act參數如下:

struct sigaction{
void (*sa_handler) (int);
void  (*sa_sigaction)(int, siginfo_t *, void *);
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer) (void);
}

Strdup拷貝串函數

C開發之----#if、#ifdef、#if defined之間的區別
#if的使用說明
#if的後面接的是表達式
#if (MAX10)||(MAX20) code… #endif
它的作用是:如果(MAX10)||(MAX20)成立,那麼編譯器就會把其中的#if 與 #endif之間的代碼編譯進去(注意:是編譯進去,不是執行!!)
#if defined的使用
#if後面接的是一個宏。
#if defined (x) …code… #endif
這個#if defined它不管裏面的“x”的邏輯是“真”還是“假”它只管這個程序的前面的宏定義裏面有沒有定義“x”這個宏,如果定義了x這個宏,那麼,編譯器會編譯中間的…code…否則不直接忽視中間的…code…代碼。
另外 #if defined(x)也可以取反,也就用 #if !defined(x)
#ifdef的使用
#ifdef的使用和#if defined()的用法一致
#ifndef又和#if !defined()的用法一致。
最後強調兩點:
第一:這幾個宏定義只是決定代碼塊是否被編譯!
第二:別忘了#endif

取消線程:
(1)一個線程可以調用pthread_cancel來取消另一個線程。
(2)被取消的線程需要被join來釋放資源。
(3)被取消的線程的返回值爲PTHREAD_CANCELED

有關線程的取消,一個線程可以爲如下三個狀態: 
     (1)可異步取消:一個線程可以在任何時刻被取消。 
     (2)可同步取消:取消的請求被放在隊列中,直到線程到達某個點,才被取消。
     (3)不可取消:取消的請求被忽略。 
       默認狀態下,線程是可同步取消的。 

 調用pthread_setcanceltype來設定線程取消的方式: 
           pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, NULL); //異步取消、 
           pthread_setcanceltype (PTHREAD_CANCEL_DEFERRED, NULL); //同步取消、 
           pthread_setcanceltype (PTHREAD_CANCEL_DISABLE, NULL);//不能取消 

互斥鎖mutex的使用方法
一,鎖的創建

鎖可以被動態或靜態創建,可以用宏PTHREAD_MUTEX_INITIALIZER來靜態的初始化鎖,採用這種方式比較容易理解,互斥鎖是pthread_mutex_t的結構體,而這個宏是一個結構常量,如下可以完成靜態的初始化鎖:

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

另外鎖可以用pthread_mutex_init函數動態的創建,函數原型如下:

int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t * attr)

二,鎖的屬性
pthread_mutexattr_init初始化鎖的屬性
三,鎖的釋放
調用pthread_mutex_destory之後,可以釋放鎖佔用的資源,但這有一個前提上鎖當前是沒有被鎖的狀態。
四,鎖操作

對鎖的操作主要包括加鎖 pthread_mutex_lock()、解鎖pthread_mutex_unlock()和測試加鎖 pthread_mutex_trylock()三個。

int pthread_mutex_lock(pthread_mutex_t *mutex)

int pthread_mutex_unlock(pthread_mutex_t *mutex)

int pthread_mutex_trylock(pthread_mutex_t *mutex)

pthread_mutex_trylock()語義與pthread_mutex_lock()類似,不同的是在鎖已經被佔據時返回EBUSY而不是掛起等待

五,鎖的使用
見pthread程序

pthread_cond_wait() 用於阻塞當前線程,等待別的線程使用pthread_cond_signal()或pthread_cond_broadcast來喚醒它。 pthread_cond_wait() 必須與pthread_mutex
配套使用。pthread_cond_wait()函數一進入wait狀態就會自動release mutex。當其他線程通過pthread_cond_signal()或pthread_cond_broadcast,把該線程喚醒,使pthread_cond_wait()通過(返回)時,該線程又自動獲得該mutex。
pthread_cond_signal函數的作用是發送一個信號給另外一個正在處於阻塞等待狀態的線程,使其脫離阻塞狀態,繼續執行.如果沒有線程處在阻塞等待狀態,pthread_cond_signal也會成功返回。
使用pthread_cond_signal一般不會有“驚羣現象”產生,他最多隻給一個線程發信號。假如有多個線程正在阻塞等待着這個條件變量的話,那麼是根據各等待線程優先級的高低確定哪個線程接收到信號開始繼續執行。如果各線程優先級相同,則根據等待時間的長短來確定哪個線程獲得信號。但無論如何一個pthread_cond_signal調用最多發信一次。
但是pthread_cond_signal在多處理器上可能同時喚醒多個線程,當你只能讓一個線程處理某個任務時,其它被喚醒的線程就需要繼續 wait,而且規範要求pthread_cond_signal至少喚醒一個pthread_cond_wait上的線程,其實有些實現爲了簡單在單處理器上也會喚醒多個線程.

CPU的緩存架構:
在這裏插入圖片描述
CPU緩存行
緩存是由緩存行組成的,通常是 64 字節(常用處理器的緩存行是 64 字節的,比較舊的處理器緩存行是 32 字節),並且它有效地引用主內存中的一塊地址。
在這裏插入圖片描述
如果訪問一個 long 類型的數組時,當數組中的一個值被加載到緩存中時,另外 7 個元素也會被加載到緩存中。
但是,如果使用的數據結構中的項在內存中不是彼此相鄰的,比如鏈表,那麼將得不到免費緩存加載帶來的好處。
不過,這種免費加載也有一個壞處。設想如果我們有個 long 類型的變量 a,它不是數組的一部分,而是一個單獨的變量,並且還有另外一個 long 類型的變量 b 緊挨着它,那麼當加載 a 的時候將免費加載 b。
看起來似乎沒有什麼毛病,但是如果一個 CPU 核心的線程在對 a 進行修改,另一個 CPU 核心的線程卻在對 b 進行讀取。
當前者修改 a 時,會把 a 和 b 同時加載到前者核心的緩存行中,更新完 a 後其它所有包含 a 的緩存行都將失效,因爲其它緩存中的 a 不是最新值了。
而當後者讀取 b 時,發現這個緩存行已經失效了,需要從主內存中重新加載。
請記住,我們的緩存都是以緩存行作爲一個單位來處理的,所以失效 a 的緩存同時,也會把 b 失效,反之亦然。
在這裏插入圖片描述
這樣就出現了一個問題,b 和 a 完全不相干,每次卻要因爲 a 的更新需要從主內存重新讀取,它被緩存未命中給拖慢了。
這就是傳說中的僞共享。

linux編程之pipe()函數
管道是一種把兩個進程之間的標準輸入和標準輸出連接起來的機制,從而提供一種讓多個進程間通信的方法,當進程創建管道時,每次
都需要提供兩個文件描述符來操作管道。其中一個對管道進行寫操作,另一個對管道進行讀操作。對管道的讀寫與一般的IO系統函數一
致,使用write()函數寫入數據,使用read()讀出數據。
int pipe(int filedes[2]);
返回值:成功,返回0,否則返回-1。參數數組包含pipe使用的兩個文件的描述符。fd[0]:讀管道,fd[1]:寫管道。
必須在fork()中調用pipe(),否則子進程不會繼承文件描述符。兩個進程不共享祖先進程,就不能使用pipe。但是可以使用命名管道。

Write函數
write(STDOUT_FILENO, buf, len);向屏幕輸出一個buf,長度爲len
STDIN_FILENO:接收鍵盤的輸入
STDOUT_FILENO:向屏幕輸出
execlp
從PATH 環境變量中查找文件並執行
execlp(“ls”, “ls”, NULL); //ls輸出結果默認對應屏幕
execlp(“wc”, “wc”, “-l”, NULL); //wc命令默認從標準讀入取數據
定義:
int execlp(const char * file,const char * arg,……);//文件,參數,NULL

發佈了28 篇原創文章 · 獲贊 20 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章