Linux systemV消息隊(duì)列和信號(hào)量詳解
一、消息隊(duì)列
1、實(shí)現(xiàn)原理
操作系統(tǒng)在內(nèi)核建立一個(gè)隊(duì)列,通信的兩個(gè)進(jìn)程AB以數(shù)據(jù)塊的形式將需要發(fā)送的數(shù)據(jù)pushback到隊(duì)列中,數(shù)據(jù)塊是一個(gè)結(jié)構(gòu)體,其中有字段標(biāo)識(shí)該數(shù)據(jù)塊是誰(shuí)發(fā)送的,所以我們只要讓不同的進(jìn)程看到同一個(gè)隊(duì)列就可以了
2、系統(tǒng)調(diào)用接口
(一)創(chuàng)建獲取一個(gè)消息隊(duì)列
msgget
函數(shù)的主要功能是創(chuàng)建一個(gè)新的消息隊(duì)列或者獲取一個(gè)已經(jīng)存在的消息隊(duì)列的標(biāo)識(shí)符
#include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> int msgget(key_t key, int msgflg);
返回值:成功返回一個(gè)msgid
,失敗返回-1
key
:ftok函數(shù)的返回值msgflg
:標(biāo)識(shí)符
函數(shù) | msgflg | 作用 | 示例 |
---|---|---|---|
msgget | IPC_CREAT | 如果指定鍵對(duì)應(yīng)的消息隊(duì)列不存在,則創(chuàng)建一個(gè)新的消息隊(duì)列;若已存在,則直接返回該消息隊(duì)列的標(biāo)識(shí)符 | msgget(key, IPC_CREAT | 0666) |
msgget | IPC_EXCL | 通常與 IPC_CREAT 一起使用,若同時(shí)設(shè)置這兩個(gè)標(biāo)志,當(dāng)消息隊(duì)列已經(jīng)存在時(shí),msgget 調(diào)用會(huì)失敗并返回 -1,errno 會(huì)被設(shè)置為 EEXIST | msgget(key, IPC_CREAT | IPC_EXCL | 0666) |
msgget | 0600 | 消息隊(duì)列的所有者具有讀寫(xiě)權(quán)限,所屬組和其他用戶沒(méi)有任何權(quán)限 | msgget(key, 0600) |
msgget | 0660 | 消息隊(duì)列的所有者和所屬組具有讀寫(xiě)權(quán)限,其他用戶沒(méi)有權(quán)限 | msgget(key, 0660) |
msgget | 0666 | 消息隊(duì)列的所有者、所屬組和其他用戶都具有讀寫(xiě)權(quán)限 | msgget(key, 0666) |
(二)控制消息隊(duì)列
msgctl
用于控制消息隊(duì)列的系統(tǒng)調(diào)用函數(shù),通常用于對(duì)消息隊(duì)列執(zhí)行各種管理操作,如獲取消息隊(duì)列狀態(tài)、設(shè)置消息隊(duì)列屬性以及刪除消息隊(duì)列等
#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
:消息隊(duì)列標(biāo)識(shí)符,msgget
函數(shù)返回值cmd
:msgctl
函數(shù)的cmd
參數(shù)常用命令如下:
命令 | 說(shuō)明 |
---|---|
IPC_STAT | 獲取消息隊(duì)列的狀態(tài)信息,將信息存儲(chǔ)在buf指向的msqid_ds結(jié)構(gòu)中。這些信息包括消息隊(duì)列的權(quán)限、所有者信息、消息隊(duì)列的大小、當(dāng)前消息數(shù)量等 |
IPC_SET | 根據(jù)buf指向的msqid_ds結(jié)構(gòu)中的值,設(shè)置消息隊(duì)列的屬性??梢栽O(shè)置的屬性包括消息隊(duì)列的權(quán)限、隊(duì)列的最大字節(jié)數(shù)等 |
IPC_RMID | 刪除指定的消息隊(duì)列。調(diào)用該命令后,消息隊(duì)列將被立即刪除,所有排隊(duì)的消息都會(huì)被丟棄,并且與該消息隊(duì)列相關(guān)的資源也會(huì)被釋放 |
MSG_INFO | 獲取與消息隊(duì)列相關(guān)的系統(tǒng)資源使用信息,例如當(dāng)前系統(tǒng)中消息隊(duì)列的總數(shù)、系統(tǒng)允許的最大消息隊(duì)列數(shù)等 |
MSG_STAT | 該命令與IPC_STAT類似,但它返回的是一個(gè)指向struct msg_info結(jié)構(gòu)的指針,該結(jié)構(gòu)包含了更多關(guān)于消息隊(duì)列的統(tǒng)計(jì)信息,如發(fā)送和接收消息的字節(jié)數(shù)等 |
buf
:一個(gè)指向msgid_ds
結(jié)構(gòu)體的指針,用于存儲(chǔ)或提供消息隊(duì)列的相關(guān)信息,msqid_ds
結(jié)構(gòu)包含了消息隊(duì)列的各種屬性,如隊(duì)列的權(quán)限、所有者信息、消息隊(duì)列的大小等
(三)發(fā)送消息
msgsnd
用于向消息隊(duì)列發(fā)送消息的系統(tǒng)調(diào)用函數(shù),它允許進(jìn)程將一個(gè)消息添加到指定的消息隊(duì)列中
#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
:消息隊(duì)列標(biāo)識(shí)符,msgget
函數(shù)返回值msgp
:指向要發(fā)送的消息結(jié)構(gòu)體的指針,該結(jié)構(gòu)體的第一個(gè)成員必須是 long 類型,用于指定消息的類型,后續(xù)可以包含消息的數(shù)據(jù)部分msgsz
:消息數(shù)據(jù)部分的長(zhǎng)度,即msgp
所指向結(jié)構(gòu)體中除第一個(gè)long
類型成員之外的數(shù)據(jù)長(zhǎng)度msgflg
:該位置為0就是不設(shè)置
函數(shù) | msgflg | 作用 | 示例 |
---|---|---|---|
msgsnd | IPC_NOWAIT | 非阻塞發(fā)送消息,當(dāng)消息隊(duì)列已滿,無(wú)法立即發(fā)送消息時(shí),如果設(shè)置了該標(biāo)志,msgsnd 函數(shù)會(huì)立即返回 -1,errno 被設(shè)置為 EAGAIN;若未設(shè)置該標(biāo)志,msgsnd 函數(shù)會(huì)阻塞,直到消息隊(duì)列有空間可以發(fā)送消息 | msgsnd(msgid, &msgbuf, sizeof(msgbuf.mtext), IPC_NOWAIT) |
(四)在消息隊(duì)列中獲取數(shù)據(jù)塊
msgrcv
用于從消息隊(duì)列接收消息的系統(tǒng)調(diào)用函數(shù),它允許進(jìn)程從指定的消息隊(duì)列中獲取消息
#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í)際收到的消息數(shù)據(jù)部分的字節(jié)數(shù),不包括最前面的long
前兩個(gè)參數(shù)與前面相同
msgsz
:接收消息時(shí)用于存儲(chǔ)消息數(shù)據(jù)部分的緩沖區(qū)的最大長(zhǎng)度msgtyp
:如果等于0,那該函數(shù)只接收消息隊(duì)列中的第一條消息,如果大于0,接收消息隊(duì)列中消息類型為msgtyp
的第一條消息,如果小于0,接收消息隊(duì)列中消息類型小于等于msgtyp
絕對(duì)值的最小類型的第一條消息msgflg
:該位置為0就是不設(shè)置
函數(shù) | msgflg | 作用 | 示例 |
---|---|---|---|
msgrcv | IPC_NOWAIT | 當(dāng)消息隊(duì)列中沒(méi)有符合要求的消息時(shí),如果設(shè)置了該標(biāo)志,msgrcv 函數(shù)會(huì)立即返回 -1,errno 被設(shè)置為 ENOMSG;若未設(shè)置該標(biāo)志,msgrcv 函數(shù)會(huì)阻塞,直到有符合要求的消息進(jìn)入消息隊(duì)列 | msgrcv(msgid, &msgbuf, sizeof(msgbuf.mtext), msgtype, IPC_NOWAIT) |
msgrcv | MSG_NOERROR | 如果接收到的消息長(zhǎng)度超過(guò)了指定的緩沖區(qū)大小,若設(shè)置了該標(biāo)志,消息會(huì)被截?cái)酁榫彌_區(qū)大小,多余部分會(huì)被丟棄,msgrcv 函數(shù)正常返回;若未設(shè)置該標(biāo)志,msgrcv 函數(shù)會(huì)返回 -1,errno 被設(shè)置為 E2BIG | msgrcv(msgid, &msgbuf, sizeof(msgbuf.mtext), msgtype, MSG_NOERROR) |
二、信號(hào)量
1、原理
信號(hào)量是一種用于實(shí)現(xiàn)進(jìn)程間同步與互斥的機(jī)制,信號(hào)量本質(zhì)上是一個(gè)整數(shù)變量,用于控制對(duì)共享資源的訪問(wèn),它可以看作是一種特殊的計(jì)數(shù)器,其值表示當(dāng)前可用的共享資源數(shù)量,信號(hào)量的值可以被多個(gè)進(jìn)程或線程讀取和修改,通過(guò)對(duì)信號(hào)量的操作,進(jìn)程或線程可以協(xié)調(diào)對(duì)共享資源的訪問(wèn)
信號(hào)量的工作基于兩個(gè)基本操作:P操作(wait操作)和V操作(signal操作)
P操作
:當(dāng)一個(gè)進(jìn)程或線程需要訪問(wèn)共享資源時(shí),它會(huì)執(zhí)行 P 操作。P 操作會(huì)將信號(hào)量的值減 1,如果減 1 后信號(hào)量的值大于等于 0,表示當(dāng)前有可用的資源,進(jìn)程或線程可以繼續(xù)訪問(wèn);如果減 1 后信號(hào)量的值小于 0,表示沒(méi)有可用的資源,進(jìn)程或線程會(huì)被阻塞,直到有其他進(jìn)程或線程釋放資源V 操作
:當(dāng)一個(gè)進(jìn)程或線程使用完共享資源后,它會(huì)執(zhí)行 V 操作,V 操作會(huì)將信號(hào)量的值加 1,如果加 1 后信號(hào)量的值小于等于 0,表示有其他進(jìn)程或線程正在等待該資源,此時(shí)會(huì)喚醒一個(gè)等待的進(jìn)程或線程
2、系統(tǒng)調(diào)用接口
(一)創(chuàng)建獲取一個(gè)信號(hào)量
semget
是用于創(chuàng)建或獲取信號(hào)量集的系統(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);
返回值:成功返回信號(hào)量標(biāo)識(shí)符semid
,失敗返回-1
nsems
:表示要?jiǎng)?chuàng)建或獲取的信號(hào)量集中信號(hào)量的數(shù)量,如果是創(chuàng)建新的信號(hào)量集則必須大于 0,如果是獲取已有的信號(hào)量集則可以為0semflg
:標(biāo)志位,用于指定創(chuàng)建或獲取信號(hào)量集的方式和權(quán)限
(二)控制信號(hào)量
semctl
是用于控制信號(hào)量集的系統(tǒng)調(diào)用函數(shù),它可以對(duì)信號(hào)量集進(jìn)行多種操作,如初始化信號(hào)量的值、獲取信號(hào)量的狀態(tài)、刪除信號(hào)量集等
#include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> int semctl(int semid, int semnum, int cmd, ...);
返回值:取決于cmd
的當(dāng)前值,對(duì)于 GETVAL
命令,返回指定信號(hào)量的當(dāng)前值,對(duì)于 IPC_STAT
、IPC_SET
和 IPC_RMID
等命令,返回 0 表示成功
semid
:信號(hào)量標(biāo)識(shí)符,semget
函數(shù)返回semnum
:信號(hào)量集中信號(hào)量的編號(hào),編號(hào)從 0 開(kāi)始,如果 cmd 操作不需要針對(duì)特定的信號(hào)量(如刪除整個(gè)信號(hào)量集),則可以忽略該參數(shù),通常將其設(shè)為 0cmd
:要執(zhí)行的命令,指定了對(duì)信號(hào)量集或特定信號(hào)量的操作類型
(三)PV操作
semop
用于對(duì)信號(hào)量集執(zhí)行操作的系統(tǒng)調(diào)用函數(shù),它允許進(jìn)程對(duì)一個(gè)或多個(gè)信號(hào)量進(jìn)行原子性的 P和 V操作,從而實(shí)現(xiàn)進(jì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ù)組包含了要對(duì)信號(hào)量集執(zhí)行的操作序列nsops
:sops
數(shù)組中元素的數(shù)量,即要執(zhí)行的操作序列的長(zhǎng)度
三、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; };
描述消息隊(duì)列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; };
描述信號(hào)量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 */ };
他們有一個(gè)同樣的特點(diǎn)就是第一個(gè)參數(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)對(duì)IPC資源進(jìn)行管理
所有的IPC資源都有一個(gè)struct kern_ipc_perm
結(jié)構(gòu),所以操作系統(tǒng)通過(guò)數(shù)組將這些struct kern_ipc_perm
結(jié)構(gòu)組織起來(lái)
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標(biāo)識(shí)符值 unsigned short seq;//是一個(gè)序列號(hào),用于生成唯一的IPC標(biāo)識(shí)符 unsigned short seq_max;//是序列號(hào)的最大值 struct semaphore sem;//這是一個(gè)信號(hào)量,用于對(duì)IPC資源的并發(fā)訪問(wèn)進(jìn)行同步控制 struct ipc_id_ary nullentry;//一個(gè)空的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
的作用就是維護(hù)當(dāng)前操作系統(tǒng)中所有IPC
資源,我們通過(guò)強(qiáng)制類型轉(zhuǎn)換來(lái)通過(guò)這個(gè)數(shù)組里存的struct ipc_id_ary*
找到具體的IPC
對(duì)象,因?yàn)?code>kern_ipc_perm是這三個(gè)結(jié)構(gòu)體中的第一個(gè)成員,我們只要知道了一個(gè)kern_ipc_perm
的地址,就相當(dāng)于知道了某個(gè)具體IPC
對(duì)象的起始地址,然后通過(guò)強(qiáng)制類型轉(zhuǎn)換就可以訪問(wèn)到該IPC
對(duì)象中的所有成員屬性,這樣就實(shí)現(xiàn)了對(duì)一個(gè)具體IPC
對(duì)象的訪問(wèn),如((struct shmid_kernel*)p[0])->q_stime
,在kern_ipc_perm
中有字段來(lái)標(biāo)識(shí)該kern_ipc_perm
是屬于哪種IPC
資源,操作系統(tǒng)就知道要將其強(qiáng)制轉(zhuǎn)化成什么類型了,我們?cè)谟脩魧用嫔鲜褂玫模?code>shmid、msqid
、semid
在內(nèi)核上看就是p
數(shù)組的下標(biāo)
ipc_id_arry
屬于操作系統(tǒng),不屬于任何進(jìn)程,數(shù)組下標(biāo)是線性遞增的,但不會(huì)因?yàn)?code>IPC資源的釋放而改變它的遞增屬性,即當(dāng)前操作系統(tǒng)中最后一個(gè)IPC
資源的下標(biāo)是100,釋放掉這個(gè)IPC
資源,下一次再創(chuàng)建IPC
資源的時(shí)候它的下標(biāo)是101,而不是100,當(dāng)遞增到一定值的時(shí)候,會(huì)回到0
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
CentOS 7 中firewall-cmd命令詳細(xì)介紹
這篇文章主要介紹了 CentOS 7 中firewall-cmd命令詳細(xì)介紹的相關(guān)資料,這里對(duì)CentOS 7的firewall-cmd命令一一介紹,希望能幫助開(kāi)始使用的朋友,需要的朋友可以參考下2016-11-11windows 10 + vwware+centos 6.5虛擬機(jī)系統(tǒng)安裝Tomcat
這篇文章主要介紹了windows 10 + vwware+centos 6.5虛擬機(jī)系統(tǒng)安裝Tomcat的相關(guān)資料,需要的朋友可以參考下2017-01-01基于linux配置selenium環(huán)境并實(shí)現(xiàn)運(yùn)行
這篇文章主要介紹了基于linux配置selenium環(huán)境并實(shí)現(xiàn)運(yùn)行,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-08-08Linux中使用Cron定時(shí)執(zhí)行SQL任務(wù)的實(shí)現(xiàn)步驟
在Linux系統(tǒng)中,計(jì)劃任務(wù)(Cron)是一種強(qiáng)大的工具,可以自動(dòng)執(zhí)行預(yù)定的任務(wù),它非常適合定期運(yùn)行腳本、備份數(shù)據(jù)、清理臨時(shí)文件等一系列重復(fù)性任務(wù),本文給大家介紹了如何在Linux中使用Cron定時(shí)執(zhí)行SQL任務(wù),需要的朋友可以參考下2024-11-11Apache ab并發(fā)負(fù)載壓力測(cè)試實(shí)現(xiàn)方法
Apache的ab命令模擬多線程并發(fā)請(qǐng)求,測(cè)試服務(wù)器負(fù)載壓力,也可以測(cè)試nginx、lighthttp、IIS等其它Web服務(wù)器的壓力2019-09-09Linux zabbix自定義監(jiān)控及報(bào)警實(shí)現(xiàn)過(guò)程解析
這篇文章主要介紹了linux zabbix自定義監(jiān)控及報(bào)警實(shí)現(xiàn)過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-08-08kali?linux?無(wú)法登錄root的問(wèn)題及解決方法
這篇文章主要介紹了kali?linux?無(wú)法登錄root的問(wèn)題及解決方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2025-04-04