> 在进程同步,并发运行时,保证按序地访问共享资源是十分重要的。因此引入了临界区的概念,一次只能有一个线程进入临界区完成他的指令。而信号量(semaphore)的作用,类似于一个交通信号灯,它负责进程协作,因此信号量又称为信号灯。 在Linux系统中,它提供两种信号量: - **内核信号量**,由内核控制路径使用 - **用户态进程使用的信号量**,这种信号量有两种接口,```POSIX```信号量和```SYSTEM V```信号量。 信号量的本质是一个计数器。一个较为常见的用法,是为每个资源都会分配一个信号量。记信号量为S,除了初始化之外,有两个标准原子操作:```wait()```和```signal()```。 ## System V信号量接口 - **semget** 创建一个新信号量或取得一个已有信号量 ```c int semget(key_t key, int num_sems, int sem_flags); ``` ```key```是一个整数值(唯一非零),可以理解成是信号量的标识符。 ```num_sems```指定了需要的信号量数目,通常为1。 ```sem_flags```是一组标志,当创建一个新的信号量时,设定权限与值```IPC_CREAT```做按位或操作。设置了sem_flags); ``` ```key```是一个整数值(唯一非零),可以理解成是信号量的标识符。 ```num_sems```指定了需要的信号量数目,通常为1。 ```sem_flags```是一组标志,当创建一个新的信号量时,设定权限与值```IPC_CREAT```做按位或操作。设置了```IPC_CREAT```标志后,即使给出的键是一个已有信号量的键,也不会产生错误。而```IPC_CREAT | IPC_EXCL```则可以创建一个新的,唯一的信号量,如果信号量已存在,返回一个错误。 函数成功返回一个相应信号标识符(非零),失败返回```-1```。 - **semctl** 直接控制信号量信息 ```c int semctl(int sem_id, int sem_num, int command, ...); ``` 第二个参数是操作信号在信号集中的编号,第一个信号的编号是```0```。 第三个参数```command```通常是下面两个值中的其中一个: ```SETVAL```:用来把信号量初始化为一个已知的值。 ```IPC_RMID```:用于删除一个已经无需继续使用的信号量标识符。 如果有第四个参数,它通常是一个```union semum```结构,定义如下: ```c union semun { int val; struct semid_ds *buf; unsigned short *arry; }; ``` - **semop** 改变信号量的值 ```c int semop(int sem_id, struct sembufsem_opa, size_t num_sem_ops); ``` ```sem_id```是由```semget```返回的信号量标识符,```sembuf```结构的定义如下: ```c struct sembuf{ short sem_num;//除非使用一组信号量,否则为0 short sem_op;//信号量在一次操作中需要改变的数据,-1即P(等待)操作,+1即V(发送信号)操作。 short sem_flg;//通常为SEM_UNDO,使操作系统跟踪信号,并在进程没有释放该信号量而终止时,操作系统释放信号量 }; ``` ## 进程同步实例 ##### 无信号量实例 ```c #include #include #include int main() { pid_t pid; pid = fork(); srand(pid); if(pid > 0) // parent process { char a = 'A'; // char to print for(int i = 0; i 10; ++i) { printf("%c", a); fflush(stdout); // flush stdout buffer sleep(1);
