最全面的linux信号量解析
生活随笔
收集整理的這篇文章主要介紹了
最全面的linux信号量解析
小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
信號(hào)量
一.什么是信號(hào)量
信號(hào)量的使用主要是用來(lái)保護(hù)共享資源,使得資源在一個(gè)時(shí)刻只有一個(gè)進(jìn)程(線(xiàn)程)
所擁有。
信號(hào)量的值為正的時(shí)候,說(shuō)明它空閑。所測(cè)試的線(xiàn)程可以鎖定而使用它。若為0,說(shuō)明
它被占用,測(cè)試的線(xiàn)程要進(jìn)入睡眠隊(duì)列中,等待被喚醒。
二.信號(hào)量的分類(lèi)
在學(xué)習(xí)信號(hào)量之前,我們必須先知道——Linux提供兩種信號(hào)量:
(1) 內(nèi)核信號(hào)量,由內(nèi)核控制路徑使用
(2) 用戶(hù)態(tài)進(jìn)程使用的信號(hào)量,這種信號(hào)量又分為POSIX信號(hào)量和SYSTEM
V信號(hào)量。
POSIX信號(hào)量又分為有名信號(hào)量和無(wú)名信號(hào)量。
有名信號(hào)量,其值保存在文件中, 所以它可以用于線(xiàn)程也可以用于進(jìn)程間的同步。無(wú)名
信號(hào)量,其值保存在內(nèi)存中。
倘若對(duì)信號(hào)量沒(méi)有以上的全面認(rèn)識(shí)的話(huà),你就會(huì)很快發(fā)現(xiàn)自己在信號(hào)量的森林里迷
失了方向。
三.內(nèi)核信號(hào)量
1.內(nèi)核信號(hào)量的構(gòu)成
內(nèi)核信號(hào)量類(lèi)似于自旋鎖,因?yàn)楫?dāng)鎖關(guān)閉著時(shí),它不允許內(nèi)核控制路徑繼續(xù)進(jìn)行。然而,
當(dāng)內(nèi)核控制路徑試圖獲取內(nèi)核信號(hào)量鎖保護(hù)的忙資源時(shí),相應(yīng)的進(jìn)程就被掛起。只有在資源
被釋放時(shí),進(jìn)程才再次變?yōu)榭蛇\(yùn)行。
只有可以睡眠的函數(shù)才能獲取內(nèi)核信號(hào)量;中斷處理程序和可延遲函數(shù)都不能使用內(nèi)
核信號(hào)量。
內(nèi)核信號(hào)量是struct semaphore類(lèi)型的對(duì)象,它在<asm/semaphore.h>中定義:
struct semaphore {
atomic_t count;
int sleepers;
wait_queue_head_t wait;
}
count:相當(dāng)于信號(hào)量的值,大于0,資源空閑;等于0,資源忙,但沒(méi)有進(jìn)程等待這
個(gè)保護(hù)的資源;小于0,資源不可用,并至少有一個(gè)進(jìn)程等待資源。
wait:存放等待隊(duì)列鏈表的地址,當(dāng)前等待資源的所有睡眠進(jìn)程都會(huì)放在這個(gè)鏈表中。
sleepers:存放一個(gè)標(biāo)志,表示是否有一些進(jìn)程在信號(hào)量上睡眠。
2.內(nèi)核信號(hào)量中的等待隊(duì)列(刪除,沒(méi)有聯(lián)系)
上面已經(jīng)提到了內(nèi)核信號(hào)量使用了等待隊(duì)列wait_queue來(lái)實(shí)現(xiàn)阻塞操作。
當(dāng)某任務(wù)由于沒(méi)有某種條件沒(méi)有得到滿(mǎn)足時(shí),它就被掛到等待隊(duì)列中睡眠。當(dāng)條件得到滿(mǎn)足
時(shí),該任務(wù)就被移出等待隊(duì)列,此時(shí)并不意味著該任務(wù)就被馬上執(zhí)行,因?yàn)樗直灰七M(jìn)工
作隊(duì)列中等待CPU資源,在適當(dāng)?shù)臅r(shí)機(jī)被調(diào)度。
內(nèi)核信號(hào)量是在內(nèi)部使用等待隊(duì)列的,也就是說(shuō)該等待隊(duì)列對(duì)用戶(hù)是隱藏的,無(wú)須用
戶(hù)干涉。由用戶(hù)真正使用的等待隊(duì)列我們將在另外的篇章進(jìn)行詳解。
3.內(nèi)核信號(hào)量的相關(guān)函數(shù)
(1)初始化:
void sema_init (struct semaphore *sem, int val);
void init_MUTEX (struct semaphore *sem); //將sem的值置為1,表示資源空閑
void init_MUTEX_LOCKED (struct semaphore *sem); //將sem的值置為0,表示資源忙
(2)申請(qǐng)內(nèi)核信號(hào)量所保護(hù)的資源:
void down(struct semaphore * sem); // 可引起睡眠
int down_interruptible(struct semaphore * sem); // down_interruptible能被信號(hào)打斷
int down_trylock(struct semaphore * sem); // 非阻塞函數(shù),不會(huì)睡眠。無(wú)法鎖定資源則
馬上返回
(3)釋放內(nèi)核信號(hào)量所保護(hù)的資源:
void up(struct semaphore * sem);
4.內(nèi)核信號(hào)量的使用例程
在驅(qū)動(dòng)程序中,當(dāng)多個(gè)線(xiàn)程同時(shí)訪(fǎng)問(wèn)相同的資源時(shí)(驅(qū)動(dòng)中的全局變量時(shí)一種典型的
共享資源),可能會(huì)引發(fā)“競(jìng)態(tài)“,因此我們必須對(duì)共享資源進(jìn)行并發(fā)控制。Linux內(nèi)核中
解決并發(fā)控制的最常用方法是自旋鎖與信號(hào)量(絕大多數(shù)時(shí)候作為互斥鎖使用)。
ssize_t globalvar_write(struct file *filp, const char *buf, size_t len, loff_t *off)
{
//獲得信號(hào)量
if (down_interruptible(&sem))
{
return - ERESTARTSYS;
}
//將用戶(hù)空間的數(shù)據(jù)復(fù)制到內(nèi)核空間的global_var
if (copy_from_user(&global_var, buf, sizeof(int)))
{
up(&sem);
return - EFAULT;
}
//釋放信號(hào)量
up(&sem);
return sizeof(int);
}
四.POSIX 信號(hào)量與SYSTEM V信號(hào)量的比較
1. 對(duì)POSIX來(lái)說(shuō),信號(hào)量是個(gè)非負(fù)整數(shù)。常用于線(xiàn)程間同步。
而SYSTEM V信號(hào)量則是一個(gè)或多個(gè)信號(hào)量的集合,它對(duì)應(yīng)的是一個(gè)信號(hào)量結(jié)構(gòu)體,
這個(gè)結(jié)構(gòu)體是為SYSTEM V IPC服務(wù)的,信號(hào)量只不過(guò)是它的一部分。常用于進(jìn)程間同步。
2.POSIX信號(hào)量的引用頭文件是“<semaphore.h>”,而SYSTEM V信號(hào)量的引用頭文件是
“<sys/sem.h>”。
3.從使用的角度,System V信號(hào)量是復(fù)雜的,而Posix信號(hào)量是簡(jiǎn)單。比如,POSIX信
號(hào)量的創(chuàng)建和初始化或PV操作就很非常方便。
五.POSIX信號(hào)量詳解
1.無(wú)名信號(hào)量
無(wú)名信號(hào)量的創(chuàng)建就像聲明一般的變量一樣簡(jiǎn)單,例如:sem_t sem_id。然后再初
始化該無(wú)名信號(hào)量,之后就可以放心使用了。
無(wú)名信號(hào)量常用于多線(xiàn)程間的同步,同時(shí)也用于相關(guān)進(jìn)程間的同步。也就是說(shuō),無(wú)名信
號(hào)量必須是多個(gè)進(jìn)程(線(xiàn)程)的共享變量,無(wú)名信號(hào)量要保護(hù)的變量也必須是多個(gè)進(jìn)程
(線(xiàn)程)的共享變量,這兩個(gè)條件是缺一不可的。
常見(jiàn)的無(wú)名信號(hào)量相關(guān)函數(shù):sem_destroy
int sem_init(sem_t *sem, int pshared, unsigned int value);
1)pshared==0 用于同一多線(xiàn)程的同步;
2)若pshared>0 用于多個(gè)相關(guān)進(jìn)程間的同步(即由fork產(chǎn)生的)
int sem_getvalue(sem_t *sem, int *sval);
取回信號(hào)量sem的當(dāng)前值,把該值保存到sval中。
若有1個(gè)或更多的線(xiàn)程或進(jìn)程調(diào)用sem_wait阻塞在該信號(hào)量上,該函數(shù)返回兩種值:
1) 返回0
2) 返回阻塞在該信號(hào)量上的進(jìn)程或線(xiàn)程數(shù)目
linux采用返回的第一種策略。
sem_wait(或sem_trywait)相當(dāng)于P操作,即申請(qǐng)資源。
int sem_wait(sem_t *sem); // 這是一個(gè)阻塞的函數(shù)
測(cè)試所指定信號(hào)量的值,它的操作是原子的。
若sem>0,那么它減1并立即返回。
若sem==0,則睡眠直到sem>0,此時(shí)立即減1,然后返回。
int sem_trywait(sem_t *sem); // 非阻塞的函數(shù)
其他的行為和sem_wait一樣,除了:
若sem==0,不是睡眠,而是返回一個(gè)錯(cuò)誤EAGAIN。
sem_post相當(dāng)于V操作,釋放資源。
int sem_post(sem_t *sem);
把指定的信號(hào)量sem的值加1;
呼醒正在等待該信號(hào)量的任意線(xiàn)程。
注意:在這些函數(shù)中,只有sem_post是信號(hào)安全的函數(shù),它是可重入函數(shù)
(a)無(wú)名信號(hào)量在多線(xiàn)程間的同步
無(wú)名信號(hào)量的常見(jiàn)用法是將要保護(hù)的變量放在sem_wait和sem_post中間所形成的
臨界區(qū)內(nèi),這樣該變量就會(huì)被保護(hù)起來(lái),例如:
#include <pthread.h>
#include <semaphore.h>
#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
int number; // 被保護(hù)的全局變量
sem_t sem_id;
void* thread_one_fun(void *arg)
{
sem_wait(&sem_id);
printf("thread_one have the semaphore\n");
number++;
printf("number = %d\n",number);
sem_post(&sem_id);
}
void* thread_two_fun(void *arg)
{
sem_wait(&sem_id);
printf("thread_two have the semaphore \n");
number--;
printf("number = %d\n",number);
sem_post(&sem_id);
}
int main(int argc,char *argv[])
{
number = 1;
pthread_t id1, id2;
sem_init(&sem_id, 0, 1);
pthread_create(&id1,NULL,thread_one_fun, NULL);
pthread_create(&id2,NULL,thread_two_fun, NULL);
pthread_join(id1,NULL);
pthread_join(id2,NULL);
printf("main,,,\n");
return 0;
}
上面的例程,到底哪個(gè)線(xiàn)程先申請(qǐng)到信號(hào)量資源,這是隨機(jī)的。如果想要某個(gè)特定的順
序的話(huà),可以用2個(gè)信號(hào)量來(lái)實(shí)現(xiàn)。例如下面的例程是線(xiàn)程1先執(zhí)行完,然后線(xiàn)程2才繼
續(xù)執(zhí)行,直至結(jié)束。
int number; // 被保護(hù)的全局變量
sem_t sem_id1, sem_id2;
void* thread_one_fun(void *arg)
{
sem_wait(&sem_id1);
printf("thread_one have the semaphore\n");
number++;
printf("number = %d\n",number);
sem_post(&sem_id2);
}
void* thread_two_fun(void *arg)
{
sem_wait(&sem_id2);
printf("thread_two have the semaphore \n");
number--;
printf("number = %d\n",number);
sem_post(&sem_id1);
}
int main(int argc,char *argv[])
{
number = 1;
pthread_t id1, id2;
sem_init(&sem_id1, 0, 1); // 空閑的
sem_init(&sem_id2, 0, 0); // 忙的
pthread_create(&id1,NULL,thread_one_fun, NULL);
pthread_create(&id2,NULL,thread_two_fun, NULL);
pthread_join(id1,NULL);
pthread_join(id2,NULL);
printf("main,,,\n");
return 0;
}
(b)無(wú)名信號(hào)量在相關(guān)進(jìn)程間的同步
說(shuō)是相關(guān)進(jìn)程,是因?yàn)楸境绦蛑泄灿?個(gè)進(jìn)程,其中一個(gè)是另外一個(gè)的子進(jìn)程(由
fork
產(chǎn)生)的。
本來(lái)對(duì)于fork來(lái)說(shuō),子進(jìn)程只繼承了父進(jìn)程的代碼副本,mutex理應(yīng)在父子進(jìn)程
中是相互獨(dú)立的兩個(gè)變量,但由于在初始化mutex的時(shí)候,由pshared = 1指
定了mutex處于共享內(nèi)存區(qū)域,所以此時(shí)mutex變成了父子進(jìn)程共享的一個(gè)變
量。此時(shí),mutex就可以用來(lái)同步相關(guān)進(jìn)程了。
#include <semaphore.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
int main(int argc, char **argv)
{
int fd, i,count=0,nloop=10,zero=0,*ptr;
sem_t mutex;
//open a file and map it into memory
fd = open("log.txt",O_RDWR|O_CREAT,S_IRWXU);
write(fd,&zero,sizeof(int));
ptr = mmap( NULL,sizeof(int),PROT_READ |
PROT_WRITE,MAP_SHARED,fd,0 );
close(fd);
/* create, initialize semaphore */
if( sem_init(&mutex,1,1) < 0) //
{
perror("semaphore initilization");
exit(0);
}
if (fork() == 0)
{ /* child process*/
for (i = 0; i < nloop; i++)
{
sem_wait(&mutex);
printf("child: %d\n", (*ptr)++);
sem_post(&mutex);
}
exit(0);
}
/* back to parent process */
for (i = 0; i < nloop; i++)
{
sem_wait(&mutex);
printf("parent: %d\n", (*ptr)++);
sem_post(&mutex);
}
exit(0);
}
2.有名信號(hào)量
有名信號(hào)量的特點(diǎn)是把信號(hào)量的值保存在文件中。
這決定了它的用途非常廣:既可以用于線(xiàn)程,也可以用于相關(guān)進(jìn)程間,甚至是不相關(guān)
進(jìn)程。
(a)有名信號(hào)量能在進(jìn)程間共享的原因
由于有名信號(hào)量的值是保存在文件中的,所以對(duì)于相關(guān)進(jìn)程來(lái)說(shuō),子進(jìn)程是繼承了父
進(jìn)程的文件描述符,那么子進(jìn)程所繼承的文件描述符所指向的文件是和父進(jìn)程一樣的,當(dāng)
然文件里面保存的有名信號(hào)量值就共享了。
(b)有名信號(hào)量相關(guān)函數(shù)說(shuō)明
有名信號(hào)量在使用的時(shí)候,和無(wú)名信號(hào)量共享sem_wait和sem_post函數(shù)。
區(qū)別是有名信號(hào)量使用sem_open代替sem_init,另外在結(jié)束的時(shí)候要像關(guān)閉文件
一樣去關(guān)閉這個(gè)有名信號(hào)量。
(1)打開(kāi)一個(gè)已存在的有名信號(hào)量,或創(chuàng)建并初始化一個(gè)有名信號(hào)量。一個(gè)單一的調(diào)用就完
成了信號(hào)量的創(chuàng)建、初始化和權(quán)限的設(shè)置。
sem_t *sem_open(const char *name, int oflag, mode_t mode , int value);
name是文件的路徑名;
Oflag 有O_CREAT或O_CREAT|EXCL兩個(gè)取值;
mode_t控制新的信號(hào)量的訪(fǎng)問(wèn)權(quán)限;
Value指定信號(hào)量的初始化值。
注意:
這里的name不能寫(xiě)成/tmp/aaa.sem這樣的格式,因?yàn)樵趌inux下,sem都是創(chuàng)建
在/dev/shm目錄下。你可以將name寫(xiě)成“/mysem”或“mysem”,創(chuàng)建出來(lái)的文件都
是“/dev/shm/sem.mysem”,千萬(wàn)不要寫(xiě)路徑。也千萬(wàn)不要寫(xiě)“/tmp/mysem”之類(lèi)的。
當(dāng)oflag = O_CREAT時(shí),若name指定的信號(hào)量不存在時(shí),則會(huì)創(chuàng)建一個(gè),而且后
面的mode和value參數(shù)必須有效。若name指定的信號(hào)量已存在,則直接打開(kāi)該信號(hào)量,
同時(shí)忽略mode和value參數(shù)。
當(dāng)oflag = O_CREAT|O_EXCL時(shí),若name指定的信號(hào)量已存在,該函數(shù)會(huì)直接返
回error。
(2) 一旦你使用了一信號(hào)量,銷(xiāo)毀它們就變得很重要。
在做這個(gè)之前,要確定所有對(duì)這個(gè)有名信號(hào)量的引用都已經(jīng)通過(guò)sem_close()函數(shù)
關(guān)閉了,然后只需在退出或是退出處理函數(shù)中調(diào)用sem_unlink()去刪除系統(tǒng)中的信號(hào)量,
注意如果有任何的處理器或是線(xiàn)程引用這個(gè)信號(hào)量,sem_unlink()函數(shù)不會(huì)起到任何的作
用。
也就是說(shuō),必須是最后一個(gè)使用該信號(hào)量的進(jìn)程來(lái)執(zhí)行sem_unlick才有效。因?yàn)槊總€(gè)
信號(hào)燈有一個(gè)引用計(jì)數(shù)器記錄當(dāng)前的打開(kāi)次數(shù),sem_unlink必須等待這個(gè)數(shù)為0時(shí)才能把
name所指的信號(hào)燈從文件系統(tǒng)中刪除。也就是要等待最后一個(gè)sem_close發(fā)生。
(c)有名信號(hào)量在無(wú)相關(guān)進(jìn)程間的同步
前面已經(jīng)說(shuō)過(guò),有名信號(hào)量是位于共享內(nèi)存區(qū)的,那么它要保護(hù)的資源也必須是位于
共享內(nèi)存區(qū),只有這樣才能被無(wú)相關(guān)的進(jìn)程所共享。
在下面這個(gè)例子中,服務(wù)進(jìn)程和客戶(hù)進(jìn)程都使用shmget和shmat來(lái)獲取得一塊共享內(nèi)
存資源。然后利用有名信號(hào)量來(lái)對(duì)這塊共享內(nèi)存資源進(jìn)行互斥保護(hù)。
<u>File1: server.c </u>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <semaphore.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define SHMSZ 27
char SEM_NAME[]= "vik";
int main()
{
char ch;
int shmid;
key_t key;
char *shm,*s;
sem_t *mutex;
//name the shared memory segment
key = 1000;
//create & initialize semaphore
mutex = sem_open(SEM_NAME,O_CREAT,0644,1);
if(mutex == SEM_FAILED)
{
perror("unable to create semaphore");
sem_unlink(SEM_NAME);
exit(-1);
}
//create the shared memory segment with this key
shmid = shmget(key,SHMSZ,IPC_CREAT|0666);
if(shmid<0)
{
perror("failure in shmget");
exit(-1);
}
//attach this segment to virtual memory
shm = shmat(shmid,NULL,0);
//start writing into memory
s = shm;
for(ch='A';ch<='Z';ch++)
{
sem_wait(mutex);
*s++ = ch;
sem_post(mutex);
}
//the below loop could be replaced by binary semaphore
while(*shm != '*')
{
sleep(1);
}
sem_close(mutex);
sem_unlink(SEM_NAME);
shmctl(shmid, IPC_RMID, 0);
exit(0);
}
<u>File 2: client.c</u>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <semaphore.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define SHMSZ 27
char SEM_NAME[]= "vik";
int main()
{
char ch;
int shmid;
key_t key;
char *shm,*s;
sem_t *mutex;
//name the shared memory segment
key = 1000;
//create & initialize existing semaphore
mutex = sem_open(SEM_NAME,0,0644,0);
if(mutex == SEM_FAILED)
{
perror("reader:unable to execute semaphore");
sem_close(mutex);
exit(-1);
}
//create the shared memory segment with this key
shmid = shmget(key,SHMSZ,0666);
if(shmid<0)
{
perror("reader:failure in shmget");
exit(-1);
}
//attach this segment to virtual memory
shm = shmat(shmid,NULL,0);
//start reading
s = shm;
for(s=shm;*s!=NULL;s++)
{
sem_wait(mutex);
putchar(*s);
sem_post(mutex);
}
//once done signal exiting of reader:This can be replaced by
another semaphore
*shm = '*';
sem_close(mutex);
shmctl(shmid, IPC_RMID, 0);
exit(0);
}
六.SYSTEM V信號(hào)量
這是信號(hào)量值的集合,而不是單個(gè)信號(hào)量。相關(guān)的信號(hào)量操作函數(shù)由<sys/ipc.h>引用。
1.信號(hào)量結(jié)構(gòu)體
內(nèi)核為每個(gè)信號(hào)量集維護(hù)一個(gè)信號(hào)量結(jié)構(gòu)體,可在<sys/sem.h>找到該定義:
struct semid_ds {
struct ipc_perm sem_perm; /* 信號(hào)量集的操作許可權(quán)限 */
struct sem *sem_base; /* 某個(gè)信號(hào)量sem結(jié)構(gòu)數(shù)組的指針,當(dāng)前信號(hào)量集
中的每個(gè)信號(hào)量對(duì)應(yīng)其中一個(gè)數(shù)組元素 */
ushort sem_nsems; /* sem_base 數(shù)組的個(gè)數(shù) */
time_t sem_otime; /* 最后一次成功修改信號(hào)量數(shù)組的時(shí)間 */
time_t sem_ctime; /* 成功創(chuàng)建時(shí)間 */
};
struct sem {
ushort semval; /* 信號(hào)量的當(dāng)前值 */
short sempid; /* 最后一次返回該信號(hào)量的進(jìn)程ID 號(hào) */
ushort semncnt; /* 等待semval大于當(dāng)前值的進(jìn)程個(gè)數(shù) */
ushort semzcnt; /* 等待semval變成0的進(jìn)程個(gè)數(shù) */
};
2.常見(jiàn)的SYSTEM V信號(hào)量函數(shù)
(a)關(guān)鍵字和描述符
SYSTEM V信號(hào)量是SYSTEM V IPC(即SYSTEM V進(jìn)程間通信)的組成部分,其他
的有SYSTEM V消息隊(duì)列,SYSTEM V共享內(nèi)存。而關(guān)鍵字和IPC描述符無(wú)疑是它們的共
同點(diǎn),也使用它們,就不得不先對(duì)它們進(jìn)行熟悉。這里只對(duì)SYSTEM V信號(hào)量進(jìn)行討論。
IPC描述符相當(dāng)于引用ID號(hào),要想使用SYSTEM V信號(hào)量(或MSG、SHM),就必須
用IPC描述符來(lái)調(diào)用信號(hào)量。而IPC描述符是內(nèi)核動(dòng)態(tài)提供的(通過(guò)semget來(lái)獲取),用
戶(hù)無(wú)法讓服務(wù)器和客戶(hù)事先認(rèn)可共同使用哪個(gè)描述符,所以有時(shí)候就需要到關(guān)鍵字KEY來(lái)
定位描述符。
某個(gè)KEY只會(huì)固定對(duì)應(yīng)一個(gè)描述符(這項(xiàng)轉(zhuǎn)換工作由內(nèi)核完成),這樣假如服務(wù)器和
客戶(hù)事先認(rèn)可共同使用某個(gè)KEY,那么大家就都能定位到同一個(gè)描述符,也就能定位到同
一個(gè)信號(hào)量,這樣就達(dá)到了SYSTEM V信號(hào)量在進(jìn)程間共享的目的。
(b)創(chuàng)建和打開(kāi)信號(hào)量
int semget(key_t key, int nsems, int oflag)
(1) nsems>0 : 創(chuàng)建一個(gè)信的信號(hào)量集,指定集合中信號(hào)量的數(shù)量,一旦創(chuàng)建就不能更改。
(2) nsems==0 : 訪(fǎng)問(wèn)一個(gè)已存在的集合
(3) 返回的是一個(gè)稱(chēng)為信號(hào)量標(biāo)識(shí)符的整數(shù),semop和semctl函數(shù)將使用它。
(4) 創(chuàng)建成功后信號(hào)量結(jié)構(gòu)被設(shè)置:
.sem_perm 的uid和gid成員被設(shè)置成的調(diào)用進(jìn)程的有效用戶(hù)ID和有效組ID
.oflag 參數(shù)中的讀寫(xiě)權(quán)限位存入sem_perm.mode
.sem_otime 被置為0,sem_ctime被設(shè)置為當(dāng)前時(shí)間
.sem_nsems 被置為nsems參數(shù)的值
該集合中的每個(gè)信號(hào)量不初始化,這些結(jié)構(gòu)是在semctl,用參數(shù)SET_VAL,SETALL
初始化的。
semget函數(shù)執(zhí)行成功后,就產(chǎn)生了一個(gè)由內(nèi)核維持的類(lèi)型為semid_ds結(jié)構(gòu)體的信號(hào)量
集,返回semid就是指向該信號(hào)量集的引索。
(c)關(guān)鍵字的獲取
有多種方法使客戶(hù)機(jī)和服務(wù)器在同一IPC結(jié)構(gòu)上會(huì)合:
(1) 服務(wù)器可以指定關(guān)鍵字IPC_PRIVATE創(chuàng)建一個(gè)新IPC結(jié)構(gòu),將返回的標(biāo)識(shí)符存放在某
處(例如一個(gè)文件)以便客戶(hù)機(jī)取用。關(guān)鍵字 IPC_PRIVATE保證服務(wù)器創(chuàng)建一個(gè)新IPC結(jié)
構(gòu)。這種技術(shù)的缺點(diǎn)是:服務(wù)器要將整型標(biāo)識(shí)符寫(xiě)到文件中,然后客戶(hù)機(jī)在此后又要讀文件
取得此標(biāo)識(shí)符。
IPC_PRIVATE關(guān)鍵字也可用于父、子關(guān)系進(jìn)程。父進(jìn)程指定 IPC_PRIVATE創(chuàng)建一個(gè)新
IPC結(jié)構(gòu),所返回的標(biāo)識(shí)符在fork后可由子進(jìn)程使用。子進(jìn)程可將此標(biāo)識(shí)符作為exec函數(shù)
的一個(gè)參數(shù)傳給一個(gè)新程序。
(2) 在一個(gè)公用頭文件中定義一個(gè)客戶(hù)機(jī)和服務(wù)器都認(rèn)可的關(guān)鍵字。然后服務(wù)器指定此關(guān)鍵
字創(chuàng)建一個(gè)新的IPC結(jié)構(gòu)。這種方法的問(wèn)題是該關(guān)鍵字可能已與一個(gè) IPC結(jié)構(gòu)相結(jié)合,在
此情況下,get函數(shù)(msgget、semget或shmget)出錯(cuò)返回。服務(wù)器必須處理這一錯(cuò)誤,刪除
已存在的IPC結(jié)構(gòu),然后試著再創(chuàng)建它。當(dāng)然,這個(gè)關(guān)鍵字不能被別的程序所占用。
(3) 客戶(hù)機(jī)和服務(wù)器認(rèn)同一個(gè)路徑名和課題I D(課題I D是0 ~ 2 5 5之間的字符值) ,然
后調(diào)用函數(shù)ftok將這兩個(gè)值變換為一個(gè)關(guān)鍵字。這樣就避免了使用一個(gè)已被占用的關(guān)鍵字的
問(wèn)題。
使用ftok并非高枕無(wú)憂(yōu)。有這樣一種例外:服務(wù)器使用ftok獲取得一個(gè)關(guān)鍵字后,該文
件就被刪除了,然后重建。此時(shí)客戶(hù)端以此重建后的文件來(lái)ftok所獲取的關(guān)鍵字就和服務(wù)器
的關(guān)鍵字不一樣了。所以一般商用的軟件都不怎么用ftok。
一般來(lái)說(shuō),客戶(hù)機(jī)和服務(wù)器至少共享一個(gè)頭文件,所以一個(gè)比較簡(jiǎn)單的方法是避免使
用ftok,而只是在該頭文件中存放一個(gè)大家都知道的關(guān)鍵字。
(d)設(shè)置信號(hào)量的值(PV操作)
int semop(int semid, struct sembuf *opsptr, size_t nops);
(1) semid: 是semget返回的semid
(2)opsptr: 指向信號(hào)量操作結(jié)構(gòu)數(shù)組
(3) nops : opsptr所指向的數(shù)組中的sembuf結(jié)構(gòu)體的個(gè)數(shù)
struct sembuf {
short sem_num; // 要操作的信號(hào)量在信號(hào)量集里的編號(hào),
short sem_op; // 信號(hào)量操作
short sem_flg; // 操作表示符
};
(4) 若sem_op 是正數(shù),其值就加到semval上,即釋放信號(hào)量控制的資源
若sem_op 是0,那么調(diào)用者希望等到semval變?yōu)?,如果semval是0就返回;
若sem_op 是負(fù)數(shù),那么調(diào)用者希望等待semval變?yōu)榇笥诨虻扔趕em_op的絕對(duì)值
例如,當(dāng)前semval為2,而sem_op = -3,那么怎么辦?
注意:semval是指semid_ds中的信號(hào)量集中的某個(gè)信號(hào)量的值
(5) sem_flg
SEM_UNDO 由進(jìn)程自動(dòng)釋放信號(hào)量
IPC_NOWAIT 不阻塞
到這里,讀者肯定有個(gè)疑惑:semop希望改變的semval到底在哪里?我們?cè)趺礇](méi)看到
有它的痕跡?其實(shí),前面已經(jīng)說(shuō)明了,當(dāng)使用semget時(shí),就產(chǎn)生了一個(gè)由內(nèi)核維護(hù)的信號(hào)
量集(當(dāng)然每個(gè)信號(hào)量值即semval也是只由內(nèi)核才能看得到了),用戶(hù)能看到的就是返回
的semid。內(nèi)核通過(guò)semop 函數(shù)的參數(shù),知道應(yīng)該去改變semid 所指向的信號(hào)量的哪個(gè)
semval。
(e)對(duì)信號(hào)集實(shí)行控制操作(semval的賦值等)
int semctl(int semid, int semum, int cmd, ../* union semun arg */);
semid是信號(hào)量集合;
semnum是信號(hào)在集合中的序號(hào);
semum是一個(gè)必須由用戶(hù)自定義的結(jié)構(gòu)體,在這里我們務(wù)必弄清楚該結(jié)構(gòu)體的組成:
union semun
{
int val; // cmd == SETVAL
struct semid_ds *buf // cmd == IPC_SET或者 cmd == IPC_STAT
ushort *array; // cmd == SETALL,或 cmd = GETALL
};
val只有cmd ==SETVAL時(shí)才有用,此時(shí)指定的semval = arg.val。
注意:當(dāng)cmd == GETVAL時(shí),semctl函數(shù)返回的值就是我們想要的semval。千萬(wàn)不要
以為指定的semval被返回到arg.val中。
array指向一個(gè)數(shù)組,當(dāng)cmd==SETALL時(shí),就根據(jù)arg.array來(lái)將信號(hào)量集的所有值都
賦值;當(dāng)cmd ==GETALL時(shí),就將信號(hào)量集的所有值返回到arg.array指定的數(shù)組中。
buf 指針只在cmd==IPC_STAT 或IPC_SET 時(shí)有用,作用是semid 所指向的信號(hào)量集
(semid_ds機(jī)構(gòu)體)。一般情況下不常用,這里不做談?wù)摗?/span>
另外,cmd == IPC_RMID還是比較有用的。
(f)例碼
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <stdio.h>
static int nsems;
static int semflg;
static int semid;
int errno=0;
union semun {
int val;
struct semid_ds *buf;
unsigned short *array;
}arg;
int main()
{
struct sembuf sops[2]; //要用到兩個(gè)信號(hào)量,所以要定義兩個(gè)操作數(shù)組
int rslt;
unsigned short argarray[80];
arg.array = argarray;
semid = semget(IPC_PRIVATE, 2, 0666);
if(semid < 0 )
{
printf("semget failed. errno: %d\n", errno);
exit(0);
}
//獲取0th信號(hào)量的原始值
rslt = semctl(semid, 0, GETVAL);
printf("val = %d\n",rslt);
//初始化0th信號(hào)量,然后再讀取,檢查初始化有沒(méi)有成功
arg.val = 1; // 同一時(shí)間只允許一個(gè)占有者
semctl(semid, 0, SETVAL, arg);
rslt = semctl(semid, 0, GETVAL);
printf("val = %d\n",rslt);
sops[0].sem_num = 0;
sops[0].sem_op = -1;
sops[0].sem_flg = 0;
sops[1].sem_num = 1;
sops[1].sem_op = 1;
sops[1].sem_flg = 0;
rslt=semop(semid, sops, 1); //申請(qǐng)0th信號(hào)量,嘗試鎖定
if (rslt < 0 )
{
printf("semop failed. errno: %d\n", errno);
exit(0);
}
//可以在這里對(duì)資源進(jìn)行鎖定
sops[0].sem_op = 1;
semop(semid, sops, 1); //釋放0th信號(hào)量
rslt = semctl(semid, 0, GETVAL);
printf("val = %d\n",rslt);
rslt=semctl(semid, 0, GETALL, arg);
if (rslt < 0)
{
printf("semctl failed. errno: %d\n", errno);
exit(0);
}
printf("val1:%d val2: %d\n",(unsigned int)argarray[0],(unsigned int)argarray[1]);
if(semctl(semid, 1, IPC_RMID) == -1)
{
Perror(“semctl failure while clearing reason”);
}
return(0);
}
七.信號(hào)量的牛刀小試——生產(chǎn)者與消費(fèi)者問(wèn)題
1.問(wèn)題描述:
有一個(gè)長(zhǎng)度為N的緩沖池為生產(chǎn)者和消費(fèi)者所共有,只要緩沖池未滿(mǎn),生產(chǎn)者便可將
消息送入緩沖池;只要緩沖池未空,消費(fèi)者便可從緩沖池中取走一個(gè)消息。生產(chǎn)者往緩沖池
放信息的時(shí)候,消費(fèi)者不可操作緩沖池,反之亦然。
2.使用多線(xiàn)程和信號(hào)量解決該經(jīng)典問(wèn)題的互斥
#include <pthread.h>
#include <stdio.h>
#include <semaphore.h>
#define BUFF_SIZE 10
char buffer[BUFF_SIZE];
char count; // 緩沖池里的信息數(shù)目
sem_t sem_mutex; // 生產(chǎn)者和消費(fèi)者的互斥鎖
sem_t p_sem_mutex; // 空的時(shí)候,對(duì)消費(fèi)者不可進(jìn)
sem_t c_sem_mutex; // 滿(mǎn)的時(shí)候,對(duì)生產(chǎn)者不可進(jìn)
void * Producer()
{
while(1)
{
sem_wait(&p_sem_mutex); //當(dāng)緩沖池未滿(mǎn)時(shí)
sem_wait(&sem_mutex); //等待緩沖池空閑
count++;
sem_post(&sem_mutex);
if(count < BUFF_SIZE)//緩沖池未滿(mǎn)
sem_post(&p_sem_mutex);
if(count > 0) //緩沖池不為空
sem_post(&c_sem_mutex);
}
}
void * Consumer()
{
while(1)
{
sem_wait(&c_sem_mutex);//緩沖池未空時(shí)
sem_wait(&sem_mutex); //等待緩沖池空閑
count--;
sem_post(&sem_mutex);
if(count > 0)
sem_post(c_sem_nutex);
}
}
int main()
{
pthread_t ptid,ctid;
//initialize the semaphores
sem_init(&empty_sem_mutex,0,1);
sem_init(&full_sem_mutex,0,0);
//creating producer and consumer threads
if(pthread_create(&ptid, NULL,Producer, NULL))
{
printf("\n ERROR creating thread 1");
exit(1);
}
if(pthread_create(&ctid, NULL,Consumer, NULL))
{
printf("\n ERROR creating thread 2");
exit(1);
}
if(pthread_join(ptid, NULL)) /* wait for the producer to finish */
{
printf("\n ERROR joining thread");
exit(1);
}
if(pthread_join(ctid, NULL)) /* wait for consumer to finish */
{
printf("\n ERROR joining thread");
exit(1);
}
sem_destroy(&empty_sem_mutex);
sem_destroy(&full_sem_mutex);
//exit the main thread
pthread_exit(NULL);
return 1;
}
一.什么是信號(hào)量
信號(hào)量的使用主要是用來(lái)保護(hù)共享資源,使得資源在一個(gè)時(shí)刻只有一個(gè)進(jìn)程(線(xiàn)程)
所擁有。
信號(hào)量的值為正的時(shí)候,說(shuō)明它空閑。所測(cè)試的線(xiàn)程可以鎖定而使用它。若為0,說(shuō)明
它被占用,測(cè)試的線(xiàn)程要進(jìn)入睡眠隊(duì)列中,等待被喚醒。
二.信號(hào)量的分類(lèi)
在學(xué)習(xí)信號(hào)量之前,我們必須先知道——Linux提供兩種信號(hào)量:
(1) 內(nèi)核信號(hào)量,由內(nèi)核控制路徑使用
(2) 用戶(hù)態(tài)進(jìn)程使用的信號(hào)量,這種信號(hào)量又分為POSIX信號(hào)量和SYSTEM
V信號(hào)量。
POSIX信號(hào)量又分為有名信號(hào)量和無(wú)名信號(hào)量。
有名信號(hào)量,其值保存在文件中, 所以它可以用于線(xiàn)程也可以用于進(jìn)程間的同步。無(wú)名
信號(hào)量,其值保存在內(nèi)存中。
倘若對(duì)信號(hào)量沒(méi)有以上的全面認(rèn)識(shí)的話(huà),你就會(huì)很快發(fā)現(xiàn)自己在信號(hào)量的森林里迷
失了方向。
三.內(nèi)核信號(hào)量
1.內(nèi)核信號(hào)量的構(gòu)成
內(nèi)核信號(hào)量類(lèi)似于自旋鎖,因?yàn)楫?dāng)鎖關(guān)閉著時(shí),它不允許內(nèi)核控制路徑繼續(xù)進(jìn)行。然而,
當(dāng)內(nèi)核控制路徑試圖獲取內(nèi)核信號(hào)量鎖保護(hù)的忙資源時(shí),相應(yīng)的進(jìn)程就被掛起。只有在資源
被釋放時(shí),進(jìn)程才再次變?yōu)榭蛇\(yùn)行。
只有可以睡眠的函數(shù)才能獲取內(nèi)核信號(hào)量;中斷處理程序和可延遲函數(shù)都不能使用內(nèi)
核信號(hào)量。
內(nèi)核信號(hào)量是struct semaphore類(lèi)型的對(duì)象,它在<asm/semaphore.h>中定義:
struct semaphore {
atomic_t count;
int sleepers;
wait_queue_head_t wait;
}
count:相當(dāng)于信號(hào)量的值,大于0,資源空閑;等于0,資源忙,但沒(méi)有進(jìn)程等待這
個(gè)保護(hù)的資源;小于0,資源不可用,并至少有一個(gè)進(jìn)程等待資源。
wait:存放等待隊(duì)列鏈表的地址,當(dāng)前等待資源的所有睡眠進(jìn)程都會(huì)放在這個(gè)鏈表中。
sleepers:存放一個(gè)標(biāo)志,表示是否有一些進(jìn)程在信號(hào)量上睡眠。
2.內(nèi)核信號(hào)量中的等待隊(duì)列(刪除,沒(méi)有聯(lián)系)
上面已經(jīng)提到了內(nèi)核信號(hào)量使用了等待隊(duì)列wait_queue來(lái)實(shí)現(xiàn)阻塞操作。
當(dāng)某任務(wù)由于沒(méi)有某種條件沒(méi)有得到滿(mǎn)足時(shí),它就被掛到等待隊(duì)列中睡眠。當(dāng)條件得到滿(mǎn)足
時(shí),該任務(wù)就被移出等待隊(duì)列,此時(shí)并不意味著該任務(wù)就被馬上執(zhí)行,因?yàn)樗直灰七M(jìn)工
作隊(duì)列中等待CPU資源,在適當(dāng)?shù)臅r(shí)機(jī)被調(diào)度。
內(nèi)核信號(hào)量是在內(nèi)部使用等待隊(duì)列的,也就是說(shuō)該等待隊(duì)列對(duì)用戶(hù)是隱藏的,無(wú)須用
戶(hù)干涉。由用戶(hù)真正使用的等待隊(duì)列我們將在另外的篇章進(jìn)行詳解。
3.內(nèi)核信號(hào)量的相關(guān)函數(shù)
(1)初始化:
void sema_init (struct semaphore *sem, int val);
void init_MUTEX (struct semaphore *sem); //將sem的值置為1,表示資源空閑
void init_MUTEX_LOCKED (struct semaphore *sem); //將sem的值置為0,表示資源忙
(2)申請(qǐng)內(nèi)核信號(hào)量所保護(hù)的資源:
void down(struct semaphore * sem); // 可引起睡眠
int down_interruptible(struct semaphore * sem); // down_interruptible能被信號(hào)打斷
int down_trylock(struct semaphore * sem); // 非阻塞函數(shù),不會(huì)睡眠。無(wú)法鎖定資源則
馬上返回
(3)釋放內(nèi)核信號(hào)量所保護(hù)的資源:
void up(struct semaphore * sem);
4.內(nèi)核信號(hào)量的使用例程
在驅(qū)動(dòng)程序中,當(dāng)多個(gè)線(xiàn)程同時(shí)訪(fǎng)問(wèn)相同的資源時(shí)(驅(qū)動(dòng)中的全局變量時(shí)一種典型的
共享資源),可能會(huì)引發(fā)“競(jìng)態(tài)“,因此我們必須對(duì)共享資源進(jìn)行并發(fā)控制。Linux內(nèi)核中
解決并發(fā)控制的最常用方法是自旋鎖與信號(hào)量(絕大多數(shù)時(shí)候作為互斥鎖使用)。
ssize_t globalvar_write(struct file *filp, const char *buf, size_t len, loff_t *off)
{
//獲得信號(hào)量
if (down_interruptible(&sem))
{
return - ERESTARTSYS;
}
//將用戶(hù)空間的數(shù)據(jù)復(fù)制到內(nèi)核空間的global_var
if (copy_from_user(&global_var, buf, sizeof(int)))
{
up(&sem);
return - EFAULT;
}
//釋放信號(hào)量
up(&sem);
return sizeof(int);
}
四.POSIX 信號(hào)量與SYSTEM V信號(hào)量的比較
1. 對(duì)POSIX來(lái)說(shuō),信號(hào)量是個(gè)非負(fù)整數(shù)。常用于線(xiàn)程間同步。
而SYSTEM V信號(hào)量則是一個(gè)或多個(gè)信號(hào)量的集合,它對(duì)應(yīng)的是一個(gè)信號(hào)量結(jié)構(gòu)體,
這個(gè)結(jié)構(gòu)體是為SYSTEM V IPC服務(wù)的,信號(hào)量只不過(guò)是它的一部分。常用于進(jìn)程間同步。
2.POSIX信號(hào)量的引用頭文件是“<semaphore.h>”,而SYSTEM V信號(hào)量的引用頭文件是
“<sys/sem.h>”。
3.從使用的角度,System V信號(hào)量是復(fù)雜的,而Posix信號(hào)量是簡(jiǎn)單。比如,POSIX信
號(hào)量的創(chuàng)建和初始化或PV操作就很非常方便。
五.POSIX信號(hào)量詳解
1.無(wú)名信號(hào)量
無(wú)名信號(hào)量的創(chuàng)建就像聲明一般的變量一樣簡(jiǎn)單,例如:sem_t sem_id。然后再初
始化該無(wú)名信號(hào)量,之后就可以放心使用了。
無(wú)名信號(hào)量常用于多線(xiàn)程間的同步,同時(shí)也用于相關(guān)進(jìn)程間的同步。也就是說(shuō),無(wú)名信
號(hào)量必須是多個(gè)進(jìn)程(線(xiàn)程)的共享變量,無(wú)名信號(hào)量要保護(hù)的變量也必須是多個(gè)進(jìn)程
(線(xiàn)程)的共享變量,這兩個(gè)條件是缺一不可的。
常見(jiàn)的無(wú)名信號(hào)量相關(guān)函數(shù):sem_destroy
int sem_init(sem_t *sem, int pshared, unsigned int value);
1)pshared==0 用于同一多線(xiàn)程的同步;
2)若pshared>0 用于多個(gè)相關(guān)進(jìn)程間的同步(即由fork產(chǎn)生的)
int sem_getvalue(sem_t *sem, int *sval);
取回信號(hào)量sem的當(dāng)前值,把該值保存到sval中。
若有1個(gè)或更多的線(xiàn)程或進(jìn)程調(diào)用sem_wait阻塞在該信號(hào)量上,該函數(shù)返回兩種值:
1) 返回0
2) 返回阻塞在該信號(hào)量上的進(jìn)程或線(xiàn)程數(shù)目
linux采用返回的第一種策略。
sem_wait(或sem_trywait)相當(dāng)于P操作,即申請(qǐng)資源。
int sem_wait(sem_t *sem); // 這是一個(gè)阻塞的函數(shù)
測(cè)試所指定信號(hào)量的值,它的操作是原子的。
若sem>0,那么它減1并立即返回。
若sem==0,則睡眠直到sem>0,此時(shí)立即減1,然后返回。
int sem_trywait(sem_t *sem); // 非阻塞的函數(shù)
其他的行為和sem_wait一樣,除了:
若sem==0,不是睡眠,而是返回一個(gè)錯(cuò)誤EAGAIN。
sem_post相當(dāng)于V操作,釋放資源。
int sem_post(sem_t *sem);
把指定的信號(hào)量sem的值加1;
呼醒正在等待該信號(hào)量的任意線(xiàn)程。
注意:在這些函數(shù)中,只有sem_post是信號(hào)安全的函數(shù),它是可重入函數(shù)
(a)無(wú)名信號(hào)量在多線(xiàn)程間的同步
無(wú)名信號(hào)量的常見(jiàn)用法是將要保護(hù)的變量放在sem_wait和sem_post中間所形成的
臨界區(qū)內(nèi),這樣該變量就會(huì)被保護(hù)起來(lái),例如:
#include <pthread.h>
#include <semaphore.h>
#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
int number; // 被保護(hù)的全局變量
sem_t sem_id;
void* thread_one_fun(void *arg)
{
sem_wait(&sem_id);
printf("thread_one have the semaphore\n");
number++;
printf("number = %d\n",number);
sem_post(&sem_id);
}
void* thread_two_fun(void *arg)
{
sem_wait(&sem_id);
printf("thread_two have the semaphore \n");
number--;
printf("number = %d\n",number);
sem_post(&sem_id);
}
int main(int argc,char *argv[])
{
number = 1;
pthread_t id1, id2;
sem_init(&sem_id, 0, 1);
pthread_create(&id1,NULL,thread_one_fun, NULL);
pthread_create(&id2,NULL,thread_two_fun, NULL);
pthread_join(id1,NULL);
pthread_join(id2,NULL);
printf("main,,,\n");
return 0;
}
上面的例程,到底哪個(gè)線(xiàn)程先申請(qǐng)到信號(hào)量資源,這是隨機(jī)的。如果想要某個(gè)特定的順
序的話(huà),可以用2個(gè)信號(hào)量來(lái)實(shí)現(xiàn)。例如下面的例程是線(xiàn)程1先執(zhí)行完,然后線(xiàn)程2才繼
續(xù)執(zhí)行,直至結(jié)束。
int number; // 被保護(hù)的全局變量
sem_t sem_id1, sem_id2;
void* thread_one_fun(void *arg)
{
sem_wait(&sem_id1);
printf("thread_one have the semaphore\n");
number++;
printf("number = %d\n",number);
sem_post(&sem_id2);
}
void* thread_two_fun(void *arg)
{
sem_wait(&sem_id2);
printf("thread_two have the semaphore \n");
number--;
printf("number = %d\n",number);
sem_post(&sem_id1);
}
int main(int argc,char *argv[])
{
number = 1;
pthread_t id1, id2;
sem_init(&sem_id1, 0, 1); // 空閑的
sem_init(&sem_id2, 0, 0); // 忙的
pthread_create(&id1,NULL,thread_one_fun, NULL);
pthread_create(&id2,NULL,thread_two_fun, NULL);
pthread_join(id1,NULL);
pthread_join(id2,NULL);
printf("main,,,\n");
return 0;
}
(b)無(wú)名信號(hào)量在相關(guān)進(jìn)程間的同步
說(shuō)是相關(guān)進(jìn)程,是因?yàn)楸境绦蛑泄灿?個(gè)進(jìn)程,其中一個(gè)是另外一個(gè)的子進(jìn)程(由
fork
產(chǎn)生)的。
本來(lái)對(duì)于fork來(lái)說(shuō),子進(jìn)程只繼承了父進(jìn)程的代碼副本,mutex理應(yīng)在父子進(jìn)程
中是相互獨(dú)立的兩個(gè)變量,但由于在初始化mutex的時(shí)候,由pshared = 1指
定了mutex處于共享內(nèi)存區(qū)域,所以此時(shí)mutex變成了父子進(jìn)程共享的一個(gè)變
量。此時(shí),mutex就可以用來(lái)同步相關(guān)進(jìn)程了。
#include <semaphore.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
int main(int argc, char **argv)
{
int fd, i,count=0,nloop=10,zero=0,*ptr;
sem_t mutex;
//open a file and map it into memory
fd = open("log.txt",O_RDWR|O_CREAT,S_IRWXU);
write(fd,&zero,sizeof(int));
ptr = mmap( NULL,sizeof(int),PROT_READ |
PROT_WRITE,MAP_SHARED,fd,0 );
close(fd);
/* create, initialize semaphore */
if( sem_init(&mutex,1,1) < 0) //
{
perror("semaphore initilization");
exit(0);
}
if (fork() == 0)
{ /* child process*/
for (i = 0; i < nloop; i++)
{
sem_wait(&mutex);
printf("child: %d\n", (*ptr)++);
sem_post(&mutex);
}
exit(0);
}
/* back to parent process */
for (i = 0; i < nloop; i++)
{
sem_wait(&mutex);
printf("parent: %d\n", (*ptr)++);
sem_post(&mutex);
}
exit(0);
}
2.有名信號(hào)量
有名信號(hào)量的特點(diǎn)是把信號(hào)量的值保存在文件中。
這決定了它的用途非常廣:既可以用于線(xiàn)程,也可以用于相關(guān)進(jìn)程間,甚至是不相關(guān)
進(jìn)程。
(a)有名信號(hào)量能在進(jìn)程間共享的原因
由于有名信號(hào)量的值是保存在文件中的,所以對(duì)于相關(guān)進(jìn)程來(lái)說(shuō),子進(jìn)程是繼承了父
進(jìn)程的文件描述符,那么子進(jìn)程所繼承的文件描述符所指向的文件是和父進(jìn)程一樣的,當(dāng)
然文件里面保存的有名信號(hào)量值就共享了。
(b)有名信號(hào)量相關(guān)函數(shù)說(shuō)明
有名信號(hào)量在使用的時(shí)候,和無(wú)名信號(hào)量共享sem_wait和sem_post函數(shù)。
區(qū)別是有名信號(hào)量使用sem_open代替sem_init,另外在結(jié)束的時(shí)候要像關(guān)閉文件
一樣去關(guān)閉這個(gè)有名信號(hào)量。
(1)打開(kāi)一個(gè)已存在的有名信號(hào)量,或創(chuàng)建并初始化一個(gè)有名信號(hào)量。一個(gè)單一的調(diào)用就完
成了信號(hào)量的創(chuàng)建、初始化和權(quán)限的設(shè)置。
sem_t *sem_open(const char *name, int oflag, mode_t mode , int value);
name是文件的路徑名;
Oflag 有O_CREAT或O_CREAT|EXCL兩個(gè)取值;
mode_t控制新的信號(hào)量的訪(fǎng)問(wèn)權(quán)限;
Value指定信號(hào)量的初始化值。
注意:
這里的name不能寫(xiě)成/tmp/aaa.sem這樣的格式,因?yàn)樵趌inux下,sem都是創(chuàng)建
在/dev/shm目錄下。你可以將name寫(xiě)成“/mysem”或“mysem”,創(chuàng)建出來(lái)的文件都
是“/dev/shm/sem.mysem”,千萬(wàn)不要寫(xiě)路徑。也千萬(wàn)不要寫(xiě)“/tmp/mysem”之類(lèi)的。
當(dāng)oflag = O_CREAT時(shí),若name指定的信號(hào)量不存在時(shí),則會(huì)創(chuàng)建一個(gè),而且后
面的mode和value參數(shù)必須有效。若name指定的信號(hào)量已存在,則直接打開(kāi)該信號(hào)量,
同時(shí)忽略mode和value參數(shù)。
當(dāng)oflag = O_CREAT|O_EXCL時(shí),若name指定的信號(hào)量已存在,該函數(shù)會(huì)直接返
回error。
(2) 一旦你使用了一信號(hào)量,銷(xiāo)毀它們就變得很重要。
在做這個(gè)之前,要確定所有對(duì)這個(gè)有名信號(hào)量的引用都已經(jīng)通過(guò)sem_close()函數(shù)
關(guān)閉了,然后只需在退出或是退出處理函數(shù)中調(diào)用sem_unlink()去刪除系統(tǒng)中的信號(hào)量,
注意如果有任何的處理器或是線(xiàn)程引用這個(gè)信號(hào)量,sem_unlink()函數(shù)不會(huì)起到任何的作
用。
也就是說(shuō),必須是最后一個(gè)使用該信號(hào)量的進(jìn)程來(lái)執(zhí)行sem_unlick才有效。因?yàn)槊總€(gè)
信號(hào)燈有一個(gè)引用計(jì)數(shù)器記錄當(dāng)前的打開(kāi)次數(shù),sem_unlink必須等待這個(gè)數(shù)為0時(shí)才能把
name所指的信號(hào)燈從文件系統(tǒng)中刪除。也就是要等待最后一個(gè)sem_close發(fā)生。
(c)有名信號(hào)量在無(wú)相關(guān)進(jìn)程間的同步
前面已經(jīng)說(shuō)過(guò),有名信號(hào)量是位于共享內(nèi)存區(qū)的,那么它要保護(hù)的資源也必須是位于
共享內(nèi)存區(qū),只有這樣才能被無(wú)相關(guān)的進(jìn)程所共享。
在下面這個(gè)例子中,服務(wù)進(jìn)程和客戶(hù)進(jìn)程都使用shmget和shmat來(lái)獲取得一塊共享內(nèi)
存資源。然后利用有名信號(hào)量來(lái)對(duì)這塊共享內(nèi)存資源進(jìn)行互斥保護(hù)。
<u>File1: server.c </u>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <semaphore.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define SHMSZ 27
char SEM_NAME[]= "vik";
int main()
{
char ch;
int shmid;
key_t key;
char *shm,*s;
sem_t *mutex;
//name the shared memory segment
key = 1000;
//create & initialize semaphore
mutex = sem_open(SEM_NAME,O_CREAT,0644,1);
if(mutex == SEM_FAILED)
{
perror("unable to create semaphore");
sem_unlink(SEM_NAME);
exit(-1);
}
//create the shared memory segment with this key
shmid = shmget(key,SHMSZ,IPC_CREAT|0666);
if(shmid<0)
{
perror("failure in shmget");
exit(-1);
}
//attach this segment to virtual memory
shm = shmat(shmid,NULL,0);
//start writing into memory
s = shm;
for(ch='A';ch<='Z';ch++)
{
sem_wait(mutex);
*s++ = ch;
sem_post(mutex);
}
//the below loop could be replaced by binary semaphore
while(*shm != '*')
{
sleep(1);
}
sem_close(mutex);
sem_unlink(SEM_NAME);
shmctl(shmid, IPC_RMID, 0);
exit(0);
}
<u>File 2: client.c</u>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <semaphore.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define SHMSZ 27
char SEM_NAME[]= "vik";
int main()
{
char ch;
int shmid;
key_t key;
char *shm,*s;
sem_t *mutex;
//name the shared memory segment
key = 1000;
//create & initialize existing semaphore
mutex = sem_open(SEM_NAME,0,0644,0);
if(mutex == SEM_FAILED)
{
perror("reader:unable to execute semaphore");
sem_close(mutex);
exit(-1);
}
//create the shared memory segment with this key
shmid = shmget(key,SHMSZ,0666);
if(shmid<0)
{
perror("reader:failure in shmget");
exit(-1);
}
//attach this segment to virtual memory
shm = shmat(shmid,NULL,0);
//start reading
s = shm;
for(s=shm;*s!=NULL;s++)
{
sem_wait(mutex);
putchar(*s);
sem_post(mutex);
}
//once done signal exiting of reader:This can be replaced by
another semaphore
*shm = '*';
sem_close(mutex);
shmctl(shmid, IPC_RMID, 0);
exit(0);
}
六.SYSTEM V信號(hào)量
這是信號(hào)量值的集合,而不是單個(gè)信號(hào)量。相關(guān)的信號(hào)量操作函數(shù)由<sys/ipc.h>引用。
1.信號(hào)量結(jié)構(gòu)體
內(nèi)核為每個(gè)信號(hào)量集維護(hù)一個(gè)信號(hào)量結(jié)構(gòu)體,可在<sys/sem.h>找到該定義:
struct semid_ds {
struct ipc_perm sem_perm; /* 信號(hào)量集的操作許可權(quán)限 */
struct sem *sem_base; /* 某個(gè)信號(hào)量sem結(jié)構(gòu)數(shù)組的指針,當(dāng)前信號(hào)量集
中的每個(gè)信號(hào)量對(duì)應(yīng)其中一個(gè)數(shù)組元素 */
ushort sem_nsems; /* sem_base 數(shù)組的個(gè)數(shù) */
time_t sem_otime; /* 最后一次成功修改信號(hào)量數(shù)組的時(shí)間 */
time_t sem_ctime; /* 成功創(chuàng)建時(shí)間 */
};
struct sem {
ushort semval; /* 信號(hào)量的當(dāng)前值 */
short sempid; /* 最后一次返回該信號(hào)量的進(jìn)程ID 號(hào) */
ushort semncnt; /* 等待semval大于當(dāng)前值的進(jìn)程個(gè)數(shù) */
ushort semzcnt; /* 等待semval變成0的進(jìn)程個(gè)數(shù) */
};
2.常見(jiàn)的SYSTEM V信號(hào)量函數(shù)
(a)關(guān)鍵字和描述符
SYSTEM V信號(hào)量是SYSTEM V IPC(即SYSTEM V進(jìn)程間通信)的組成部分,其他
的有SYSTEM V消息隊(duì)列,SYSTEM V共享內(nèi)存。而關(guān)鍵字和IPC描述符無(wú)疑是它們的共
同點(diǎn),也使用它們,就不得不先對(duì)它們進(jìn)行熟悉。這里只對(duì)SYSTEM V信號(hào)量進(jìn)行討論。
IPC描述符相當(dāng)于引用ID號(hào),要想使用SYSTEM V信號(hào)量(或MSG、SHM),就必須
用IPC描述符來(lái)調(diào)用信號(hào)量。而IPC描述符是內(nèi)核動(dòng)態(tài)提供的(通過(guò)semget來(lái)獲取),用
戶(hù)無(wú)法讓服務(wù)器和客戶(hù)事先認(rèn)可共同使用哪個(gè)描述符,所以有時(shí)候就需要到關(guān)鍵字KEY來(lái)
定位描述符。
某個(gè)KEY只會(huì)固定對(duì)應(yīng)一個(gè)描述符(這項(xiàng)轉(zhuǎn)換工作由內(nèi)核完成),這樣假如服務(wù)器和
客戶(hù)事先認(rèn)可共同使用某個(gè)KEY,那么大家就都能定位到同一個(gè)描述符,也就能定位到同
一個(gè)信號(hào)量,這樣就達(dá)到了SYSTEM V信號(hào)量在進(jìn)程間共享的目的。
(b)創(chuàng)建和打開(kāi)信號(hào)量
int semget(key_t key, int nsems, int oflag)
(1) nsems>0 : 創(chuàng)建一個(gè)信的信號(hào)量集,指定集合中信號(hào)量的數(shù)量,一旦創(chuàng)建就不能更改。
(2) nsems==0 : 訪(fǎng)問(wèn)一個(gè)已存在的集合
(3) 返回的是一個(gè)稱(chēng)為信號(hào)量標(biāo)識(shí)符的整數(shù),semop和semctl函數(shù)將使用它。
(4) 創(chuàng)建成功后信號(hào)量結(jié)構(gòu)被設(shè)置:
.sem_perm 的uid和gid成員被設(shè)置成的調(diào)用進(jìn)程的有效用戶(hù)ID和有效組ID
.oflag 參數(shù)中的讀寫(xiě)權(quán)限位存入sem_perm.mode
.sem_otime 被置為0,sem_ctime被設(shè)置為當(dāng)前時(shí)間
.sem_nsems 被置為nsems參數(shù)的值
該集合中的每個(gè)信號(hào)量不初始化,這些結(jié)構(gòu)是在semctl,用參數(shù)SET_VAL,SETALL
初始化的。
semget函數(shù)執(zhí)行成功后,就產(chǎn)生了一個(gè)由內(nèi)核維持的類(lèi)型為semid_ds結(jié)構(gòu)體的信號(hào)量
集,返回semid就是指向該信號(hào)量集的引索。
(c)關(guān)鍵字的獲取
有多種方法使客戶(hù)機(jī)和服務(wù)器在同一IPC結(jié)構(gòu)上會(huì)合:
(1) 服務(wù)器可以指定關(guān)鍵字IPC_PRIVATE創(chuàng)建一個(gè)新IPC結(jié)構(gòu),將返回的標(biāo)識(shí)符存放在某
處(例如一個(gè)文件)以便客戶(hù)機(jī)取用。關(guān)鍵字 IPC_PRIVATE保證服務(wù)器創(chuàng)建一個(gè)新IPC結(jié)
構(gòu)。這種技術(shù)的缺點(diǎn)是:服務(wù)器要將整型標(biāo)識(shí)符寫(xiě)到文件中,然后客戶(hù)機(jī)在此后又要讀文件
取得此標(biāo)識(shí)符。
IPC_PRIVATE關(guān)鍵字也可用于父、子關(guān)系進(jìn)程。父進(jìn)程指定 IPC_PRIVATE創(chuàng)建一個(gè)新
IPC結(jié)構(gòu),所返回的標(biāo)識(shí)符在fork后可由子進(jìn)程使用。子進(jìn)程可將此標(biāo)識(shí)符作為exec函數(shù)
的一個(gè)參數(shù)傳給一個(gè)新程序。
(2) 在一個(gè)公用頭文件中定義一個(gè)客戶(hù)機(jī)和服務(wù)器都認(rèn)可的關(guān)鍵字。然后服務(wù)器指定此關(guān)鍵
字創(chuàng)建一個(gè)新的IPC結(jié)構(gòu)。這種方法的問(wèn)題是該關(guān)鍵字可能已與一個(gè) IPC結(jié)構(gòu)相結(jié)合,在
此情況下,get函數(shù)(msgget、semget或shmget)出錯(cuò)返回。服務(wù)器必須處理這一錯(cuò)誤,刪除
已存在的IPC結(jié)構(gòu),然后試著再創(chuàng)建它。當(dāng)然,這個(gè)關(guān)鍵字不能被別的程序所占用。
(3) 客戶(hù)機(jī)和服務(wù)器認(rèn)同一個(gè)路徑名和課題I D(課題I D是0 ~ 2 5 5之間的字符值) ,然
后調(diào)用函數(shù)ftok將這兩個(gè)值變換為一個(gè)關(guān)鍵字。這樣就避免了使用一個(gè)已被占用的關(guān)鍵字的
問(wèn)題。
使用ftok并非高枕無(wú)憂(yōu)。有這樣一種例外:服務(wù)器使用ftok獲取得一個(gè)關(guān)鍵字后,該文
件就被刪除了,然后重建。此時(shí)客戶(hù)端以此重建后的文件來(lái)ftok所獲取的關(guān)鍵字就和服務(wù)器
的關(guān)鍵字不一樣了。所以一般商用的軟件都不怎么用ftok。
一般來(lái)說(shuō),客戶(hù)機(jī)和服務(wù)器至少共享一個(gè)頭文件,所以一個(gè)比較簡(jiǎn)單的方法是避免使
用ftok,而只是在該頭文件中存放一個(gè)大家都知道的關(guān)鍵字。
(d)設(shè)置信號(hào)量的值(PV操作)
int semop(int semid, struct sembuf *opsptr, size_t nops);
(1) semid: 是semget返回的semid
(2)opsptr: 指向信號(hào)量操作結(jié)構(gòu)數(shù)組
(3) nops : opsptr所指向的數(shù)組中的sembuf結(jié)構(gòu)體的個(gè)數(shù)
struct sembuf {
short sem_num; // 要操作的信號(hào)量在信號(hào)量集里的編號(hào),
short sem_op; // 信號(hào)量操作
short sem_flg; // 操作表示符
};
(4) 若sem_op 是正數(shù),其值就加到semval上,即釋放信號(hào)量控制的資源
若sem_op 是0,那么調(diào)用者希望等到semval變?yōu)?,如果semval是0就返回;
若sem_op 是負(fù)數(shù),那么調(diào)用者希望等待semval變?yōu)榇笥诨虻扔趕em_op的絕對(duì)值
例如,當(dāng)前semval為2,而sem_op = -3,那么怎么辦?
注意:semval是指semid_ds中的信號(hào)量集中的某個(gè)信號(hào)量的值
(5) sem_flg
SEM_UNDO 由進(jìn)程自動(dòng)釋放信號(hào)量
IPC_NOWAIT 不阻塞
到這里,讀者肯定有個(gè)疑惑:semop希望改變的semval到底在哪里?我們?cè)趺礇](méi)看到
有它的痕跡?其實(shí),前面已經(jīng)說(shuō)明了,當(dāng)使用semget時(shí),就產(chǎn)生了一個(gè)由內(nèi)核維護(hù)的信號(hào)
量集(當(dāng)然每個(gè)信號(hào)量值即semval也是只由內(nèi)核才能看得到了),用戶(hù)能看到的就是返回
的semid。內(nèi)核通過(guò)semop 函數(shù)的參數(shù),知道應(yīng)該去改變semid 所指向的信號(hào)量的哪個(gè)
semval。
(e)對(duì)信號(hào)集實(shí)行控制操作(semval的賦值等)
int semctl(int semid, int semum, int cmd, ../* union semun arg */);
semid是信號(hào)量集合;
semnum是信號(hào)在集合中的序號(hào);
semum是一個(gè)必須由用戶(hù)自定義的結(jié)構(gòu)體,在這里我們務(wù)必弄清楚該結(jié)構(gòu)體的組成:
union semun
{
int val; // cmd == SETVAL
struct semid_ds *buf // cmd == IPC_SET或者 cmd == IPC_STAT
ushort *array; // cmd == SETALL,或 cmd = GETALL
};
val只有cmd ==SETVAL時(shí)才有用,此時(shí)指定的semval = arg.val。
注意:當(dāng)cmd == GETVAL時(shí),semctl函數(shù)返回的值就是我們想要的semval。千萬(wàn)不要
以為指定的semval被返回到arg.val中。
array指向一個(gè)數(shù)組,當(dāng)cmd==SETALL時(shí),就根據(jù)arg.array來(lái)將信號(hào)量集的所有值都
賦值;當(dāng)cmd ==GETALL時(shí),就將信號(hào)量集的所有值返回到arg.array指定的數(shù)組中。
buf 指針只在cmd==IPC_STAT 或IPC_SET 時(shí)有用,作用是semid 所指向的信號(hào)量集
(semid_ds機(jī)構(gòu)體)。一般情況下不常用,這里不做談?wù)摗?/span>
另外,cmd == IPC_RMID還是比較有用的。
(f)例碼
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <stdio.h>
static int nsems;
static int semflg;
static int semid;
int errno=0;
union semun {
int val;
struct semid_ds *buf;
unsigned short *array;
}arg;
int main()
{
struct sembuf sops[2]; //要用到兩個(gè)信號(hào)量,所以要定義兩個(gè)操作數(shù)組
int rslt;
unsigned short argarray[80];
arg.array = argarray;
semid = semget(IPC_PRIVATE, 2, 0666);
if(semid < 0 )
{
printf("semget failed. errno: %d\n", errno);
exit(0);
}
//獲取0th信號(hào)量的原始值
rslt = semctl(semid, 0, GETVAL);
printf("val = %d\n",rslt);
//初始化0th信號(hào)量,然后再讀取,檢查初始化有沒(méi)有成功
arg.val = 1; // 同一時(shí)間只允許一個(gè)占有者
semctl(semid, 0, SETVAL, arg);
rslt = semctl(semid, 0, GETVAL);
printf("val = %d\n",rslt);
sops[0].sem_num = 0;
sops[0].sem_op = -1;
sops[0].sem_flg = 0;
sops[1].sem_num = 1;
sops[1].sem_op = 1;
sops[1].sem_flg = 0;
rslt=semop(semid, sops, 1); //申請(qǐng)0th信號(hào)量,嘗試鎖定
if (rslt < 0 )
{
printf("semop failed. errno: %d\n", errno);
exit(0);
}
//可以在這里對(duì)資源進(jìn)行鎖定
sops[0].sem_op = 1;
semop(semid, sops, 1); //釋放0th信號(hào)量
rslt = semctl(semid, 0, GETVAL);
printf("val = %d\n",rslt);
rslt=semctl(semid, 0, GETALL, arg);
if (rslt < 0)
{
printf("semctl failed. errno: %d\n", errno);
exit(0);
}
printf("val1:%d val2: %d\n",(unsigned int)argarray[0],(unsigned int)argarray[1]);
if(semctl(semid, 1, IPC_RMID) == -1)
{
Perror(“semctl failure while clearing reason”);
}
return(0);
}
七.信號(hào)量的牛刀小試——生產(chǎn)者與消費(fèi)者問(wèn)題
1.問(wèn)題描述:
有一個(gè)長(zhǎng)度為N的緩沖池為生產(chǎn)者和消費(fèi)者所共有,只要緩沖池未滿(mǎn),生產(chǎn)者便可將
消息送入緩沖池;只要緩沖池未空,消費(fèi)者便可從緩沖池中取走一個(gè)消息。生產(chǎn)者往緩沖池
放信息的時(shí)候,消費(fèi)者不可操作緩沖池,反之亦然。
2.使用多線(xiàn)程和信號(hào)量解決該經(jīng)典問(wèn)題的互斥
#include <pthread.h>
#include <stdio.h>
#include <semaphore.h>
#define BUFF_SIZE 10
char buffer[BUFF_SIZE];
char count; // 緩沖池里的信息數(shù)目
sem_t sem_mutex; // 生產(chǎn)者和消費(fèi)者的互斥鎖
sem_t p_sem_mutex; // 空的時(shí)候,對(duì)消費(fèi)者不可進(jìn)
sem_t c_sem_mutex; // 滿(mǎn)的時(shí)候,對(duì)生產(chǎn)者不可進(jìn)
void * Producer()
{
while(1)
{
sem_wait(&p_sem_mutex); //當(dāng)緩沖池未滿(mǎn)時(shí)
sem_wait(&sem_mutex); //等待緩沖池空閑
count++;
sem_post(&sem_mutex);
if(count < BUFF_SIZE)//緩沖池未滿(mǎn)
sem_post(&p_sem_mutex);
if(count > 0) //緩沖池不為空
sem_post(&c_sem_mutex);
}
}
void * Consumer()
{
while(1)
{
sem_wait(&c_sem_mutex);//緩沖池未空時(shí)
sem_wait(&sem_mutex); //等待緩沖池空閑
count--;
sem_post(&sem_mutex);
if(count > 0)
sem_post(c_sem_nutex);
}
}
int main()
{
pthread_t ptid,ctid;
//initialize the semaphores
sem_init(&empty_sem_mutex,0,1);
sem_init(&full_sem_mutex,0,0);
//creating producer and consumer threads
if(pthread_create(&ptid, NULL,Producer, NULL))
{
printf("\n ERROR creating thread 1");
exit(1);
}
if(pthread_create(&ctid, NULL,Consumer, NULL))
{
printf("\n ERROR creating thread 2");
exit(1);
}
if(pthread_join(ptid, NULL)) /* wait for the producer to finish */
{
printf("\n ERROR joining thread");
exit(1);
}
if(pthread_join(ctid, NULL)) /* wait for consumer to finish */
{
printf("\n ERROR joining thread");
exit(1);
}
sem_destroy(&empty_sem_mutex);
sem_destroy(&full_sem_mutex);
//exit the main thread
pthread_exit(NULL);
return 1;
}
總結(jié)
以上是生活随笔為你收集整理的最全面的linux信号量解析的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Linux内核引导简析
- 下一篇: Linux驱动设计之信号量