嵌入式Linux併發程式設計,程序間通訊方式,System V IPC,訊號燈集,建立/開啟semget(),初始化semctl(),P/V操作semop(),sembuf結構體定義
阿新 • • 發佈:2018-12-14
文章目錄
- 1,System V IPC - 訊號燈
- 2,System V IPC - 訊號燈特點
- 3,System V訊號燈使用步驟
- 4,訊號燈集/共享記憶體---示例
1,System V IPC - 訊號燈
- 訊號燈也叫訊號量,用於程序/執行緒同步或互斥的機制
- 訊號燈的型別
·Posix 無名訊號燈
·Posix有名訊號燈
·System V 訊號燈 - 訊號燈的含義
·計數訊號燈(訊號燈的值就是他代表的資源的數量,Posix 無名訊號燈/Posix有名訊號燈,都是計數訊號燈)
2,System V IPC - 訊號燈特點
- System V 訊號燈是一個或多個計數訊號燈的集合
- 可同時操作集合中的多個訊號燈
- 申請多個資源時避免死鎖(訊號燈在一個集合裡,需要的資源同時申請,都滿足時資源才能申請到)
3,System V訊號燈使用步驟
- 開啟/建立訊號燈 semget
- 訊號燈初始化 semctl
- P/V操作 semop
- 刪除訊號燈 semctl
3.1,訊號燈建立/開啟 semget()
#include <sys/ipc.h>
#include <sys/sem.h>
int semget(key_t key, int nsems, int semflg);
- 成功時返回訊號燈的id,失敗時返回-1
- key 和訊號燈關聯的key IPC_PRIVATE 或 ftok
- nsems 集合中包含的計數訊號燈個數
- semflg 標誌位 IPC_CREAT|0666 IPC_EXCL(通常和IPC_CREAT一起使用,加上IPC_EXCL,如果訊號燈存在,則報錯)
訊號燈在使用之前必須初始化,且只能初始化一次,通常第一個程序初始化,後面的程序就不能初始化了。
if((semid = semget(key,3,IPC_CREAT|0666|IPC_EXCL)) < 0)
{
if(errno == EEXIST)
{
semid = semget(key,3,IPC_CREAT|0666)
}
}
else
{
初始化訊號燈集;
}
3.2,訊號燈初始化 semctl()
#include <sys/ipc.h>
#include <sys/sem.h>
int semctl(int semid, int semnum, int cmd, …);
- 成功時返回0,失敗時返回EOF
- semid 要操作的訊號燈集id
- semnum 要操作的集合中的訊號燈編號
- cmd 執行的操作 SETVAL(設定訊號燈值) IPC_RMID(刪除整個訊號燈集)
- union semun 取決於cmd(SETVAL需要用到第四個引數,IPC_RMID不需要第四個引數),該共用體需要使用者自己定義
系統中union semun 共用體參考
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 */
struct seminfo *__buf; /* Buffer for IPC_INFO
(Linux-specific) */
};
3.2.1,訊號燈初始化—示例
要求:假設訊號燈集合中包含兩個訊號燈;第一個初始化為2,第二個初始化為0
union semun myun;
myun.val = 2;
if (semctl(semid, 0, SETVAL, myun) < 0)
{
perror(“semctl”); exit(-1);
}
myun.val = 0;
if (semctl(semid, 1, SETVAL, myun) < 0)
{
perror(“semctl”); exit(-1);
}
3.3,訊號燈P/V操作 semop()
#include <sys/ipc.h>
#include <sys/sem.h>
int semop(int semid, struct sembuf *sops, unsigned nsops);
- 成功時返回0,失敗時返回-1
- semid 要操作的訊號燈集id
- sops 描述對訊號燈操作的結構體(陣列)
- nsops 要操作的訊號燈的個數(一次操作sops數組裡的前nsops個元素)
3.3.1,訊號燈操作 sembuf結構體定義
struct sembuf
{
short sem_num;
short sem_op;
short sem_flg;
};
- semnum 訊號燈編號
- sem_op -1:P操作 1:V操作 (也可以讓訊號數量增加或減少多個,將“1”換成“n”)
- sem_flg 0(阻塞方式) / IPC_NOWAIT
假設當前訊號燈集合中有3個訊號燈(意味著最多可以同時操作3個訊號燈),編號:0,1,2
現在同時對編號為0和2的訊號燈程序P操作
struct sembuf buf[3];//結構體陣列的大小應該和訊號燈集中訊號燈的數目保持一致
buf[0].sem_num = 0;
buf[0].sem_op = -1;
buf[0].sem_flg = 0;
buf[1].sem_num = 2;
buf[1].sem_op = -1;
buf[1].sem_flg = 0;
semop(semid,&buf,2);//2表示依次取出buf中的前兩個元素進行操作
4,訊號燈集/共享記憶體—示例
要求:父子程序通過System V訊號燈同步對共享記憶體的讀寫
·父程序從鍵盤輸入字串到共享記憶體
·子程序刪除字串中的空格並列印
·父程序輸入quit後刪除共享記憶體和訊號燈集,程式結束
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#define N 64 //共享記憶體大小
#define READ 0 //可讀緩衝區(個數)訊號燈在訊號燈集合中的編號為0
#define WRITE 1 //可寫緩衝區(個數)訊號燈在訊號燈集合中的編號為1
union semun{
int val;
struct semid_ds *buf;
unsigned short *array;
struct seminfo * __buf;
};
void init_sem(int semid,int s[],int n);
void sem_pv(int semid,int num,int op);
int main(int argc, const char *argv[])
{
int shmid,semid,s[]={0,1};
pid_t pid;
key_t key;
char *shmaddr;
if((key = ftok(".",'s')) == -1)//生成KEY值
{
perror("ftok");
exit(-1);
}
if((shmid = shmget(key,N,IPC_CREAT|0666)) < 0)//申請共享記憶體
{
perror("shmget");
exit(-1);
}
if((semid = semget(key,2,IPC_CREAT|0666)) < 0)//申請訊號燈集
{
perror("semget");
goto _error1;//申請失敗,釋放申請的共享記憶體,程式退出
}
init_sem(semid,s,2);//訊號燈集初始化
if((shmaddr = shmat(shmid,NULL,0)) == (char *)-1)//對映共享記憶體地址
{
perror("shmat");
goto _error2;//對映失敗,釋放申請的訊號燈集,釋放申請的共享記憶體,程式退出
}
if((pid = fork()) < 0)//建立子程序
{
perror("fork");
goto _error2;
}
else if(pid == 0)
{
char *p,*q;
while(1)
{
sem_pv(semid,READ,-1);//對讀訊號燈進行P操作
p = q = shmaddr;
while(*q)
{
if(*q != ' ')
{
*p++ = *q;
}
q++;
}
*p = '\0';
printf("%s",shmaddr);
sem_pv(semid,WRITE,1);//對寫訊號燈進行V操作
}
}
else
{
while(1)
{
sem_pv(semid,WRITE,-1);//對寫訊號燈進行P操作
printf("input > ");
fgets(shmaddr,N,stdin);
if(strcmp(shmaddr,"quite\n") == 0)break;
sem_pv(semid,READ,1);//對讀訊號燈進行V操作
}
kill(pid,SIGUSR1);//給子程序發訊號,讓其結束
}
_error2:
semctl(semid,0,IPC_RMID);//釋放申請的訊號燈集
_error1:
shmctl(shmid,IPC_RMID,NULL);//釋放申請的共享記憶體
return 0;
}
void init_sem(int semid,int s[],int n)//訊號燈集初始化函式
{
int i;
union semun myyun;
for(i=0;i<n;i++)
{
myyun.val = s[i];//讀訊號燈值初始化為0,寫訊號燈值初始化為1
semctl(semid,i,SETVAL,myyun);
}
}
void sem_pv(int semid,int num,int op)//訊號燈集P/V操作函式
{
struct sembuf buf;
buf.sem_num = num;//訊號燈編號
buf.sem_op = op;
buf.sem_flg = 0;
semop(semid,&buf,1);
}