#include<stdio.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/sem.h>
#include<sys/unistd.h>
#include<stdlib.h>
#include<math.h>
#include<errno.h>
/****************************************************
訊號量的主要作用是限制多線程或者進程對臨界區的訪問,類似於互斥鎖mutex
使用執行個體:如取票通道只有N個,最多隻能允許N個人同時取票,
此時若有M個人時,且M>N,需要則訪問不到的通道就可以使用阻塞等待與非阻塞等待
******************************************************/
int main() {
int pid; //進程ID
int val = 1; //為可用資源數量,即通道數N=1
int semid; //為號誌(即訊號量)集的識別代號
key_t semkey; //唯一索引值
if ((semkey = ftok("/tmp", 0)) < 0) //將檔案路徑和項目ID(後面那個“1“)轉換為System V IPC Key
{
printf("ftok函數轉換出現錯誤。\n");
exit(1);
} else
printf("ftok函數轉換成功,key值為%d\n", semkey);
if ((semid = semget(semkey, 1, IPC_CREAT | IPC_EXCL | 0700)) < 0) //建立號誌集,其中包含1個號誌
{
printf("semget函數建立號誌集出現錯誤。\n");
exit(2);
} else
printf("semget函數建立號誌整合功,號誌集識別代號semid為:%d\n", semid);
if ((semctl(semid, 0, SETVAL, val)) < 0) //此句與上句合起來的功能是將semid對應的訊號集中的第一個訊號(0表示第一個訊號)的可用資源數量設定為1
{
printf("semctl函數設定可用資源數量時出現錯誤。\n");
exit(9);
} else
printf("semctl函數設定可用資源數量成功。\n");
if ((pid = fork()) < 0) {
printf("fork函數建立子進程出現錯誤。\n");
exit(3);
} else if (pid > 0) //父進程,先索取共用資源,而後釋放
{
struct sembuf p_op_buf, v_op_buf;
printf("父進程ID為:%d,該進程將要索取共用資源\n", getpid());
p_op_buf.sem_num = 0;
p_op_buf.sem_op = -1; //申請資源是-1,讓出資源是+1
if (semop(semid, &p_op_buf, 1) < 0) //以上三行向semid代表的號誌集的第一個號誌申請一個資源
{
printf("父進程中,semop函數索取共用資源出現錯誤。\n");
exit(4);
} else {
int i;
printf("父進程中,semop函數索取共用資源成功,索取個數為:%d\n", abs(p_op_buf.sem_op));
printf("ID為%d的父進程現在要睡上6秒鐘。\n", getpid());
for (i = 6; i > 0; i--) {
printf("%d號進程(父進程)還要睡%d秒鐘……\n", getpid(), i);
sleep(1);
}
printf("%d號進程(父進程)已經醒來,並將釋放資源。\n", getpid());
v_op_buf.sem_num = 0;
v_op_buf.sem_op = 1;
p_op_buf.sem_flg = 0; //該標誌位此處不清零不會出現錯誤,但下面子進程中的該標誌位應清零,因為那時父進程已經佔據了資源,子進程申請不到資源的情況下,該標誌位如果不清零(比如採用不賦值的預設處理),可能會出現錯誤。
if (semop(semid, &v_op_buf, 1) < 0) //以上三行將釋放一個資源給semid代表的號誌集第一個號誌
{
printf("父進程中,semop函數索取共用資源出現錯誤。\n");
exit(5);
} else {
printf("ID號為%d的父進程中,semop函數釋放共用資源成功,釋放個數為:%d\n", getpid(),
v_op_buf.sem_op);
sleep(1);
}
}
} else //子進程不斷申請共用資源,申請到後聲明一下,然後釋放
{
struct sembuf p_op_buf, v_op_buf;
sleep(2); //等待父進程將唯一的資源佔用
printf("子進程ID為:%d,該進程將要索取共用資源\n", getpid());
p_op_buf.sem_num = 0;
p_op_buf.sem_op = -1;
//sem_flg:訊號操作標誌,可能的選擇有兩種
//IPC_NOWAIT 對訊號的操作不能滿足時,semop()不會阻塞,並立即返回,同時設定錯誤資訊。
//SEM_UNDO 程式結束時(不論正常或不正常),保證訊號值會被重設為semop()調用前的值。這樣做的目的在於避免程式在異常情況下結束時未將鎖定資源解鎖,造成該資源永遠鎖定。
p_op_buf.sem_flg = SEM_UNDO; //該標誌位不用要清零,此處不清零將出現錯誤,0表示預設阻塞
//阻塞申請
if (semop(semid, &p_op_buf, 1) < 0) //向semid代表的號誌集的第一個號誌申請一個資源,申請不到就一直在這兒等著,什麼時候有了資源,什麼時候接著向下進行
{
printf("子進程中,semop函數索取共用資源出現錯誤:%d\n", errno);
exit(6);
} else
printf("**********子程式已經成功申請到資源!**********\n");
v_op_buf.sem_num = 0;
v_op_buf.sem_op = 1;
if (semop(semid, &v_op_buf, 1) < 0) //以上三行將釋放一個資源給semid代表的號誌集第一個號誌
{
printf("子進程中,semop函數索取共用資源出現錯誤。\n");
exit(7);
} else {
printf("子進程中,semop函數釋放共用資源成功,釋放個數為:%d\n", v_op_buf.sem_op);
printf("ID號為%d的進程(子進程)退出!\n", getpid());
if (semctl(semid, 0, IPC_RMID, 0) < 0) {
printf("調用semctl刪除訊號量集出現錯誤。\n");
exit(8);
} else {
printf("識別代號為%d的訊號量集已經被刪除\n", semid);
exit(0);
}
}
}
}