linux高级编程有作用吗,Linux 高级编程
8種機械鍵盤軸體對比
本人程序員,要買一個寫代碼的鍵盤,請問紅軸和茶軸怎么選?
版權聲明:本文為 DLonng 原創文章,可以隨意轉載,但必須在明確位置注明出處!
信號量 semaphore
信號量(semaphore)與之前介紹的管道,消息隊列的等 IPC 的思想不同,信號量是一個計數器,用來為多個進程或線程提供對共享數據的訪問。
信號量的原理
常用的信號量是二值信號量,它控制單個共享資源,初始值為 1,操作如下:測試該信號量是否可用
若信號量為 1,則當前進程使用共享資源,并將信號量減 1(加鎖)
若信號量為 0,則當前進程不可以使用共享資源并休眠,必須等待信號量為 1 時進程才能繼續執行(解鎖)
要注意因為是使用信號量來保護共享資源,所以信號量本身的操作不能被打斷,即必須是原子操作,因此由內核來實現信號量。
查看信號量
類似消息隊列和共享內存,我們也可以使用 ipcs 命令來查看當前系統的信號量資源:
ipcs -s
------ Semaphore Arrays --------
key semid owner perms nsems
目前我的系統中沒有信號量,在后面例子中會使用這個命令來查看創建的信號量。
信號量的基本操作
Linux 內核提供了一套對信號量的操作,包括獲取,設置,操作信號量,下面就來學習具體的 API。
1. 獲取信號量
使用 semget 來創建或獲取一個與 key 有關的信號量。
#include
#include
#include
/*
* key:返回的 ID 與 key 有關系
* nsems:信號量的值
* semflg:創建標記
* return:成功返回信號量 ID,失敗返回 -1,并設置 erron
*/
int semget(key_t key, int nsems, int semflg);
關于參數的詳細解釋參考 man semget
2. 操作信號量
使用 semop 可以對一個信號量加 1 或者減 1:
#include
#include
#include
/*
* semid:信號量 ID
* sops:對信號量的操作
* nsops:要操作的信號數量
* return:成功返回 0,失敗返回 -1,并設置 erron
*/
int semop(int semid, struct sembuf *sops, size_t nsops);
sembuf 表示了對信號量操作的屬性:
struct sembuf {
/* 信號量的個數,除非使用多個信號量,否則設置為 0 */
unsigned short sem_num;
/* 信號量的操作,-1 表示 p 操作,1 表示 v 操作 */
short sem_op;
/* 通常設置為 SEM_UNDO,使得 OS 能夠跟蹤信號量并在沒有釋放時自動釋放 */
short sem_flg;
};
在進行信號量的 pv 操作時都是使用這個結構作為參數,詳細解釋參考 man semop。
3. 設置信號量
使用 semctl 可以設置一個信號量的初始值:
#include
#include
#include
/*
* semid:要設置的信號量 ID
* semnum:要設置的信號量的個數
* cmd:設置的屬性
*/
int semctl(int semid, int semnum, int cmd, ...);
第 4 個參數的類型是 union semun 結構:
union semun {
int val; /* Value for SETVAL */
struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
unsigned short *array; /* Array for GETALL, SETALL */
};
在使用信號量時必須手動定義這個結構,并且在初始化設置信號量(SETVAL)時需要使用這個參數,詳細解釋可以參考 man semctl。
例子:使用信號量進行進程間的同步
下面來學習一個實際使用信號量來進行進程間通信的例子,例子實現的功能是:一個程序的兩個實例同步訪問同一段代碼,先來看看使用的關鍵的函數。
1. 獲取信號量
在這個例子中將獲取信號量包裝成一個函數 sem_get:
// 創建或獲取一個信號量
int sem_get(int sem_key) {
int sem_id = semget(sem_key, 1, IPC_CREAT | 0666);
if (sem_id == -1) {
printf("sem get failed.n");
exit(-1);
} else {
printf("sem_id = %dn", sem_id);
return sem_id;
}
}
創建或者獲取成功打印信號量的 id,否則打印錯誤信息。
2. 初始化信號量
我們只初始化一個信號量,并設置 val = 1:
// 初始化信號量
int set_sem(int sem_id) {
union semun sem_union;
sem_union.val = 1;
if(semctl(sem_id, 0, SETVAL, sem_union) == -1) {
fprintf(stderr, "Failed to set semn");
return 0;
}
return 1;
}
主要使用了 union semun 作為第 4 個參數,其中 sem_union.val = 1,并且第 3 個參數必須為 SETVAL。
3. 刪除信號量
雖然可以指定 OS 自動釋放信號量,但這個還是要介紹手動釋放的方法:
// 刪除信號量
void del_sem(int sem_id) {
union semun sem_union;
if(semctl(sem_id, 0, IPC_RMID, sem_union) == -1)
fprintf(stderr, "Failed to delete sem, sem has been del.n");
}
第 3 個參數指定 IPC_RMID 來刪除信號量。
4. 信號量的 PV 操作
下面的函數將信號量的 val 減 1,實現了 PV 操作:
// 減 1,加鎖,P 操作
void sem_down(int sem_id) {
if (-1 == semop(sem_id, &sem_lock, 1))
fprintf(stderr, "semaphore lock failed.n");
}
// 加 1,解鎖,V 操作
void sem_up(int sem_id) {
if (-1 == semop(sem_id, &sem_unlock, 1))
fprintf(stderr, "semaphore unlock failed.n");
}
5. main 函數
最后來看看主程序的邏輯,先創建或獲取信號量,然后在第一次調用時初始化,接著執行 PV 操作,最后在第二次調用后刪除信號量:
int main(int argc, char **argv) {
int sem_id = sem_get(12);
// 第一次調用多加一個參數,第二次調用不加參數,僅在第一次調用時創建信號量
if (argc > 1 && (!set_sem(sem_id))) {
printf("set sem failed.n");
return -1;
}
// P 操作
sem_down(sem_id);
printf("sem lock...n");
printf("do something...n");
sleep(10);
// V 操作
sem_up(sem_id);
printf("sem unlock...n");
// 第二次調用后刪除信號量
if (argc == 1)
del_sem(sem_id);
return 0;
}
6. 編譯,運行,測試
先編譯:
gcc sem.c -o sem
在第一個終端運行,我們多加一個無用的參數來表示這是第一次運行:
./sem 1
sem_id = 0
sem lock...
do something...
# 10 s 等待
sem unlock...
我們使用 ipcs -s 查看一下當前系統中的信號量:
ipcs -s
------ Semaphore Arrays --------
key semid owner perms nsems
0x0000000c 0 orange 666 1
看到用戶 orange 已經成功創建了一個權限為 666 ,ID 為 0 的信號量了,再打開第二個終端,不加額外的參數再運行一次:
./sem
sem_id = 0
# 第一個終端打印完 sem unlock 后
sem lock...
do something...
# 10 s 等待
sem unlock...
因為是第二次運行,所以最后信號量會被刪除,我們再來看看 ipcs -s 的結果:
ipcs -s
------ Semaphore Arrays --------
key semid owner perms nsems
可以看到信號量被成功刪除了,這個效果親自運行測試后可以理解的更加深刻,這兩個進程是同步訪問 do something 這部分代碼的,第二個進程會等待第一個進程 unlock 后再運行,建議你下載代碼實際運行一下。
拓展:信號量在 Linux 內核中的實現機制
最后,我們再來簡單分析下信號量在 Linux 內核中的實現機制,了解機制可以幫助我們更好的理解和使用信號量。其實內核中的共享內存,消息隊列和信號量的實現機制幾乎是相同的,信號量也是開辟一片內存,然后對鏈表進行操作。
1. glibc 信號量函數分析
int semget (key, nsems, semflg)
key_t key;
int nsems;
int semflg;
{
return INLINE_SYSCALL (ipc, 5, IPCOP_semget, key, nsems, semflg, NULL);
}
semget 函數直接使用 INLINE_SYSCALL 進行系統調用陷入內核,semop 和 semctl 也是類似,下面來看看內核中的實現。
2. semget 分析
semget 函數為信號量開辟一片新的內存,內核中的調用如下,也是使用了 ipc_ops 這個數據結構:
其中回調了 newary 這個函數,它完成信號量的創建和獲取:
可以看出,整個過程與消息隊列和共享內存幾乎相同。
3. semop 分析
semop 對信號量進行 PV 操作,其中主要是對 sem_op 進行加 1 或者減 1,大體的過程如下:
4. semctl 分析
semctl 對信號量進行控制,主要是使用 switch 來判斷當前的命令然后執行相應的操作:
要注意的是,主要的處理邏輯在 semctl_main 這個函數中,其中每個 cmd 都有具體的執行邏輯,有興趣可以詳細分析。
結語
本次就簡單地介紹了信號量的基本操作和內核的實現機制,對與信號量的應用并沒有介紹太多,更多的應用方法還需要在實際工作中去實踐。建議你將共享內存,消息隊列和信號量自己總結對照分析一遍,看看它們的實現機制是不是幾乎相同的,這可以加深你對他們的理解,了解些原理總是有些好處的。那我們下次再見,謝謝你的閱讀。推薦關注我的公眾號:「程序小哥」,查看更多精品原創!
總結
以上是生活随笔為你收集整理的linux高级编程有作用吗,Linux 高级编程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: squares怎么读
- 下一篇: 十五分之二十三减括号十五分之五加二十五分