一、相關知識
訊號量:一個整數;
大於或等於0時代表可供並發進程使用的資源實體數;
小於0時代表正在等待使用臨界區的進程數;
用於互斥的訊號量初始值應大於0;
只能通過P、V原語操作而改變;
訊號量元素組成:
1、表示訊號量元素的值;
2、最後操作訊號量元素的進程ID
3、等待訊號量元素值+1的進程數;
4、等待訊號量元素值為0的進程數;
二、主要函數
1.1 建立訊號量
int semget(
key_t key, //標識訊號量的關鍵字,有三種方法:1、使用IPC——PRIVATE讓系統產生,
// 2、挑選一個隨機數,3、使用ftok從檔案路徑名中產生
int nSemes, //訊號量集中元素個數
int flag //IPC_CREAT;IPC_EXCL 只有在訊號量集不存在時建立
)
成功:返回訊號量控制代碼
失敗:返回-1
1.2 使用ftok函數根據檔案路徑名產生一個關鍵字
key_t ftok(const char *pathname,int proj_id);
路徑名稱必須有相應許可權
1.3 控制訊號量
int semctl(
int semid, //訊號量集的控制代碼
int semnum, //訊號量集的元素數
int cmd, //命令
/*union senum arg */... //
)
成功:返回相應的值
失敗:返回-1
命令詳細說明:
cmd: IPC_RMID 刪除一個訊號量
IPC_EXCL 只有在訊號量集不存在時建立
IPC_SET 設定訊號量的許可權
SETVAL 設定指定訊號量的元素的值為 agc.val
GETVAL 獲得一個指定訊號量的值
GETPID 獲得最後操縱此元素的最後進程ID
GETNCNT 獲得等待元素變為1的進程數
GETZCNT 獲得等待元素變為0的進程數
union senum 定義如下:
union senum{
int val;
struct semid_ds *buf;
unsigned short * array;
}agc;
其中 semid_ds 定義如下:
struct semid_ds{
struct ipc_pem sem_pem; //operation pemission struct
time_t sem_otime; //last semop()time
time_t sem_ctime; //last time changed by semctl()
struct sem *sembase; //ptr to first semaphore in array
struct sem_queue *sem_pending; //pending operations
struct sem_queue *sem_pending_last; //last pending operations
struct sem_undo *undo; //undo requests on this arrary
unsigned short int sem_nsems; //number of semaphores in set
};
1.4 對訊號量 +1 或 -1 或測試是否為0
int semop(
int semid,
struct sembuf *sops, //指向元素運算元組
unsigned short nsops //數組中元素操作的個數
)
結構 sembuf 定義
sembuf{
short int sem_num; //semaphore number
short int sem_op; //semaphore operaion
short int sem_flg //operation flag
};
三、例子:
2.1 伺服器
#i nclude <sys/sem.h>
#i nclude <sys/ipc.h>
#define SEGSIZE 1024
#define READTIME 1
union semun {
int val;
struct semid_ds *buf;
unsigned short *array;
} arg;
//產生訊號量
int sem_creat(key_t key)
{
union semun sem;
int semid;
sem.val = 0;
semid = semget(key,1,IPC_CREAT|0666);
if (-1 == semid){
printf("create semaphore error\n");
exit(-1);
}
semctl(semid,0,SETVAL,sem);
return semid;
}
//刪除訊號量
void del_sem(int semid)
{
union semun sem;
sem.val = 0;
semctl(semid,0,IPC_RMID,sem);
}
//p
int p(int semid)
{
struct sembuf sops={0,+1,IPC_NOWAIT};
return (semop(semid,&sops,1));
}
//v
int v(int semid)
{
struct sembuf sops={0,-1,IPC_NOWAIT};
return (semop(semid,&sops,1));
}
int main()
{
key_t key;
int shmid,semid;
char *shm;
char msg[7] = "-data-";
char i;
struct semid_ds buf;
key = ftok("/",0);
shmid = shmget(key,SEGSIZE,IPC_CREAT|0604);
if (-1 == shmid){
printf(" create shared memory error\n");
return -1;
}
shm = (char *)shmat(shmid,0,0);
if (-1 == (int)shm){
printf(" attach shared memory error\n");
return -1;
}
semid = sem_creat(key);
for (i = 0;i <= 3;i++){
sleep(1);
p(semid);
sleep(READTIME);
msg[5] = '0' + i;
memcpy(shm,msg,sizeof(msg));
sleep(58);
v(semid);
}
shmdt(shm);
shmctl(shmid,IPC_RMID,&buf);
del_sem(semid);
return 0;
//gcc -o shm shm.c -g
}
2.2 用戶端
#i nclude <sys/sem.h>
#i nclude <time.h>
#i nclude <sys/ipc.h>
#define SEGSIZE 1024
#define READTIME 1
union semun {
int val;
struct semid_ds *buf;
unsigned short *array;
} arg;
// 列印程式執行時間
void out_time(void)
{
static long start = 0;
time_t tm;
if (0 == start){
tm = time(NULL);
start = (long)tm;
printf(" now start ...\n");
}
printf(" second: %ld \n",(long)(time(NULL)) - start);
}
//建立訊號量
int new_sem(key_t key)
{
union semun sem;
int semid;
sem.val = 0;
semid = semget(key,0,0);
if (-1 == semid){
printf("create semaphore error\n");
exit(-1);
}
return semid;
}
//等待訊號量變成0
void wait_v(int semid)
{
struct sembuf sops={0,0,0};
semop(semid,&sops,1);
}
int main(void)
{
key_t key;
int shmid,semid;
char *shm;
char msg[100];
char i;
key = ftok("/",0);
shmid = shmget(key,SEGSIZE,0);
if(-1 == shmid){
printf(" create shared memory error\n");
return -1;
}
shm = (char *)shmat(shmid,0,0);
if (-1 == (int)shm){
printf(" attach shared memory error\n");
return -1;
}
semid = new_sem(key);
for (i = 0;i < 3;i ++){
sleep(2);
wait_v(semid);
printf("Message geted is: %s \n",shm + 1);
out_time();
}
shmdt(shm);
return 0;
// gcc -o shmc shmC.c -g
}
另一個例子:
題目是:寫一個程式,該程式建立兩個進程,分別列印"this is the child process"和"father say hello to child",要求交替列印,輸出成"this father is say the hello child to process child",每列印一個單詞進程阻塞一段時間。將輸出列印到目前的目錄下的tmp檔案中。
答:
- #include <stdio.h>
- #include <stdlib.h>
- #include <sys/types.h>
- #include <sys/ipc.h>
- #include <sys/sem.h>
- #include <sys/stat.h>
- #include <fcntl.h>
-
- union semun
- {
- int val;
- struct semid_ds *buf;
- unsigned short int *array;
- struct seminfo *__buf;
- };
-
- int main(void)
- {
- char* buf_child[]={"this", "is", "the", "child", "process"};
- char* buf_father[]={"father", "say", "hello", "to", "child"};
- int i = 0, semid, fd;
- pid_t pid;
- struct sembuf sb; //訊號量操作
- union semun sem;
- semid = semget(1000, 2, 0666 | IPC_CREAT); //申請訊號量組,包含2個訊號量
-
- sem.val = 0;
- semctl(semid, 0, SETVAL, sem); //初始化0號訊號量為0
- sem.val = 1;
- semctl(semid, 1, SETVAL, sem); //初始化1號訊號量為1
-
- fd=open("tmp",O_CREAT|O_TRUNC|O_WRONLY,0666);
-
- pid = fork();
- switch (pid) {
- case -1:
- perror("fork fail");
- break;
- case 0: /* child consume */
- srand((unsigned int)getpid());
- while (i < 5) {
- sb.sem_num = 1; //將1號訊號量
- sb.sem_op = -1; //減1
- sb.sem_flg = sb.sem_flg & ~IPC_NOWAIT;
- semop(semid, &sb, 1);
-
- write(fd,buf_child[i], strlen(buf_child[i]));
- sleep(rand());
- write(fd,&" ", 1);
- i++;
-
- sb.sem_num = 0; //將0號訊號量
- sb.sem_op = 1; //加1
- sb.sem_flg = sb.sem_flg & ~IPC_NOWAIT;
- semop(semid, &sb, 1); //操作訊號量
- }
- break;
- default:/* parent production */
- srand((unsigned int)getpid());
- while (i < 5) {
- sb.sem_num = 0; //將0號訊號量
- sb.sem_op = -1; //減1
- sb.sem_flg = sb.sem_flg & ~IPC_NOWAIT;
- semop(semid, &sb, 1); //操作訊號量
-
- write(fd,buf_father[i], strlen(buf_father[i]));
- sleep(rand());
- write(fd,&" ", 1);
- i++;
-
- sb.sem_num = 1;
- sb.sem_op = 1;
- sb.sem_flg = sb.sem_flg & ~IPC_NOWAIT;
- semop(semid, &sb, 1);
- }
- break;
- }
- return 0;
- }
posix訊號量:
1。POSIX無名訊號量 如果你學習過作業系統,那麼肯定熟 悉PV操作了.PV操作是原子操作.也就是操作是不可以中斷的,在一定的時間內,只能夠有一個進程的代碼在CPU上面執行.在系統當中,有時候為了順利的 使用和保護共用資源,大家提出了訊號的概念. 假設我們要使用一台印表機,如果在同一時刻有兩個進程在向印表機輸出,那麼最終的結果會是什麼呢.為了處理 這種情況,POSIX標準提出了有名訊號量和無名訊號量的概念,由於Linux只實現了無名訊號量,我們在這裡就只是介紹無名訊號量了. 訊號量的使用主 要是用來保護共用資源,使的資源在一個時刻只有一個進程所擁有.為此我們可以使用一個號誌.當號誌的值為某個值的時候,就表明此時資源不可以使用.否
則就表>示可以使用. 為了提供效率,系統提供了下面幾個函數
POSIX的無名訊號量的函數有以下幾個:
#include
int sem_init(sem_t *sem,int pshared,unsigned int value);
int sem_destroy(sem_t *sem);
int sem_wait(sem_t *sem);
int sem_trywait(sem_t *sem);
int sem_post(sem_t *sem);
int sem_getvalue(sem_t *sem);
sem_init 建立一個號誌,並初始化其值為value.pshared決定了訊號量能否在幾個進程間共用.由於目前Linux還沒有實現進程間共用號誌,所以這個 值只能夠取0. sem_destroy是用來刪除號誌的.sem_wait調用將阻塞進程,直到號誌的值大於0.這個函數返回的時候自動的將號誌 的值的件一.sem_post和sem_wait相反,是將號誌的內容加一同時發出訊號喚醒等待的進程..sem_trywait和sem_wait相 同,不過不阻塞的,當號誌的值為0的時候返回EAGAIN,表示以後重試.sem_getvalue得到號誌的值.
由於Linux不支 持,我們沒有辦法用來源程式解釋了.
這幾個函數的使用相當簡單的.比如我們有一個程式要向一個系統印表機列印兩頁.我們首先建立一個號誌,並 使其初始值為1,表示我們有一個資源可用.然後一個進程調用sem_wait由於這個時候號誌的值為1,所以這個函數返回,印表機開始列印了,同時訊號 燈的值為0 了. 如果第二個進程要列印,調用sem_wait時候,由於號誌的值為0,資源不可用,於是被阻塞了.當第一個進程列印完成以後,調用 sem_post號誌的值為1了,這個時候系統通知第二個進程,於是第二個進程的sem_wait返回.第二個進程開始列印了.