semaphore很類似windows下的kernel object,一旦建立,可以被多個進程使用,當然一個進程中多個線程也可以使用semaphore來互斥。談到互斥,一般semaphore的值就是1。man 7 sem_overview可以看到很多有用的資訊。
Semaphore和pipe一樣,也有有名的和無名的兩種。無名的嘛,在父子進程間使用比較方便,有名的嘛,在"不搭界"的多進程中使用很方便。
這裡貼一段代碼,代碼最能說明問題,其他都參考manual就OK了。
/* Change umask temporarily to make sure the correct permission */
old_mask = umask(0);
super_mmsem = sem_open("/supermm", O_CREAT | O_RDWR, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH, 1);
if (super_mmsem == SEM_FAILED) {
fprintf(stderr, "Create mm semaphore failed. Name: %s, Reason: %s\n", "/supermm", strerror(errno));
return 1;
}
umask(old_mask);
............
/* Release after used */
if (super_mmsem != NULL) {
sem_close(super_mmsem);
sem_unlink("/supermm");
}
1. 使用umask將建立出來的檔案許可權要設定正確了。因為後續所有不是root的進程都要能讀寫這個有名semaphore才行。sem_open和open很類似,建立出來的檔案,許可權是我們指定的許可權,也就是上面代碼中的:S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH 再與上 umask的非。就是:<我們設定的許可權> & ~umask。這就是為什麼調用umask(0)的原因。Linux預設的umask是022,所以如果要設定的許可權是666的話,與上~022,出來的就是644了。
2. sem_open的第一個參數需要注意,是這個有名sem的path,這裡path不能寫成/tmp/aaa.sem這樣的格式,因為Linux下,sem都是建立在/dev/shm目錄下的(Linux的/dev/shm是一個tmpfs類型的特殊fs,就像/proc, /sys一樣,用mount命令就可以看到),而且會加上sem.這樣的prefix,所以,這裡代碼裡的值是/supermm,這樣建立出來的檔案就是/dev/shm/sem.supermm了,千萬不要寫路徑。名字可以以/打頭,不以/打頭也是可以的。
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。
if (super_mmsem == NULL) /* Avoid create semaphore multi times */
{
super_mmsem = sem_open("/supermm", O_RDWR);
if (super_mmsem == SEM_FAILED) {
/* How can we do now? */
fprintf(stderr, "Open semaphore %s failed. Reason: %s\n", "/supermm", strerror(errno));
return 1;
}
}
......
if (sem_wait(super_mmsem) == -1) {
fprintf(stderr, "Call sem_wait failed. Reason: %s\n", strerror(errno));
return 1;
}
......
if (sem_post(super_mmsem) == -1) {
fprintf(stderr, "Call sem_post failed, this is FATAL, maybe cause deadlock. Reason: %s\n", strerror(errno));
return 1;
}
使用非常方便,記得用完之後sem_close。
That's it。不過注意使用無名semaphore的話,API和這些是不一樣的,諸如有sem_init這樣的API存在。所以如果使用無名sem的話,請參考linux manual。