新聞中心
在Linux系統(tǒng)中,進(jìn)程間的通信是非常常見的場景。其中,信號量是一種常用的進(jìn)程間通信機(jī)制,它可以用來實(shí)現(xiàn)進(jìn)程的同步和互斥。在多進(jìn)程或多線程的環(huán)境中,控制進(jìn)程之間的訪問同步和互斥是非常重要的,而信號量正是解決這一問題的有效機(jī)制。

本文將介紹Linux中進(jìn)程間共享信號量的實(shí)現(xiàn)方法,包括信號量的定義、初始化、操作等,希望能夠幫助讀者深入理解信號量的知識,更好地應(yīng)用它們進(jìn)行進(jìn)程間通信。
一、信號量的定義
信號量是一種用于同步進(jìn)程和線程的機(jī)制,它主要用于控制進(jìn)程的互斥和同步。在Linux系統(tǒng)中,信號量是由一個整形變量和一組信號量操作組成的。信號量變量的值表示當(dāng)前資源的可用數(shù)目。當(dāng)信號量的值為正數(shù)時,表示還有可用資源;當(dāng)信號量的值為0時,表示所有資源都被占用;當(dāng)信號量的值為負(fù)數(shù)時,表示有進(jìn)程正在等待釋放資源。
在Linux系統(tǒng)中,信號量的定義如下:
“`
struct sembuf{
unsigned short sem_num; // 信號量編號
short sem_op; // 信號量操作
short sem_; // 操作標(biāo)志
};
union semun{
int val; // 信號量初始值
struct semid_ds *buf; // IPC_STAT、IPC_SET 操作用緩存區(qū)
unsigned short *array; // SETALL、GETALL 操作用數(shù)組
};
int semget(key_t key, int nsems, int sem); // 創(chuàng)建或打開一個信號量
int semctl(int semid, int semnum, int cmd, union semun arg); // 對一個信號量或信號量執(zhí)行控制操作
int semop(int semid, struct sembuf *sops, unsign int nsops); // 執(zhí)行信號量操作
“`
二、信號量的初始化
在使用信號量進(jìn)行進(jìn)程間通信時,需要先創(chuàng)建或打開一個信號量,并進(jìn)行初始化。在Linux中,信號量的初始化可以使用 semget() 函數(shù)和 semctl() 函數(shù)進(jìn)行。
1.使用 semget() 函數(shù)創(chuàng)建信號量
(1)semget() 函數(shù)原型
“`
#include
#include
#include
int semget(key_t key, int nsems, int sem);
“`
(2)參數(shù)說明
key:用于標(biāo)識信號集的key值。
nsems:創(chuàng)建的信號集的數(shù)量。
sem:信號集的訪問權(quán)限。
(3)函數(shù)返回值
成功:返回信號量集的標(biāo)識符。
失?。悍祷?-1 并設(shè)置 errno 來指示錯誤的類型。
(4)示例代碼
“`
#include
#include
#include
#include
int mn()
{
int semid;
key_t key = 1234;
semid = semget(key, 1, 0666 | IPC_CREAT);
if (semid == -1) {
perror(“semget”);
return -1;
}
printf(“succeed to create semaphore: %d\n”, semid);
return 0;
}
“`
上面的示例代碼使用 key 為 1234 創(chuàng)建了一個信號量,該包含了一個信號量。sem 的值被設(shè)置為 0666 | IPC_CREAT 來表示新創(chuàng)建的信號量,如果已經(jīng)存在該信號量,則直接打開該信號量。
2. 使用 semctl() 函數(shù)初始化信號量
使用 semctl() 函數(shù)初始化信號量時,可以設(shè)置信號量的值、操作等。semctl() 函數(shù)使用如下:
(1)semctl() 函數(shù)原型
“`
#include
#include
#include
int semctl(int semid, int semnum, int cmd, union semun arg);
“`
(2)參數(shù)說明
semid:信號量集的標(biāo)識符。
semnum:對信號量中的第幾個信號量進(jìn)行操作。
cmd:操作類型,包括如下:
– SETVAL:設(shè)置信號量集中的一個信號量的值。
– IPC_RMID:從內(nèi)核中刪除信號量。
arg:參數(shù)類型 union semun,可以是如下情況:
– val:val 成員用于 SETVAL 操作設(shè)置一個信號量或數(shù)組的值。
– buf:buf 成員用于 IPC_STAT 和 IPC_SET 操作,獲取或修改信號量的狀態(tài)信息。
– array:array 成員用于 SETALL 和 GETALL 操作,設(shè)置或獲取整個信號量數(shù)組的值。
(3)函數(shù)返回值
成功:返回信號量集的標(biāo)識符。
失?。悍祷?-1 并設(shè)置 errno 來指示錯誤的類型。
(4)示例代碼
“`
#include
#include
#include
#include
int mn()
{
int semid;
key_t key = 1234;
union semun arg;
struct sembuf sembuf;
int val = 1;
semid = semget(key, 1, 0666 | IPC_CREAT);
if (semid == -1) {
perror(“semget”);
return -1;
}
arg.val = val;
if (semctl(semid, 0, SETVAL, arg) == -1) {
perror(“semctl”);
return -1;
}
printf(“succeed to initialize semaphore: %d\n”, semid);
return 0;
}
“`
上面的示例代碼創(chuàng)建一個 key 值為 1234 的信號量,包含了一個信號量。使用 SETVAL 操作設(shè)置該信號量的值為 1。如果初始化成功,則返回該信號量的標(biāo)識符 semid。
三、信號量的操作
在信號量的中,可以對單個信號量進(jìn)行操作,也可以對信號量中的所有信號量進(jìn)行操作。信號量的操作主要包括加操作和減操作。在 Linux 平臺下,可以使用 semop() 函數(shù)完成對信號量的操作。
在使用 semop() 函數(shù)對信號量進(jìn)行操作時,需要傳入一個結(jié)構(gòu)體 sembuf 作為信號量操作的參數(shù),或者傳入多個 sembuf,以便一次執(zhí)行多個信號量操作。
(1)sembuf 結(jié)構(gòu)體的定義
“`
struct sembuf {
unsigned short sem_num; // 信號量中的信號量編號
short sem_op; // 信號量操作
short sem_; // 操作標(biāo)志
};
“`
sem_num:信號量中的信號量編號。
sem_op:信號量的操作,其中,信號量的操作可以為如下值之一:
– 正整數(shù):表示向信號量中增加信號量值;
– 負(fù)整數(shù):表示從信號量中減去信號量值;
– 0:表示讀取信號量中的值。
sem_:操作標(biāo)志,可用于 IPC_NOWT 等標(biāo)志的設(shè)置。
(2)semop() 函數(shù)原型
“`
#include
#include
#include
int semop(int semid, struct sembuf *sops, unsigned nsops);
“`
(3)參數(shù)說明
semid:信號量的標(biāo)識符。
sops:指向 sembuf 結(jié)構(gòu)數(shù)組的指針,其中每一個 sembuf 結(jié)構(gòu)體描述了一個信號量的操作。
nsops:sops 指向的 sembuf 結(jié)構(gòu)體的數(shù)量。
(4)函數(shù)返回值
成功:返回 0。
失?。悍祷?-1 并設(shè)置 errno 來指示錯誤的類型,如 EAGN 表示操作被阻塞,EINVAL 不能操作等等。
(5)信號量操作的示例代碼
下面的示例代碼展示了如何在兩個進(jìn)程中對共享的信號量進(jìn)行操作。兩個進(jìn)程的實(shí)現(xiàn)方式分別是獲取和釋放共享資源。
之一個進(jìn)程:
“`
#include
#include
#include
#include
#include
#include
const char *data = “hello, world”;
int p(int semid, int semnum)
{
struct sembuf sem_buf;
sem_buf.sem_num = semnum;
sem_buf.sem_op = -1; // 清空信號量
sem_buf.sem_ = SEM_UNDO; // 系統(tǒng)內(nèi)核在進(jìn)程退出時恢復(fù)信號量
return semop(semid, &sem_buf, 1);
}
int v(int semid, int semnum)
{
struct sembuf sem_buf;;
sem_buf.sem_num = semnum;
sem_buf.sem_op = 1; // 加 1
sem_buf.sem_ = SEM_UNDO; // 系統(tǒng)內(nèi)核在進(jìn)程退出時恢復(fù)信號量
return semop(semid, &sem_buf, 1);
}
int mn()
{
int semid;
key_t key = 1234;
pid_t pid;
union semun arg;
struct sembuf sembuf;
int val = 1;
semid = semget(key, 1, 0666 | IPC_CREAT);
if (semid == -1) {
perror(“semget”);
return -1;
}
arg.val = val;
if (semctl(semid, 0, SETVAL, arg) == -1) {
perror(“semctl”);
return -1;
}
pid = fork();
if (pid == -1) {
perror(“fork”);
return -1;
} else if (pid == 0) {
// 子進(jìn)程
p(semid, 0);
printf(“child process get the semaphore, write data\n”);
write(STDOUT_FILENO, data, strlen(data));
sleep(1);
v(semid, 0);
printf(“child process release the semaphore\n”);
} else {
// 父進(jìn)程
struct sembuf sem_buf;
sem_buf.sem_num = 0;
sem_buf.sem_op = -1; // 清空信號量
sem_buf.sem_ = SEM_UNDO; // 系統(tǒng)內(nèi)核在進(jìn)程退出時恢復(fù)信號量
semop(semid, &sem_buf, 1);
printf(“parent process get the semaphore, read data\n”);
read(STDIN_FILENO, data, strlen(data));
v(semid, 0);
printf(“parent process release the semaphore\n”);
}
semctl(semid, 0, IPC_RMID, arg);
return 0;
}
“`
上面的代碼中,fork() 函數(shù)創(chuàng)建一個子進(jìn)程,在子進(jìn)程中利用信號量實(shí)現(xiàn)獲取和釋放共享資源的操作。在父進(jìn)程中也是利用信號量控制讀取和釋放共享資源的操作。
四、
相關(guān)問題拓展閱讀:
- linux進(jìn)程間通訊的幾種方式
- LINUX下系統(tǒng)編程,多進(jìn)程間數(shù)據(jù)共享,即共享變量
linux進(jìn)程間通訊的幾種方式
一。管道(pipe)
管道是Linux支持的最初IPC方式,管道可分為無名管道,有名管道等。
(一)無名管道,它具有幾個特點(diǎn):
1) 管道是半雙工的,只能支持?jǐn)?shù)據(jù)的單向流動;兩進(jìn)程間需要通信時需要建立起兩個管道;
2) 無名管道使用pipe()函數(shù)創(chuàng)建,只能用于父子進(jìn)程或者兄弟進(jìn)程之間;
3) 管道對于通信的兩端進(jìn)程而言,實(shí)質(zhì)上是一種獨(dú)立的文件,只存在于內(nèi)存中;
4) 數(shù)據(jù)的讀寫操作:一個進(jìn)程向管道中寫數(shù)據(jù),所寫的數(shù)據(jù)添加在管道緩沖區(qū)的尾部;另一個進(jìn)程在管道中緩沖區(qū)的頭部讀數(shù)據(jù)。
?。ǘ┯忻艿?/p>
有名管道也是半雙工的,不過它允許沒有親緣關(guān)系的進(jìn)程間進(jìn)行通信。具體點(diǎn)說就是,有名管道提供了一個路徑名與之進(jìn)行關(guān)聯(lián),以FIFO(先進(jìn)先出)的形式存在于文件系統(tǒng)中。這樣即使是不相干的進(jìn)程也可以通過FIFO相互通信,只要他們能訪問已經(jīng)提供的路徑。
值得注意的是,只有在管道有讀端時,往管道中寫數(shù)據(jù)才有意義。否則,向管道寫數(shù)據(jù)的進(jìn)程會接收到內(nèi)核發(fā)出來的SIGPIPE信號;應(yīng)用程序可以自定義該信號處理函數(shù),或者直接忽略該信號。
二。信號量(semophore)
信號量是一種計數(shù)器,可以控制進(jìn)程間多個線程或者多個進(jìn)程對資源的同步訪問,它常實(shí)現(xiàn)為一種鎖機(jī)制。實(shí)質(zhì)上,信號量是一個被保護(hù)的變量,并且只能通過初始化和兩個標(biāo)準(zhǔn)的原子操作(P/V)來訪問。(P,V操作也常稱為wait(s),signal(s))
三。信號(Signal)
信號是Unix系統(tǒng)中使用的最古老的進(jìn)程間通信的方法之一。操作系統(tǒng)通過信號來通知某一進(jìn)程發(fā)生了某一種預(yù)定好的事件;接收到信號的進(jìn)程可以選擇不同的方式處理該信號,一是可以采用默認(rèn)處理機(jī)制—進(jìn)程中斷或退出,一是忽略該信號,還有就是自定義該信號的處理函數(shù),執(zhí)行相應(yīng)的動作。
內(nèi)核為進(jìn)程生產(chǎn)信號,來響應(yīng)不同的事件,這些事件就是信號源。信號源可以是:異常,其他進(jìn)程,終端的中斷(Ctrl-C,Ctrl+\等),作業(yè)的控制(前臺,后臺進(jìn)程的管理等),分配額問題(cpu超時或文件過大等),內(nèi)核通知(例如I/O就緒等),報警(計時器)。
四。消息隊列(Message Queue)
消息隊列就是消息的一個鏈表,它允許一個或者多個進(jìn)程向它寫消息,一個或多個進(jìn)程向它讀消息。Linux維護(hù)了一個消息隊列向量表:msgque,來表示系統(tǒng)中所有的消息隊列。
消息隊列克服了信號傳遞信息少,管道只能支持無格式字節(jié)流和緩沖區(qū)受限的缺點(diǎn)。
五。共享內(nèi)存(shared memory)
共享內(nèi)存映射為一段可以被其他進(jìn)程訪問的內(nèi)存。該共享內(nèi)存由一個進(jìn)程所創(chuàng)建,然后其他進(jìn)程可以掛載到該共享內(nèi)存中。共享內(nèi)存是最快的IPC機(jī)制,但由于linux本身不能實(shí)現(xiàn)對其同步控制,需要用戶程序進(jìn)行并發(fā)訪問控制,因此它一般結(jié)合了其他通信機(jī)制實(shí)現(xiàn)了進(jìn)程間的通信,例如信號量。
socket也是一種進(jìn)程間的通信機(jī)制,不過它與其他通信方式主要的區(qū)別是:它可以實(shí)現(xiàn)不同主機(jī)間的進(jìn)程通信。
進(jìn)程間通信(IPC,Interprocess communication)是一組編程接口,讓程序員能夠協(xié)調(diào)不同的進(jìn)程,使之能在一個操作系統(tǒng)里同時運(yùn)行,并相互傳遞、交換信息。這使得一個程序能夠在同一時間里處理許多用戶的要求。因為即使只有一個用戶發(fā)出要求,也可能導(dǎo)致一個操作系統(tǒng)中多個進(jìn)程的運(yùn)行,進(jìn)程之間必須互相通話。IPC接口就提供了這種可能性。每個IPC方法均有它自己的優(yōu)點(diǎn)和局限性,一般,對于單個程序而言使用所有的IPC方法是不常見的。
1、無名管道通信
無名管道(pipe):管道是一種半雙工的通信方式,數(shù)據(jù)只能單向流動,而且只能在具有親緣關(guān)系的進(jìn)程間使用,進(jìn)程的親緣關(guān)系通常是指父子進(jìn)程關(guān)系。
2、高級管道通信
高級管道(popen):將另一個程序當(dāng)做一個新的進(jìn)程在當(dāng)前程序進(jìn)程中啟動,則它算是當(dāng)前程序的子進(jìn)程,這種方式我們稱為高級管道方式。
3、有名管道通信
有名管道(named pipe):有名管道也是半雙工的通信方式,但是它允許無親緣關(guān)系進(jìn)程間的通信。
4、消息隊列通信
消息隊列(message
queue):消息隊列是由消息的鏈表,存放在內(nèi)核中并由消息隊列標(biāo)識符標(biāo)識,消息隊列克服了信號傳遞信息少、管道只能承載無格式字節(jié)流以及緩沖區(qū)大小受限等缺點(diǎn)。
5、信號量通信
信號量(semophore):信號量是一個計數(shù)器,可以用來控制多個進(jìn)程對共享資源的訪問,它常作為一種鎖機(jī)制,防止某進(jìn)程正在訪問共享資源時,其他進(jìn)程訪問該資源。因此,主要作為進(jìn)程間以及同一進(jìn)程內(nèi)不同線程之間的同步手段。
6、信號
信號(sinal):信號是一種比較復(fù)雜的通信方式,用于通知接收進(jìn)程某個事件已經(jīng)發(fā)生。
7、共享內(nèi)存通信
共享內(nèi)存(shared
memory):共享內(nèi)存就是映射一段能被其他進(jìn)程所訪問的內(nèi)存,這段共享內(nèi)存由一個進(jìn)程創(chuàng)建,但多個進(jìn)程都可以訪問。共享內(nèi)存是最快的IPC方式,它是針對其他進(jìn)程間通信方式運(yùn)行效率低而專門設(shè)計的。它往往與其他通信機(jī)制,如信號量,配合使用,來實(shí)現(xiàn)進(jìn)程間的同步和通信。
8、套接字通信
套接字(socket):套接字也是一種進(jìn)程間通信機(jī)制,與其他通信機(jī)制不同的是,它可用于不同機(jī)器間的進(jìn)程通信。
LINUX下系統(tǒng)編程,多進(jìn)程間數(shù)據(jù)共享,即共享變量
There are programming details at this site:
Unix多進(jìn)程編程和進(jìn)程間的通訊
有詳細(xì)的源程序,我就不一一復(fù)制。
linux 可以照貓畫虎。
共享內(nèi)存相關(guān)的API怎么使用不難:
1. 首先調(diào)用shmget分配一個新的共享內(nèi)存,這里你可以指定其大小,如果你要分配一個整形,那你可以將size參數(shù)設(shè)置成4,如果你要共享一個結(jié)構(gòu)體那就將size參數(shù)設(shè)置成你的結(jié)構(gòu)體大小,操作系統(tǒng)不關(guān)心你要共享什么,它只關(guān)心你要分配多少個字節(jié)的區(qū)間。而且實(shí)際上操作系統(tǒng)會將你要求的大小按照內(nèi)存頁面的大小進(jìn)行對齊,也就是說它可能實(shí)際上給你分配若干個頁面的物理存儲空間,只要這個空間能夠容納你所指定的大小就ok了。它的第三個參數(shù)是關(guān)于一些訪問權(quán)限設(shè)置的,要講起來太長,建議自己搜索一下,或者用man查查幫助??傊{(diào)用完shmget以后系統(tǒng)會給你創(chuàng)建一段共享內(nèi)存,然后返回給你一個shmid,也就是這個共享內(nèi)存的標(biāo)識,你可以理解為給它取了個名字。
2. 接著調(diào)用shmat將這段共享內(nèi)存映射到你的進(jìn)程的虛擬地址空間上。這個函數(shù)的之一個參數(shù)就是你之前調(diào)用shmget創(chuàng)建的共享內(nèi)存的名字shmid;第二個參數(shù)是個指針,指向你的進(jìn)程虛存空間中的某個地址,你可以通過傳入一個確定的地址強(qiáng)行要求操作系統(tǒng)將共享內(nèi)存映射到你指定的虛存地址上(可能會失敗,如果你指定的虛擬地址空間已經(jīng)映射了別的物理存儲空間),也可以通過傳入0地址讓系統(tǒng)給你選擇一個合適的地址(它會通過返回值把地址返回給你)。第三個參數(shù)則允許你指定一些特殊的標(biāo)志位,還是那句話,太復(fù)雜自己搜索一下看看,一般應(yīng)用不需要用到。
至于例子嘛你可以看看下面這個鏈接:
另外,你要知道只用共享內(nèi)存是不互斥的,你必須結(jié)合信號量一起使用才能防止互斥問題的出現(xiàn)。如果你共享的只是一個整形變量可能問題不大,因為對頁面對齊的整形變量的讀寫都是原子操作,但如果你共享的是個復(fù)雜的結(jié)構(gòu)體就得小心了。
簡單的實(shí)現(xiàn),沒有添加同步機(jī)制,回頭再添加上去,而且,我是在不同終端里面寫的,你可以把兩段代碼,一個放到父進(jìn)程,一個放到子進(jìn)程…就可以了
你說的這些API,自己man 一次,看看說明就知道用法了….
樓上說的對齊的問題,我沒有太注意..不過,不管你要共享什么,一個sizeof看看大小,一個memcpy拷貝,你就作為數(shù)據(jù)直接拷貝到共享內(nèi)存區(qū)域就OK了…另外一邊再拷貝回來,用一個結(jié)構(gòu)體類型的指針指向你拷貝回來的數(shù)據(jù),不就給這部分內(nèi)存再規(guī)劃成一個結(jié)構(gòu)體了。。
至于具體的, KEY 的含義,你需要了解linux的ipc機(jī)制。
#include
#include
#include
#include
#define BUF_SIZE 100
#define KEY 99
int main(void)
{
int shmid;
char *shmptr;
shmid=shmget(99,BUF_SIZE,IPC_CREAT|0666);
if(shmid==-1)
{
printf(“Shared Memory Created error…\n”);exit(0);
}
shmptr=shmat(shmid,NULL,0);
if(shmptr==(void*)-1)
{
printf(“shmat error,shmptr= %d \n”,shmptr);
exit(1);
}
while(1)
{
printf(“type strings into Shared Memory:”);
fgets(shmptr,BUF_SIZE,stdin);
}
return 0;
}
下面這段就每隔10秒鐘掃描共享內(nèi)存區(qū)域的內(nèi)容:
#include
#include
#include
#include
#define BUF_SIZE 100
#define KEY 99
int main(void)
{
int shmid;
char *shmptr;
shmid=shmget(99,BUF_SIZE,IPC_CREAT|0666);
if(shmid==-1)
{
printf(“Shared Memory Created error…\n”);exit(0);
}
shmptr=shmat(shmid,NULL,0);
if(shmptr==(void*)-1)
{
printf(“shmat error,shmptr= %d \n”,shmptr);
exit(1);
}
while(1)
{
printf(“Infomation in Shared Memory:”);
printf(“%s \n”,shmptr);
sleep(10);
}
return 0;
}
幫你頂,希望有這方面專業(yè)知道的朋友能站出來幫助你解決困難.
關(guān)于linux不同進(jìn)程信號燈共享的介紹到此就結(jié)束了,不知道你從中找到你需要的信息了嗎 ?如果你還想了解更多這方面的信息,記得收藏關(guān)注本站。
香港服務(wù)器選創(chuàng)新互聯(lián),2H2G首月10元開通。
創(chuàng)新互聯(lián)(www.cdcxhl.com)互聯(lián)網(wǎng)服務(wù)提供商,擁有超過10年的服務(wù)器租用、服務(wù)器托管、云服務(wù)器、虛擬主機(jī)、網(wǎng)站系統(tǒng)開發(fā)經(jīng)驗。專業(yè)提供云主機(jī)、虛擬主機(jī)、域名注冊、VPS主機(jī)、云服務(wù)器、香港云服務(wù)器、免備案服務(wù)器等。
分享名稱:Linux進(jìn)程間共享信號量的實(shí)現(xiàn)(linux不同進(jìn)程信號燈共享)
URL網(wǎng)址:http://m.fisionsoft.com.cn/article/djsghsj.html


咨詢
建站咨詢
