semaphore很類似windows下的kernel object,一旦建立,可以被多個進程使用,當然一個進程中多個線程也可以使用semaphore來互斥。談到互斥,一般semaphore的值就是 1。man 7 sem_overview可以看到很多有用的資訊,再結合sem_open, sem_wait, sem_post這些函數的man手冊,就可以把程式編的七七八八了。
Semaphore和pipe一樣,也有有名的和無名的兩種。無名的嘛,在父子進程間使用比較方便,有名的嘛,在不搭界的多進程中使用很方便。
這裡貼一段代碼,代碼最能說明問題,其他都參考manual就OK了。
在Fix TI的一個bug的時候,做了這樣的互斥。首先在ResourceManager中建立一個semaphore,因為ResourceManager以daemon程式的方式,一開始就會啟動:
-
Code: Select all
-
/* Change umask temporarily to make sure the correct permission */
old_mask = umask(0);
alp_mmsem = sem_open(ALP_MMSEM_NAME, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH, 1);
if (alp_mmsem == SEM_FAILED) {
fprintf(stderr, "ALP create mm semaphore failed. Name: %s, Reason: %s\n",
ALP_MMSEM_NAME, strerror(errno));
unlink(RM_SERVER_IN);
return 1;
}
umask(old_mask);
用完釋放:
-
Code: Select all
-
if (alp_mmsem != NULL) {
sem_close(alp_mmsem);
sem_unlink(ALP_MMSEM_NAME);
}
1. 使用umask將建立出來的檔案許可權要設定正確了。因為後續所有不是root的進程都要能讀寫這個有名semaphore才行。sem_open和 open很類似,建立出來的檔案,許可權是我們指定的許可權,也就是上面代碼中的:S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH 再與上 umask的非。就是:<我們設定的許可權> & ~umask。所以這裡我們將umask設成0,非一下就是全1,這樣保證出來的檔案就是我們設定的那個許可權。Linux預設的umask是022,所以 如果要設定的許可權是666的話,與上~022,出來的就是644了。umask的傳回值是上次的umask值,所以這裡代碼裡在設定完我們的檔案許可權後, 再把umask恢複回去,不影響後續的代碼。
2. 非常重要的是,sem_open的第一個參數,是這個有名sem的path,這裡path不能寫成/tmp/aaa.sem這樣的格式,因為Linux 下,sem都是建立在/dev/shm目錄下的(Linux的/dev/shm是一個tmpfs類型的特殊fs,就像/proc, /sys一樣,用mount命令就可以看到),而且會加上sem.這樣的prefix,所以,這裡我們的ALP_MMSEM_NAME的值是 /alpmm,這樣建立出來的檔案就是/dev/shm/sem.alpmm了,千萬不要寫路徑哦。名字可以以/打頭,不以/打頭也是可以的。
3. 和windows的kernel object一樣,sem也有counter,只有所有open了該sem的進程都sem_close了,該sem才會真正被核心銷毀。使用sem_unlink就可以刪掉該sem對應的檔案。
創 建好了,就可以使用了,和windows kernel object一樣,使用也是調用sem_open首先得到一個sem的handle(當然這裡flag就不能寫O_CREAT了),然後sem_wait 就是將該sem的值減1,由於sem定義規定了sem的值不能小於0,所以如果sem_wait之後的sem的值小於1的話,sem_wait就會 block,直到有人增加了sem的值才會return,這樣達到互斥的效果。sem_post就是給sem的值加1。
-
Code: Select all
-
if (alp_mmsem == NULL) /* Avoid create semaphore multi times */
{
alp_mmsem = sem_open(ALP_MMSEM_NAME, O_RDWR);
if (alp_mmsem == SEM_FAILED) {
/* How can we do now? */
fprintf(stderr, "ALP LCML open semaphore %s failed. Reason: %s\n",
ALP_MMSEM_NAME, strerror(errno));
return OMX_ErrorUndefined; /* FIXME, We should define our OMX ErrorCode here */
}
}......
if (sem_wait(alp_mmsem) == -1) {
fprintf(stderr, "ALP LCML sem_wait failed. Reason: %s\n", strerror(errno));
return OMX_ErrorUndefined;
}
......
if (sem_post(alp_mmsem) == -1) {
fprintf(stderr, "ALP LCML sem_post failed, this is FATAL, maybe cause deadlock. Reason: %s\n", strerror(errno));
return OMX_ErrorUndefined;
}
使用非常方便,記得用完之後sem_close。
基本上就是這樣了。不過注意使用無名semaphore的話,API和這些是不一樣的,諸如有sem_init這樣的API存在。所以如果使用無名sem的話,請參考linux manual。