Linux 信号量详解一
信号量主要用于进程间(不是线程)的互斥,通过sem_p()函数加锁使用资源,sem_v函数解锁释放资源,在加锁期间,CPU从硬件级别关闭中断,防止pv操作被打断。
semget函数 int semget(key_t key, int nsems, int semflg); --功能:用来创建和访问一个信号量集 --参数 key:信号集的key值 nsems:信号集中信号量的个数 semflg:由九个权限标志构成,他们的用法和创建文件时使用的mode模式标志是一样的 --返回值:成功返回一个非负整数,即该信号集的标识码,失败返回-1,并且更新errno
shmctl函数 int semctl(int semid, int semnum, int cmd, ...); --功能:用于控制信号量集 --参数 semid:由semget返回的信号集标识码 semnum:信号集中信号量的序号(信号量的序号从0开始,和数组类似) cmd:将要采取的动作(有5个可取值) 最后一个参数根据命令不同而不同 --返回值:成功返回0;失败返回-1并且更新errno
semop函数 int semop(int semid, struct sembuf *sops, unsigned nsops); --功能: --参数 semid:semget函数的返回值 sops:是个指向一个结构体的指针 nsops:信号量的个数 --返回值:成功返回0,失败返回-1,并且更新errno
semop函数续 --sembuf结构体 struct sembuf { unsigned short sem_num; /* semaphore number */ short sem_op; /* semaphore operation */ short sem_flg; /* operation flags */ }; sem_num是信号量的编号 sem_op是信号量一次PV操作是加减的数值,一般只会用到两个值,一个是"-1",也就是P操作,等待信号量变得可用;另一个是"+1",也就是V操作,发出信号量已经变得可用。 sem_flg的两个取值IPC_NOWAIT或SEM_UNDO,SEM_UNDO是进程完成P操作后直接被终止了,那么系统会自动执行V操作,恢复成默认值
//信号量API #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <unistd.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> //这个定义不可少 union semun { int val; // Value for SETVAL struct semid_ds *buf; // Buffer for IPC_STAT, IPC_SET unsigned short *array; // Array for GETALL, SETALL struct seminfo *__buf; // Buffer for IPC_INFO(Linux-specific) }; /** * sem_setval - 设置信号量的资源值 * semid:由semget返回的信号集标识码 * val:资源值 * 成功返回0,失败返回-1 * */ int sem_setval(int semid, int val) { int ret = 0; //union semun必须自定义 union semun su; su.val = val; //semctl()第二个参数是序号,这里取第一个信号量 ret = semctl(semid, 0, SETVAL, su); if (ret == -1) { perror("semctl() err"); } return ret; } /** * sem_setval - 获取信号量的资源值 * semid:由semget返回的信号集标识码 * 成功返回可用资源值,失败返回-1 * */ int sem_getval(int semid) { int ret = 0; //semctl()第二个参数是序号,这里取第一个信号量 //当使用GETVAL命令时,调用中的最后一个参数被忽略 //成功返回该信号量的可用资源值 ret = semctl(semid, 0, GETVAL, 0); if (ret == -1) { perror("semctl() err"); } printf("getval=%d\n", ret); return ret; } /** * sem_p - 信号量P操作 * semid:由semget返回的信号集标识码 * 成功返回0,失败返回-1 * */ int sem_p(int semid) { int ret=0; //通过struct sembuf结构体的sem_op属性设置P操作 //sem_flg默认设置为0 struct sembuf sbuf={0,-1,0}; ret=semop(semid,&sbuf,1); if(ret==-1) perror("semop() err"); return ret; } /** * sem_v - 信号量V操作 * semid:由semget返回的信号集标识码 * 成功返回0,失败返回-1 * */ int sem_v(int semid) { int ret=0; //通过struct sembuf结构体的sem_op属性设置P操作 //sem_flg默认设置为0 struct sembuf sbuf={0,1,0}; ret=semop(semid,&sbuf,1); if(ret==-1) perror("semop() err"); return ret; } int main() { //创建或者访问信号量集 int semid = 0; //第二个参数创建几个信号量 semid = semget(0x1234, 1, 0666 | IPC_CREAT | IPC_EXCL); if (semid == -1) { if (errno == EEXIST) { printf("该信号量集已经存在!\n"); semid = semget(0x1234, 1, 0666); } else { perror("semget() err"); return -1; } } //设置第0个信号量的s(可用资源)的值为1 sem_setval(semid, 1); sem_getval(semid); sem_p(semid); printf("dddddd\n"); sem_v(semid); return 0; }