Linux下的IPC-信号量的使用
几个进程映射同一内存区是一种最快的IPC方法,但单纯使用mmap,各进程之间,会有数据“不一致”的风险,需要一种机制保护共享区在某一时刻只允许一个进程操作,这时就要使用信号量了。因此本文可认为是这篇文章《Linux下的IPC-共享内存的使用》http://www.linuxidc.com/Linux/2011-09/42070.htm的继续。
本文以一个完整的程序为例子,来说明信号量的使用。以下是整个程序的代码。
- #include <stdio.h>
- #include <unistd.h>
- #include <sys/sem.h>
- #include <sys/types.h>
- #include <sys/ipc.h>
- #include <stdlib.h>
- #include <errno.h>
- #include <string.h>
- //==========================对信号量的封装函数==============================
- #define IPC_ID 0x26
- /* 我们必须自己定义 semun 联合类型。 */
- union semun
- {
- int val;
- struct semid_ds *buf;
- unsigned short int *array;
- struct seminfo *__buf;
- };
- int WhenError(const char * msg, int eno)
- {
- printf("semaphore error[%d]:%s, %s\n", eno, msg, strerror(eno));
- exit(0);
- return -1;
- }
- /* 获取一个count元信号量的标识符。如果需要则创建这个信号量 */
- int CreateSemaphore(int count)
- {
- key_t key;
- key = ftok(".",IPC_ID);
- int semid;
- semid = semget (key, count, 0666|IPC_CREAT);
- return (semid == -1)? WhenError("create sem(semget)", errno):semid;
- }
- /* 释放信号量。所有用户必须已经结束使用这个信号量。如果失败,返回 -1 */
- int FreeSemaphore(int semid)
- {
- union semun ignored_argument;
- return semctl (semid, 0, IPC_RMID, ignored_argument); //the second and forth args will be ignored in this case.
- }
- /* Set a semaphore to a value。*/
- int SetSemaphoreValue(int semid, int index, int value)
- {
- union semun argument;
- argument.val = value;
- int ret;
- ret= semctl(semid,index,SETVAL,argument);
- return (ret == -1)? WhenError("Set Value(semctl)", errno):ret;
- }
- int SetSemaphoreValues(int semid, unsigned short * values)
- {
- union semun argument;
- argument.array=values;
- int ret;
- ret= semctl(semid,0,SETALL,argument);
- return (ret == -1)? WhenError("Set Values(semctl)", errno):ret;
- }
- int GetSemaphoreValue(int semid, int index)
- {
- int ret = semctl(semid,index,GETVAL,0);
- return (ret == -1)? WhenError("Get Value(semctl)", errno):ret;
- }
- /* 如sem[index]为负且不超时,则阻塞,超时则返回EAGAIN,否则将信号量减1,返回0*/
- int TryLockSemaphore(int semid, int index, int milliseconds)
- {
- int ret;
- struct sembuf operation;
- operation.sem_num = index;
- operation.sem_op = -1;
- if(milliseconds<1)
- {
- operation.sem_flg = IPC_NOWAIT;
- ret= semop (semid, &operation, 1);
- }
- Else
- {
- operation.sem_flg = SEM_UNDO;
- struct timespec timeout;
- timeout.tv_sec = (milliseconds / 1000);
- timeout.tv_nsec = (milliseconds - timeout.tv_sec*1000L)*1000000L;
- ret= semtimedop(semid, &operation, 1, &timeout);
- }
- if(ret == -1)
- {
- if(errno == EAGAIN) return EAGAIN;
- }
- return (ret == -1)? WhenError("Wait(semop)", errno):ret;
- }
- /* 如果sem[index]为负,则阻塞,直到信号量值为正,然后将其减1*/
- int LockSemaphore(int semid, int index)
- {
- struct sembuf operation;
- operation.sem_num = index;
- operation.sem_op = -1; /* 减一。 */
- operation.sem_flg = SEM_UNDO; /* 允许撤销操作 */
- int ret;
- ret= semop (semid, &operation, 1);
- return (ret == -1)? WhenError("Wait(semop)", errno):ret;
- }
- /* 将sem[index]值加一。 这个操作会立即返回,应配合LockSemaphore一起使用。*/
- int UnlockSemaphore(int semid, int index)
- {
- struct sembuf operation;
- operation.sem_num = index;
- operation.sem_op = 1; /* 加一 */
- operation.sem_flg = SEM_UNDO; /* 允许撤销操作 */
- int ret;
- ret= semop (semid, &operations, 1);
- return (ret == -1)? WhenError("Post(semctl)", errno):ret;
- }
- //========================================================================
- int main()
- {
- pid_t pid;
- int semid;
- semid = CreateSemaphore(1);
- SetSemaphoreValue(semid, 0, 1);
- pid = fork();
- if(pid==0) //child 1
- {
- sleep(1);
- int res;
- res = LockSemaphore(semid, 0);
- /* Try LockSemaphore的用法
- res = EAGAIN;
- while(res== EAGAIN)
- {
- res= TryLockSemaphore(semid, 0, 1000); //设置1000ms超时
- //do something
- }
- */
- printf("child 1 set[%d]....\n",res);
- // do critical things
- UnlockSemaphore (semid, 0);
- printf("child 1 post[%d]....\n",res);
- return 0;
- }
- int dd;
- dd = LockSemaphore (semid, 0);
- printf("parent set 1[%d]....\n",dd);
- sleep(5);
- dd = UnlockSemaphore (semid, 0);
- sleep(2);
- printf("parent end: ...[%d]\n",dd);
- FreeSemaphore(semid);
- }
信号量的操作,涉及3个系统函数semget、semop和semctl,它们分别负责信号量的“创建”、PV操作和信号量的控制(查询值、释放等)。这三个函数的原型及其各参数的意义,请参阅有关文章,这里不列出了。