Linux信號量函數

      網友投稿 1005 2025-03-31

      1 信號量的概念


      信號量,或稱信號燈,其原理是一種數據操作鎖的概念,本身不具備數據交換的功能,它負責協調各個進程,保證保證兩個或多個關鍵代碼段不被并發調用,確保公共資源的合理使用。信號量分為單值和多值兩種。

      在信號的實際應用中,是不能只定義一個信號量的,而只能定義一個信號量集,其中包含一組信號量,同一信號量集中的信號量使用同一個引用ID,這樣的設置是為了多個資源或同步操作的需要。每個信號量集都有一個與之對應的結構,其中記錄了信號量集的各種信息,該結構定義為:

      struct semid_ds { struct ipc_perm sem_perm; // operation permission struct struct sem *sem_base; // ptr to first semaphore in set unsigned short sem_nsems; // numbers of semaphore in set time_t sem_otime; // last semop time time_t sem_ctime; // last change time };

      ipc_perm結構體包含了信號量集的鍵值、所有者信息及操作權限等,定義如下:

      struct ipc_perm { __key_t __key; // key value __uid_t uid; // owner uid __gid_t gid; // owner gid __uid_t cuid; // creater uid __gid_t cgid; // creater gid unsigned short int mode; // read/write permission unsigned short int __seq; // seq number ...... };

      sem結構體記錄了一個信號量的信息,定義如下:

      struct sem { unsigned short semval; // semaphore value, always >= 0 pid_t sempid; // pid for last operation unsigned short semncent; // numbers of processes awaiting semval > currval unsigned short semzcnt; // numbers of processes awaiting semval = 0 };

      2 信號量集的相關操作

      2.1 創建/打開信號量集

      使用semget()函數創建或打開一個信號量集,其函數原型為:

      #include #include #include int semget(key_t key, int nsems, int flag);

      運行成功返回信號量集的ID號,失敗返回-1。

      參數key為一個鍵值,可通過ftok()函數生成,參數nsems為創建的信號量集中包含的信號量個數,參數flag為操作參數,具體使用方法與創建共享內存的shmget()使用類似。

      2.2 對信號量集的操作

      使用semop()函數操作一個信號量集,其函數原型為:

      #include #include #include int semop(int semid, struct sembuf semoparray[], size_t nops);

      運行成功返回0,失敗返回-1。

      參數semid為semget()函數返回的信號量集ID號,參數semoparray是一個struct sembuf結構類型的數組,參數nops為前一數組參數的元素個數,sembuf的定義如下:

      struct sembuf { unsigned short sem_num; short sem_op; short sem_flg; }

      其中sem_num是信號集中的某一資源(即指定操作的信號量),其取值為0到資源總數(ipc_perm.sem_nsems)之間的整數;sem_op為一個整數,指明要執行的操作;sem_flg說明函數semop()的行為。

      Linux信號量及函數

      sem_op > 0:表示進程對資源使用完畢,釋放相應的資源數,并將sem_op的值加到信號量的值上。

      sem_op = 0:進程阻塞直到信號量的相應值為0,當信號量已經為0,函數立即返;信號量值不為0,則依據sem_flg的IPC_NOWAIT位決定函數動作:

      sem_flg指定IPC_NOWAIT,則semop()函數出錯返回EAGAIN;

      sem_flg沒有指定IPC_NOWAIT,則將信號量的semncnt的值減1,然后進程掛起直到下述情況發生:

      信號量為0,則將信號量的semncnt的值加1,函數semop()成功返回;

      信號量被刪除,函數semop()出錯返回EIDRM;

      進程捕捉到信號,并從信號處理函數返回,信號量的semncnt的值減1,函數semop()出錯返回EINTR。

      sem_op < 0:請求sem_op的絕對值的資源,若相應的資源數可以滿足請求,則將該信號量的值減去sem_op的絕對值,函數成功返回;若資源數不足,這個操作與sem_flg有關:

      sem_flg指定IPC_NOWAIT,則semop()函數出錯返回EAGAIN;

      sem_flg沒有指定IPC_NOWAIT,則將信號量的semncnt的值加1,然后進程掛起直到下述情況發生:

      資源數滿足請求,信號值減去sem_op的絕對值,成功返回;

      信號量被刪除,函數semop()出錯返回EIDRM;

      進程捕捉到信號,并從信號處理函數返回,信號量的semncnt的值減1,函數semop()出錯返回EINTR。

      2.3 信號量集的控制

      與共享內存的控制相似,信號量集也有自己的控制函數semctl(),函數原型如下:

      #include #include #include int semctl(int semid, int semnum, int cmd, union semun arg);

      運行成功返回大于等于0的值,失敗返回-1,并設置錯誤變量errno。

      參數semid為semget()函數返回的信號量集ID號,參數semnum指定信號量集中的某一信號量,類似于下標索引,參數cmd定義函數的操作,具體含義與后面的參數arg有關,arg是一個結構體,定義如下:

      union semun { int val; struct semid_ds *buf; unsigned short array; struct seminfo *__buf; };

      cmd的取值含義如下:

      3 編程示例

      示例1:信號量創建(內含ftok生成key原理)

      創建信號量示例,sem_create.c:

      #include #include #include #include #include #include #include #define PATHNAME "/tmp" int main(int argc, char *argv[]) { key_t keyid; int sigid; int nsems, semflg; nsems = 1; semflg = IPC_CREAT|0666; // below 4 lines code not necessary, just show the PATHNANE -> dev & inode, // then combine with proj_id to calculate the keyid (ftok work principle) struct stat stat_info; stat(PATHNAME, &stat_info); printf("dev:%lx\n", stat_info.st_dev); printf("inode:%lx\n", stat_info.st_ino); // generate key if((keyid = ftok(PATHNAME, 1)) == -1) { perror("ftok() failed\n"); exit(1); } else printf("key is %x\n", keyid); // create a semaphore if((sigid = semget(keyid, nsems, semflg)) == -1) { perror("semget() failed\n"); exit(1); } printf("sem create ok!\n"); return 0; }

      運行結果:

      $ ./sem_create dev:804 inode:280001 key is 1040001 sem create ok!

      這里說明一下ftok()函數生成key的原理,ftok()定義如下:

      key_t ftok(const char *pathname, int proj_id);

      首先根據一個任意存在的pathname路徑提取其所屬文件系統的特有信息,包括設備號(stat.st_dev)和inode號(stat.st_ino),其獲取方法參見上述程序中的sata結構,然后再結合ftok()函數的proj_id參數,按照如下規則計算key:

      key1 = stat.st_ino & 0xffff; // 保留低16位 key2 = stat.st_dev & 0xff; // 保留低8位 key2 << = 16; // 左移16位 key3 = proj_id & 0xff; // 保留低8位 key3 << = 24; // 左移24位 key = key1|key2|key3; // 三者進行或運算

      本例中,pathname=’/tmp‘,ftok()函數提取的設備號為0x804,inode號為0x280001,proj_id為0x01,根據以上運算過程,key1=0x01,key2=0x40000,key3=0x1000000,三者求或得到key=0x1040001,與程序輸出的結果一致。

      示例2:服務器-客戶端模式

      服務器端,信號量的創建與初始化,監視信號量資源使用情況,sem_server.c:

      #include #include #include #include #include #include #define PATHNAME "/tmp" #define RESOURCE 3 union semun { int val; struct semid_ds *buf; unsigned short *array; struct seminfo *__buf; }; int main(int argc, char *argvp[]) { key_t keyid; int sigid; int nsems, semflg; union semun semopt; struct sembuf sbuf = {0, 0, SEM_UNDO}; nsems = 1; semflg = IPC_CREAT|0666; // generate key if((keyid = ftok(PATHNAME, 3)) == -1) { perror("ftok() failed\n"); exit(1); } else printf("key is %x\n", keyid); // create a semaphore if((sigid = semget(keyid, nsems, semflg)) == -1) { perror("semget() failed\n"); exit(1); } else printf("create semaphore ok\n"); // initial the semaphore semopt.val = RESOURCE; if(semctl(sigid, 0, SETVAL, semopt) == -1) { perror("semctl() failed\n"); exit(1); } else printf("initial the semphore ok\n"); // monitor the semphore usage status while(1) { if(semop(sigid, &sbuf, 1) == 0)// use up!!! printf("resources have been exhausted\n"); sleep(3); } return 0; }

      客戶端程序,使用按鍵p和v模擬P操作和V操作,sem_client.c:

      #include #include #include #include #include #include #define PATHNAME "/tmp" #define RESOURCE 3 int main(int argc, char *argvp[]) { key_t keyid; int sigid; int nsems, semflg; char opt; struct sembuf pbuf = {0, -1, IPC_NOWAIT}; // P operate struct sembuf vbuf = {0, 1, IPC_NOWAIT}; // V operate nsems = 1; semflg = IPC_CREAT|0666; // generate key if((keyid = ftok(PATHNAME, 3)) == -1) { perror("ftok() failed\n"); exit(1); } else printf("key is %x\n", keyid); // get the semaphore if((sigid = semget(keyid, nsems, semflg)) == -1) { perror("semget() failed\n"); exit(1); } else printf("get the semaphore ok\n"); // use p/v to simulate the PV operation while(1) { opt = getchar(); switch(opt) { case 'p':// -- if(semop(sigid, &pbuf, 1) == -1) printf("-->resources have been exhausted\n");// exhausted else printf("-->Total unused resources:%d\n", semctl(sigid, 0, GETVAL, 0)); break; case 'v':// ++ if(semop(sigid, &vbuf, 1) == -1) printf("-->V opreation failed\n"); else if(semctl(sigid, 0, GETVAL, 0) > RESOURCE) { printf("-->resources to achieve maximum\n");// achieve maximum semop(sigid, &pbuf, 1); } else printf("-->Total unused resources:%d\n", semctl(sigid, 0, GETVAL, 0)); break; case 's':// show printf("-->Total unused resources:%d\n", semctl(sigid, 0, GETVAL, 0)); break; case 'q':// quit exit(0); } sleep(1); } return 0; }

      編譯上面兩個程序并執行,先運行服務器端程序:

      $ ./sem_server key is 3040001 create semaphore ok initial the semphore ok

      可以看到信號量創建并初始化成功,進入監視狀態,在另一個shell中運行客戶端程序:

      $ ./sem_client key is 3040001 get the semaphore ok p -->Total unused resources:2 p -->Total unused resources:1 p -->Total unused resources:0 v -->Total unused resources:1 s -->Total unused resources:1 q

      運行后,使用鍵盤的p和v鍵入shell,繼續觀察服務器端的結果,當客戶端多次使用p鍵使資源耗盡后,服務器端會每隔3秒進行相應的輸出提示報警,客戶端使用v鍵增加資源,服務器端停止報警。

      $ ./sem_server key is 3040001 create semaphore ok initial the semphore ok resources have been exhausted resources have been exhausted resources have been exhausted

      最后可以使用Ctrl+c終止服務器端程序。

      示例3

      獲取各種信號量的信息,利用信號量實現共享資源的申請和釋放,sem_app.c:

      #include #include #include #include #include #include #include #include #define SEM_PATH "." #define max_tries 3 int semid; union semun { int val; struct semid_ds *buf; unsigned short array; struct seminfo *__buf; }; int main(void) { int flag1, flag2, key, i, init_ok, tmperrno; struct semid_ds sem_info; struct seminfo sem_info2; union semun arg; struct sembuf askfor_res, free_res; flag1 = IPC_CREAT|IPC_EXCL|00666; flag2 = IPC_CREAT|00666; // generate key key = ftok(SEM_PATH, 'a'); if(key == -1) { perror("ftok error"); exit(1); } // create a semaphore init_ok = 0; semid = semget(key, 1, flag1); if(semid < 0) { tmperrno = errno; perror("semget"); if(tmperrno == EEXIST) { // semid = semget(key, 1, flag2); arg.buf = &sem_info; for(i=0; isem_otime!=0) { i = max_tries; init_ok = 1; } else sleep(1); } } if(!init_ok) { arg.val = 1; if(semctl(semid, 0, SETVAL, arg) == -1) perror("semctl setval error"); } } else { perror("semget error, process exit"); exit(1); } } else // semid >= 0, do some initializing { arg.val = 1; if(semctl(semid, 0, SETVAL, arg) == -1) perror("semctl setval error"); } // get some information about the semaphore and the limit of semaphore in Linux arg.buf = &sem_info; if(semctl(semid, 0, IPC_STAT, arg) == -1) perror("semctl IPC_STAT"); printf("owner's uid is %d\n", arg.buf->sem_perm.uid); printf("owner's gid is %d\n", arg.buf->sem_perm.gid); printf("creater's uid is %d\n", arg.buf->sem_perm.cuid); printf("creater's gid is %d\n", arg.buf->sem_perm.cgid); arg.__buf = &sem_info2; if(semctl(semid, 0, SEM_INFO, arg) == -1) perror("semctl IPC_INFO"); printf("the number of entries in semaphore map is %d\n", arg.__buf->semmap); printf("max number of semaphore identifiers is %d\n", arg.__buf->semmni);//o); printf("mas number of semaphore in system is %d\n", arg.__buf->semmns); printf("the number of undo structures system wide is %d\n", arg.__buf->semmnu); printf("max number of semaphore per semid is %d\n", arg.__buf->semmsl); printf("max number of ops per semop call is %d\n", arg.__buf->semopm); printf("max number of undo entries per process is %d\n", arg.__buf->semume); printf("the size of struct sem_undo is %d\n", arg.__buf->semusz); printf("the maximum semaphore value is %d\n", arg.__buf->semvmx); // now ask for available resource askfor_res.sem_num = 0; askfor_res.sem_op = -1; askfor_res.sem_flg = SEM_UNDO; if(semop(semid, &askfor_res, 1) == -1) perror("semop error"); sleep(3); printf("now free the resource\n"); // now free resource free_res.sem_num = 0; // resources num free_res.sem_op = 1; free_res.sem_flg = SEM_UNDO; if(semop(semid, &free_res, 1) == -1) if(errno == EIDRM) printf("the semaphore set was removed\n"); if(semctl(semid, 0, IPC_RMID) == -1) perror("semctl IPC_RMID"); else printf("remove sem ok\n"); return 0; }

      編譯運行:

      $ ./sem_app owner's uid is 1000 owner's gid is 1000 creater's uid is 1000 creater's gid is 1000 the number of entries in semaphore map is 1024000000 max number of semaphore identifiers is 32000 mas number of semaphore in system is 1024000000 the number of undo structures system wide is 1024000000 max number of semaphore per semid is 32000 max number of ops per semop call is 500 max number of undo entries per process is 500 the size of struct sem_undo is 1 the maximum semaphore value is 32767 now free the resource remove sem okt

      linux 任務調度

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      上一篇:實用Excel技巧分享:“分列工具”的幾種實用操作
      下一篇:excel快速添加多個標題步驟分享
      相關文章
      亚洲日韩一区二区三区| 久久久久亚洲av无码专区喷水| 亚洲综合无码无在线观看| 在线观看亚洲电影| 国产亚洲精品精华液| 久久精品亚洲精品国产色婷| 亚洲熟女乱综合一区二区| 亚洲欧洲精品在线| 亚洲国产黄在线观看| 18亚洲男同志videos网站| 亚洲成网777777国产精品| 国产亚洲综合久久| 亚洲成人影院在线观看| 亚洲精品麻豆av| 亚洲天然素人无码专区| 亚洲国产精品成人午夜在线观看| 久久久久亚洲Av无码专| 亚洲美女激情视频| 日本亚洲成高清一区二区三区 | 国产色在线|亚洲| 亚洲国产成人一区二区精品区| 国产亚洲人成网站在线观看不卡 | 国产一区二区三区亚洲综合| 337p日本欧洲亚洲大胆人人| 亚洲制服丝袜精品久久| 亚洲精品一级无码鲁丝片| 亚洲国产天堂久久久久久| 亚洲av手机在线观看| 亚洲人成人无码网www国产| 国产成人A亚洲精V品无码| 日韩国产精品亚洲а∨天堂免| 亚洲制服在线观看| 亚洲另类无码专区丝袜| 精品久久久久久亚洲综合网| 亚洲国产精品成人久久蜜臀| 亚洲国产精品综合久久网络| 日日噜噜噜噜夜夜爽亚洲精品 | 亚洲免费在线视频播放| 亚洲丝袜中文字幕| 亚洲熟妇无码久久精品| 亚洲综合色区中文字幕|