Linux 核心 IPC 通訊原始碼分析 訊息佇列

2021-09-02 00:25:51 字數 4226 閱讀 5871

本文對最新的 linux-4.19.4 核心原始碼進行分析,並詳細指出核心 ipc 機制中的訊息佇列的原理。

ipc(程序間通訊,interprocess communication)是核心提供的系統中程序間進行通訊的一種機制。系統中每個程序的使用者位址空間互不干擾,所以需要核心來提供程序之間進行通訊機制。

程序間通訊的七種方式:

訊息佇列是訊息的鏈結表,包括 posix 訊息佇列和 system v 訊息佇列。訊息佇列克服了訊號承載資訊量少、管道只能承載無格式位元組流以及緩衝區大小受限等缺點,克服了早期 lunix 通訊機制的一些缺點。訊息佇列將訊息看作乙個記錄,具有特定的格式以及特定的優先順序,對訊息佇列有寫許可權的程序可以向中按照一定的規則新增新訊息;對訊息佇列有讀許可權的程序則可以從訊息佇列中讀取訊息,訊息佇列是隨核心持續的。

總體結構見圖:

上圖描述乙個訊息佇列的資料結構。

訊息佇列的 q_messages 字段指向乙個訊息的鍊錶,這裡面存放著等待讀取的訊息。每個 msg_msg 結構占有乙個頁(若訊息小於乙個則只占用訊息大小 + msg_msg 資料結構大小),頁頭部為 msg_msg 資料結構,剩餘為資料區。若訊息超過乙個頁,剩餘訊息會存放在頁頭為 msg_msgseg 資料結構的頁中。因此對於 n 個頁,設頁大小的 m,struct msg_msg 結構大小為 a,msg_msgseg 資料結構的大小為 b,n 個頁最大能用於儲存資料的空間為:n*m - a – b *(n-1),每頁最大值為 2^13 (8192)。

指向乙個 sleeping 的接收者鍊錶,這個鍊錶上每個結構都指向乙個等待接受的程序(阻塞),等待訊息佇列被寫入它需要的資訊。

指向乙個 sleeping 傳送者鍊錶,這個鍊錶上每個結構都指向乙個等待傳送訊息的程序(阻塞),等待訊息佇列可以讓它寫入的資訊。

每個 msg_msg **程序放入到佇列中乙個資料。

/* one msg_msg structure for each message */

struct msg_msg ;

用於儲存 msg_msg 中溢位的資料。

struct msg_msgseg ;
訊息佇列中睡眠的接受者程序資料結構。

/* one msg_receiver structure for each sleeping receiver */

struct msg_receiver ;

訊息佇列中睡眠的傳送者程序資料結構。

/* one msg_sender for each sleeping sender */

struct msg_sender ;

早前 64 位的 linux 有乙個名為 cve-2009-2009 的漏洞,所以新版本的 linux 在呼叫這些函式的時候外加了一層封裝 syscall_definex。例如雖然在程式上層可以直接呼叫 msgsnd(msqid,&msgs,sizeof(struct msgstru),ipc_nowait) 這樣的形式來傳送訊息,但是在底層是用以下的形式來呼叫 :

syscall_define4(msgsnd, int, msqid, struct msgbuf __user *, msgp, size_t, msgsz,int,msg***)
對於 syscall_define4,首個變數用於函式名,剩下的偶數對引數,依次代表引數型別與引數變數。syscall_definex,隨後的 x 就是對於不同的引數的個數。

#ifndef syscall_define0

#define syscall_define0(sname) \

syscall_metadata(_##sname, 0); \

asmlinkage long sys_##sname(void); \

allow_error_injection(sys_##sname, errno); \

asmlinkage long sys_##sname(void)

#endif /* syscall_define0 */

#define syscall_define1(name, ...) syscall_definex(1, _##name, __va_args__)

#define syscall_define2(name, ...) syscall_definex(2, _##name, __va_args__)

#define syscall_define3(name, ...) syscall_definex(3, _##name, __va_args__)

#define syscall_define4(name, ...) syscall_definex(4, _##name, __va_args__)

#define syscall_define5(name, ...) syscall_definex(5, _##name, __va_args__)

#define syscall_define6(name, ...) syscall_definex(6, _##name, __va_args__)

#define syscall_define_maxargs 6

#define syscall_definex(x, sname, ...) \

syscall_metadata(sname, x, __va_args__) \

__syscall_definex(x, sname, __va_args__)

#define __protect(...) asmlinkage_protect(__va_args__)

/* * the asmlinkage stub is aliased to a function named __se_sys_*() which

* sign-extends 32-bit ints to longs whenever needed. the actual work is

* done within __do_sys_*().

*/#ifndef __syscall_definex

#define __syscall_definex(x, name, ...) \

__diag_push(); \

__diag_ignore(gcc, 8, "-wattribute-alias", \

"type aliasing is used to sanitize syscall arguments");\

asmlinkage long sys##name(__map(x,__sc_decl,__va_args__)) \

__attribute__((alias(__stringify(__se_sys##name)))); \

allow_error_injection(sys##name, errno); \

static inline long __do_sys##name(__map(x,__sc_decl,__va_args__));\

asmlinkage long __se_sys##name(__map(x,__sc_long,__va_args__)); \

asmlinkage long __se_sys##name(__map(x,__sc_long,__va_args__)) \

\__diag_pop(); \

static inline long __do_sys##name(__map(x,__sc_decl,__va_args__))

#endif /* __syscall_definex */

syscall_definex 的定義和它具體呼叫的方法中 ## 是連線符,直接將引數的原來的字元替換為 ## 後的佔位符。va_args代表前面 … 裡面的可變引數最後呼叫了__do_sys##name 的方法,在後面加上大括號就是乙個函式的具體定義了。

該函式用於獲得乙個訊息佇列,核心從指定 ipc_namespace 對應的 ipc_ids 獲取相應鍵值的訊息佇列,如果沒有的話就會新建乙個佇列。

該函式用於向指定訊息佇列中傳送資訊,該函式會將訊息插入到鍊錶尾部。函式還會將資訊傳送到佇列關聯的 sleep 接收者

該函式用於從指定訊息佇列中接收資訊,如果佇列中沒有訊息,就會將程序加入到休眠程序列表中,等待佇列中有新的訊息加入。

linux socket通訊原始碼

初學socket通訊,參考的是linuxc程式設計大全的23章的23 5例子,但是發現這個例子原始碼裡有好幾處錯誤,因為初學,很多不懂,吃了虧,因此將修改後能正常執行的 記錄在這裡 參考 server.c include include include include include include...

Handler通訊 原始碼分析

1.messagequeue 訊息佇列 執行緒中更新 ui 的時候經常是呼叫 sendmessage 和 sendmessagedelayed 這樣 我跟蹤 進入到 handler 的 sendmessage 方法 public final boolean sendmessage message m...

ucos II 任務間通訊原始碼分析

ucos ii 2.0版本的任務間通訊提供訊息郵箱和訊息佇列兩種機制,都基於核心的事件控制塊機制實現。訊息郵箱 訊息郵箱主要函式分析 訊息佇列 訊息佇列全域性變數 typedef struct os q os q typedef struct os q data os ext os q osqfre...