程序間的通訊方式_訊號量
訊號量
當我們編寫的程式使用了執行緒時,不管它是執行在多使用者系統上、多執行緒系統上,還是執行在多使用者多程序系統上,我們通常會發現程式中存在著一部分臨界程式碼,我們需要只有一個程序(或一個執行執行緒)可以進入這個臨界程式碼並擁有對資源獨佔式的訪問權。為了防止出現因多個程式同時訪問一個共享資源而引發的問題,我們需要有一種方法,它可以通過生成並使用令牌來授權,在任一時刻只能有一個執行執行緒訪問程式碼的臨界區域。訊號量的一個更正式的定義式是:它是一個特殊變數,只允許對它進行等待(wait)和傳送訊號(signal)這兩種操作。
1. 臨界資源
同一時刻只能被一個程序訪問的資源
2. 臨界區
訪問臨界資原始碼區域
3. 原子操作
任何情況下都不能被打斷的操作
4. 核心物件
用於對程序間通訊時,多程序能夠訪問同一資源的記錄
訊號量的作用:程序間同步控制,訊號量相當於記錄資源能夠同時被多少個程序訪問
訊號量的操作:
建立或獲取:如果是建立,必須初始化;如果獲取,則不能初始化。
減一操作:P 操作
加一操作:V 操作
刪除:
訊號量操作的相關函式:
int semget((key_t)key, int nsems, int flag); //建立或者獲取
第一個引數 key 是整數值,不相關的程序可以通過它訪問同一個訊號量。
nsems 引數指定需要的訊號量數目,它幾乎總是取值為1。
flag 引數是一組標誌,它與 open 的函式標誌非常相似。
int semop(int semid, struct sembuf *buf, int lenyh); //P V 操作,用於改變訊號量的值
第一個引數 semid 是有semget返回的訊號量識別符號。
第二個引數 buf 是指向一個結構體陣列的指標,每個陣列元素至少包括以下幾個成員:
struct sembuf
{
short sem_num; //訊號量編號
short sem_op; //訊號量在一次操作中需要改變的值,-1為 P 操作,+1為 V 操作
short sem_flg;
}
int semctl(int semid, int pos, int cmd, /*union semun un*/);
cmd 引數為將要採取的動作。
練習:A 程序輸入“OK”,B 程序輸出數字0~9。
程式碼如下:
#ifndef _SEM_H
#define _SEM_H
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semid;
union semun
{
int val;
};
void sem_get();
void sem_p();
void sem_v();
void sem_del();
#endif
#include "sem.h"
void sem_get()
{
semid = semget((key_t)1234, 1, 0664);
if (semid == -1)
{
semid = semget((key_t)1234, 1, 0664 | IPC_CREAT);
assert(semid != -1);
union semun v;
v.val = 0;
if (semctl(semid, 0, SETVAL, v) == -1)
{
perror("error");
exit(0);
}
}
}
void sem_p()
{
struct sembuf buffer;
buffer.sem_num = 0;
buffer.sem_op = -1;
buffer.sem_flg = SEM_UNDO;
if (semop(semid, &buffer, 1) == -1)
{
perror("p error");
exit(0);
}
}
void sem_v()
{
struct sembuf buffer;
buffer.sem_num = 0;
buffer.sem_op = 1;
buffer.sem_flg = SEM_UNDO;
if (semop(semid, &buffer, 1) == -1)
{
perror("v error");
exit(0);
}
}
void sem_del()
{
if (semctl(semid, 0, IPC_RMID) == -1)
{
perror("del error");
exit(0);
}
}
程序 A 程式碼如下:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <fcntl.h>
//#include <sys/ipc.h>
//#include <sys/sem.h>
#include "sem.h"
#include "sem.c"
int main()
{
sem_get();
while (1)
{
printf("Please input: ");
fflush(stdout);
char buffer[128] = {0};
fgets(buffer, 127, stdin);
buffer[strlen(buffer)-1] = 0;
if (strncmp(buffer, "OK", 2) == 0)
{
sem_v();
}
if (strncmp(buffer, "end", 3) == 0)
{
break;
}
}
sem_del();
return 0;
}
B 程序程式碼如下:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <fcntl.h>
//#include <sys/ipc.h>
//#include <sys/sem.h>
#include "sem.h"
#include "sem.c"
int main()
{
sem_get();
sem_p();
for (int i=0; i<10; i++)
{
printf("%d \n", i);
}
sem_del();
return 0;
}