Linux systemV消息隊列和信號量詳解
一、消息隊列
1、實現(xiàn)原理
操作系統(tǒng)在內(nèi)核建立一個隊列,通信的兩個進程AB以數(shù)據(jù)塊的形式將需要發(fā)送的數(shù)據(jù)pushback到隊列中,數(shù)據(jù)塊是一個結(jié)構(gòu)體,其中有字段標識該數(shù)據(jù)塊是誰發(fā)送的,所以我們只要讓不同的進程看到同一個隊列就可以了
2、系統(tǒng)調(diào)用接口
(一)創(chuàng)建獲取一個消息隊列
msgget函數(shù)的主要功能是創(chuàng)建一個新的消息隊列或者獲取一個已經(jīng)存在的消息隊列的標識符
#include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> int msgget(key_t key, int msgflg);
返回值:成功返回一個msgid,失敗返回-1
key:ftok函數(shù)的返回值msgflg:標識符
| 函數(shù) | msgflg | 作用 | 示例 |
|---|---|---|---|
| msgget | IPC_CREAT | 如果指定鍵對應(yīng)的消息隊列不存在,則創(chuàng)建一個新的消息隊列;若已存在,則直接返回該消息隊列的標識符 | msgget(key, IPC_CREAT | 0666) |
| msgget | IPC_EXCL | 通常與 IPC_CREAT 一起使用,若同時設(shè)置這兩個標志,當(dāng)消息隊列已經(jīng)存在時,msgget 調(diào)用會失敗并返回 -1,errno 會被設(shè)置為 EEXIST | msgget(key, IPC_CREAT | IPC_EXCL | 0666) |
| msgget | 0600 | 消息隊列的所有者具有讀寫權(quán)限,所屬組和其他用戶沒有任何權(quán)限 | msgget(key, 0600) |
| msgget | 0660 | 消息隊列的所有者和所屬組具有讀寫權(quán)限,其他用戶沒有權(quán)限 | msgget(key, 0660) |
| msgget | 0666 | 消息隊列的所有者、所屬組和其他用戶都具有讀寫權(quán)限 | msgget(key, 0666) |
(二)控制消息隊列
msgctl用于控制消息隊列的系統(tǒng)調(diào)用函數(shù),通常用于對消息隊列執(zhí)行各種管理操作,如獲取消息隊列狀態(tài)、設(shè)置消息隊列屬性以及刪除消息隊列等
#include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> int msgctl(int msgid, int cmd, struct msqid_ds *buf);
返回值:返回0表示操作成功,返回-1表示操作失敗
msgid:消息隊列標識符,msgget函數(shù)返回值cmd:msgctl函數(shù)的cmd參數(shù)常用命令如下:
| 命令 | 說明 |
|---|---|
| IPC_STAT | 獲取消息隊列的狀態(tài)信息,將信息存儲在buf指向的msqid_ds結(jié)構(gòu)中。這些信息包括消息隊列的權(quán)限、所有者信息、消息隊列的大小、當(dāng)前消息數(shù)量等 |
| IPC_SET | 根據(jù)buf指向的msqid_ds結(jié)構(gòu)中的值,設(shè)置消息隊列的屬性??梢栽O(shè)置的屬性包括消息隊列的權(quán)限、隊列的最大字節(jié)數(shù)等 |
| IPC_RMID | 刪除指定的消息隊列。調(diào)用該命令后,消息隊列將被立即刪除,所有排隊的消息都會被丟棄,并且與該消息隊列相關(guān)的資源也會被釋放 |
| MSG_INFO | 獲取與消息隊列相關(guān)的系統(tǒng)資源使用信息,例如當(dāng)前系統(tǒng)中消息隊列的總數(shù)、系統(tǒng)允許的最大消息隊列數(shù)等 |
| MSG_STAT | 該命令與IPC_STAT類似,但它返回的是一個指向struct msg_info結(jié)構(gòu)的指針,該結(jié)構(gòu)包含了更多關(guān)于消息隊列的統(tǒng)計信息,如發(fā)送和接收消息的字節(jié)數(shù)等 |
buf:一個指向msgid_ds結(jié)構(gòu)體的指針,用于存儲或提供消息隊列的相關(guān)信息,msqid_ds結(jié)構(gòu)包含了消息隊列的各種屬性,如隊列的權(quán)限、所有者信息、消息隊列的大小等
(三)發(fā)送消息
msgsnd用于向消息隊列發(fā)送消息的系統(tǒng)調(diào)用函數(shù),它允許進程將一個消息添加到指定的消息隊列中
#include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> int msgsnd(int msgid, const void *msgp, size_t msgsz, int msgflg);
返回值:成功返回0,失敗返回-1
msgid:消息隊列標識符,msgget函數(shù)返回值msgp:指向要發(fā)送的消息結(jié)構(gòu)體的指針,該結(jié)構(gòu)體的第一個成員必須是 long 類型,用于指定消息的類型,后續(xù)可以包含消息的數(shù)據(jù)部分msgsz:消息數(shù)據(jù)部分的長度,即msgp所指向結(jié)構(gòu)體中除第一個long類型成員之外的數(shù)據(jù)長度msgflg:該位置為0就是不設(shè)置
| 函數(shù) | msgflg | 作用 | 示例 |
|---|---|---|---|
| msgsnd | IPC_NOWAIT | 非阻塞發(fā)送消息,當(dāng)消息隊列已滿,無法立即發(fā)送消息時,如果設(shè)置了該標志,msgsnd 函數(shù)會立即返回 -1,errno 被設(shè)置為 EAGAIN;若未設(shè)置該標志,msgsnd 函數(shù)會阻塞,直到消息隊列有空間可以發(fā)送消息 | msgsnd(msgid, &msgbuf, sizeof(msgbuf.mtext), IPC_NOWAIT) |
(四)在消息隊列中獲取數(shù)據(jù)塊
msgrcv用于從消息隊列接收消息的系統(tǒng)調(diào)用函數(shù),它允許進程從指定的消息隊列中獲取消息
#include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> ssize_t msgrcv(int msgid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
返回值:成功返回實際收到的消息數(shù)據(jù)部分的字節(jié)數(shù),不包括最前面的long
前兩個參數(shù)與前面相同
msgsz:接收消息時用于存儲消息數(shù)據(jù)部分的緩沖區(qū)的最大長度msgtyp:如果等于0,那該函數(shù)只接收消息隊列中的第一條消息,如果大于0,接收消息隊列中消息類型為msgtyp的第一條消息,如果小于0,接收消息隊列中消息類型小于等于msgtyp絕對值的最小類型的第一條消息msgflg:該位置為0就是不設(shè)置
| 函數(shù) | msgflg | 作用 | 示例 |
|---|---|---|---|
| msgrcv | IPC_NOWAIT | 當(dāng)消息隊列中沒有符合要求的消息時,如果設(shè)置了該標志,msgrcv 函數(shù)會立即返回 -1,errno 被設(shè)置為 ENOMSG;若未設(shè)置該標志,msgrcv 函數(shù)會阻塞,直到有符合要求的消息進入消息隊列 | msgrcv(msgid, &msgbuf, sizeof(msgbuf.mtext), msgtype, IPC_NOWAIT) |
| msgrcv | MSG_NOERROR | 如果接收到的消息長度超過了指定的緩沖區(qū)大小,若設(shè)置了該標志,消息會被截斷為緩沖區(qū)大小,多余部分會被丟棄,msgrcv 函數(shù)正常返回;若未設(shè)置該標志,msgrcv 函數(shù)會返回 -1,errno 被設(shè)置為 E2BIG | msgrcv(msgid, &msgbuf, sizeof(msgbuf.mtext), msgtype, MSG_NOERROR) |
二、信號量
1、原理
信號量是一種用于實現(xiàn)進程間同步與互斥的機制,信號量本質(zhì)上是一個整數(shù)變量,用于控制對共享資源的訪問,它可以看作是一種特殊的計數(shù)器,其值表示當(dāng)前可用的共享資源數(shù)量,信號量的值可以被多個進程或線程讀取和修改,通過對信號量的操作,進程或線程可以協(xié)調(diào)對共享資源的訪問
信號量的工作基于兩個基本操作:P操作(wait操作)和V操作(signal操作)
P操作:當(dāng)一個進程或線程需要訪問共享資源時,它會執(zhí)行 P 操作。P 操作會將信號量的值減 1,如果減 1 后信號量的值大于等于 0,表示當(dāng)前有可用的資源,進程或線程可以繼續(xù)訪問;如果減 1 后信號量的值小于 0,表示沒有可用的資源,進程或線程會被阻塞,直到有其他進程或線程釋放資源V 操作:當(dāng)一個進程或線程使用完共享資源后,它會執(zhí)行 V 操作,V 操作會將信號量的值加 1,如果加 1 后信號量的值小于等于 0,表示有其他進程或線程正在等待該資源,此時會喚醒一個等待的進程或線程
2、系統(tǒng)調(diào)用接口
(一)創(chuàng)建獲取一個信號量
semget是用于創(chuàng)建或獲取信號量集的系統(tǒng)調(diào)用函數(shù)
#include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> int semget(key_t key, int nsems, int semflg);
返回值:成功返回信號量標識符semid,失敗返回-1
nsems:表示要創(chuàng)建或獲取的信號量集中信號量的數(shù)量,如果是創(chuàng)建新的信號量集則必須大于 0,如果是獲取已有的信號量集則可以為0semflg:標志位,用于指定創(chuàng)建或獲取信號量集的方式和權(quán)限
(二)控制信號量
semctl是用于控制信號量集的系統(tǒng)調(diào)用函數(shù),它可以對信號量集進行多種操作,如初始化信號量的值、獲取信號量的狀態(tài)、刪除信號量集等
#include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> int semctl(int semid, int semnum, int cmd, ...);
返回值:取決于cmd的當(dāng)前值,對于 GETVAL 命令,返回指定信號量的當(dāng)前值,對于 IPC_STAT、IPC_SET 和 IPC_RMID 等命令,返回 0 表示成功
semid:信號量標識符,semget函數(shù)返回semnum:信號量集中信號量的編號,編號從 0 開始,如果 cmd 操作不需要針對特定的信號量(如刪除整個信號量集),則可以忽略該參數(shù),通常將其設(shè)為 0cmd:要執(zhí)行的命令,指定了對信號量集或特定信號量的操作類型
(三)PV操作
semop用于對信號量集執(zhí)行操作的系統(tǒng)調(diào)用函數(shù),它允許進程對一個或多個信號量進行原子性的 P和 V操作,從而實現(xiàn)進程間的同步與互斥
#include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> int semop(int semid, struct sembuf *sops, unsigned nsops);
返回值:成功返回0,失敗返回-1
sops:指向struct sembuf結(jié)構(gòu)體數(shù)組的指針,該數(shù)組包含了要對信號量集執(zhí)行的操作序列nsops:sops數(shù)組中元素的數(shù)量,即要執(zhí)行的操作序列的長度
三、systemV IPC方法的比較
1、描述IPC資源的結(jié)構(gòu)體
描述共享內(nèi)存IPC資源結(jié)構(gòu)體:
struct shmid_kernel /* private to the kernel */
{
struct kern_ipc_perm shm_perm;
struct file * shm_file;
int id;
unsigned long shm_nattch;
unsigned long shm_segsz;
time_t shm_atim;
time_t shm_dtim;
time_t shm_ctim;
pid_t shm_cprid;
pid_t shm_lprid;
struct user_struct *mlock_user;
};
描述消息隊列IPC資源結(jié)構(gòu)體:
struct msg_queue {
struct kern_ipc_perm q_perm;
time_t q_stime; /* last msgsnd time */
time_t q_rtime; /* last msgrcv time */
time_t q_ctime; /* last change time */
unsigned long q_cbytes; /* current number of bytes on queue */
unsigned long q_qnum; /* number of messages in queue */
unsigned long q_qbytes; /* max number of bytes on queue */
pid_t q_lspid; /* pid of last msgsnd */
pid_t q_lrpid; /* last receive pid */
struct list_head q_messages;
struct list_head q_receivers;
struct list_head q_senders;
};
描述信號量IPC資源結(jié)構(gòu)體:
struct sem_array {
struct kern_ipc_perm sem_perm; /* permissions .. see ipc.h */
time_t sem_otime; /* last semop time */
time_t sem_ctime; /* last change time */
struct sem *sem_base; /* ptr to first semaphore in array */
struct sem_queue *sem_pending; /* pending operations to be processed */
struct sem_queue **sem_pending_last; /* last pending operation */
struct sem_undo *undo; /* undo requests on this array */
unsigned long sem_nsems; /* no. of semaphores in array */
};
他們有一個同樣的特點就是第一個參數(shù)都是struct kern_ipc_perm類型的
struct kern_ipc_perm
{
spinlock_t lock;
int deleted;
key_t key;
uid_t uid;
gid_t gid;
uid_t cuid;
gid_t cgid;
mode_t mode;
unsigned long seq;
void *security;
};
2、操作系統(tǒng)對IPC資源進行管理
所有的IPC資源都有一個struct kern_ipc_perm結(jié)構(gòu),所以操作系統(tǒng)通過數(shù)組將這些struct kern_ipc_perm結(jié)構(gòu)組織起來
ipc_ids是 Linux 內(nèi)核中用于管理IPC資源的核心數(shù)據(jù)結(jié)構(gòu)
struct ipc_ids {
int in_use;//記錄當(dāng)前系統(tǒng)中正在使用的IPC資源的數(shù)量
int max_id;//表示系統(tǒng)中允許的最大IPC標識符值
unsigned short seq;//是一個序列號,用于生成唯一的IPC標識符
unsigned short seq_max;//是序列號的最大值
struct semaphore sem;//這是一個信號量,用于對IPC資源的并發(fā)訪問進行同步控制
struct ipc_id_ary nullentry;//一個空的ipc_id_ary結(jié)構(gòu)
struct ipc_id_ary* entries;//指向ipc_id_ary結(jié)構(gòu)體的指針
};struct ipc_id_ary {
int size;
struct kern_ipc_perm *p[0];
};這里的柔性數(shù)組p的作用就是維護當(dāng)前操作系統(tǒng)中所有IPC資源,我們通過強制類型轉(zhuǎn)換來通過這個數(shù)組里存的struct ipc_id_ary*找到具體的IPC對象,因為kern_ipc_perm是這三個結(jié)構(gòu)體中的第一個成員,我們只要知道了一個kern_ipc_perm的地址,就相當(dāng)于知道了某個具體IPC對象的起始地址,然后通過強制類型轉(zhuǎn)換就可以訪問到該IPC對象中的所有成員屬性,這樣就實現(xiàn)了對一個具體IPC對象的訪問,如((struct shmid_kernel*)p[0])->q_stime,在kern_ipc_perm中有字段來標識該kern_ipc_perm是屬于哪種IPC資源,操作系統(tǒng)就知道要將其強制轉(zhuǎn)化成什么類型了,我們在用戶層面上使用的:shmid、msqid、semid在內(nèi)核上看就是p數(shù)組的下標
ipc_id_arry屬于操作系統(tǒng),不屬于任何進程,數(shù)組下標是線性遞增的,但不會因為IPC資源的釋放而改變它的遞增屬性,即當(dāng)前操作系統(tǒng)中最后一個IPC資源的下標是100,釋放掉這個IPC資源,下一次再創(chuàng)建IPC資源的時候它的下標是101,而不是100,當(dāng)遞增到一定值的時候,會回到0
總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
windows 10 + vwware+centos 6.5虛擬機系統(tǒng)安裝Tomcat
這篇文章主要介紹了windows 10 + vwware+centos 6.5虛擬機系統(tǒng)安裝Tomcat的相關(guān)資料,需要的朋友可以參考下2017-01-01
基于linux配置selenium環(huán)境并實現(xiàn)運行
這篇文章主要介紹了基于linux配置selenium環(huán)境并實現(xiàn)運行,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-08-08
Linux中使用Cron定時執(zhí)行SQL任務(wù)的實現(xiàn)步驟
在Linux系統(tǒng)中,計劃任務(wù)(Cron)是一種強大的工具,可以自動執(zhí)行預(yù)定的任務(wù),它非常適合定期運行腳本、備份數(shù)據(jù)、清理臨時文件等一系列重復(fù)性任務(wù),本文給大家介紹了如何在Linux中使用Cron定時執(zhí)行SQL任務(wù),需要的朋友可以參考下2024-11-11
Apache ab并發(fā)負載壓力測試實現(xiàn)方法
Apache的ab命令模擬多線程并發(fā)請求,測試服務(wù)器負載壓力,也可以測試nginx、lighthttp、IIS等其它Web服務(wù)器的壓力2019-09-09
Linux zabbix自定義監(jiān)控及報警實現(xiàn)過程解析
這篇文章主要介紹了linux zabbix自定義監(jiān)控及報警實現(xiàn)過程解析,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-08-08

