IPC
信号量
semget
-
#include<sys/sem.h> int semget(key_t _key ,int _nsems,int _semflg);
-
功能:创建一个新的信号量或获取一个已经存在的信号量的键值。
-
返回值:成功返回信号量的标识码ID。失败返回-1;
-
参数:
- _key 为整型值,用户可以自己设定。有两种情况:
- 键值是IPC_PRIVATE,该值通常为0,意思就是创建一个仅能被进程进程给我的信号量。
- 键值不是IPC_PRIVATE,我们可以指定键值,例如1234;也可以一个ftok()函数来取得一个唯一的键值。
- nsems 表示初始化信号量的个数。比如我们要创建一个信号量,则该值为1.,创建2个就是2。
- semflg :信号量的创建方式或权限。有IPC_CREAT,IPC_EXCL。
- IPC_CREAT如果信号量不存在,则创建一个信号量,否则获取。
- IPC_EXCL只有信号量不存在的时候,新的信号量才建立,否则就产生错误。
- _key 为整型值,用户可以自己设定。有两种情况:
-
semid=semget(MYKEY,1,IPC_CREAT|0666);//创建了一个权限为666的信号量
semctrl
-
#include<sys/sem.h> int semctl(int _semid ,int _semnum,int _cmd ……);
-
功能:控制信号量的信息。
-
返回值:成功返回0,失败返回-1;
-
参数:
- _semid 信号量的标志码(ID),也就是semget()函数的返回值;
- semnum, 操作信号在信号集中的编号。从0开始。
- _cmd 命令,表示要进行的操作。
-
参数cmd中可以使用的命令如下:
- IPC_STAT读取一个信号量集的数据结构semid_ds,并将其存储在semun中的buf参数中。
- IPC_SET设置信号量集的数据结构semid_ds中的元素ipc_perm,其值取自semun中的buf参数。
-
IPC_RMID将信号量集从内存中删除。
- GETALL用于读取信号量集中的所有信号量的值。
- GETNCNT返回正在等待资源的进程数目。
-
GETPID返回最后一个执行semop操作的进程的PID。
-
GETVAL返回信号量集中的一个单个的信号量的值。
-
GETZCNT返回这在等待完全空闲的资源的进程数目。
-
SETALL设置信号量集中的所有的信号量的值。
-
SETVAL设置信号量集中的一个单独的信号量的值。
-
Semunion ;第4个参数是可选的;semunion :是union semun的实例。
-
* union semun { int val; struct semid_ds *buf; unsigned short *arrary; };
semop
-
sembuf
-
struct sembuf{ short sem_num; short sem_op; short sem_flg; };
-
sem_num是信号量的编号,如果你的工作不需要使用一组信号量,这个值一般就取为0
-
sem_op是信号量一次PV操作时加减的数值,一般只会用到两个值,一个是“-1”,也就是P操作,等待信号量变得可用;另一个是“+1”,也就是我们的V操作,发出信号量已经变得可用
-
sem_flag通常被设置为SEM_UNDO.她将使操作系统跟踪当前进程对该信号量的修改情况
-
-
semop
-
int semop(int sem_id, struct sembuf *sem_opa, size_t num_sem_ops);
-
作用是改变信号量的值
-
sem_post and sem_wait
- sem_post函数(函数原型 int sem_post(sem_t *sem);)
- 作用是给信号量的值加上一个“1”。 当有线程阻塞在这个信号量上时,调用这个函数会使其中一个线程不在阻塞,选择机制是有线程的调度策略决定的。
- sem_wait函数(函数原型 int sem_wait(sem_t * sem);)
- 它的作用是从信号量的值减去一个“1”,但它永远会先等待该信号量为一个非零值才开始做减法。
进程
fork
-
用于创建一个新进程,称为子进程
pid_t fork( void);
-
(pid_t 是一个宏定义,其实质是int 被定义在#include<sys/types.h>中)
-
返回值: 若成功调用一次则返回两个值,
- 子进程返回0
- 父进程返回子进程ID;
- 否则,出错返回-1
信号量和互斥锁的区别与联系
- 互斥锁(Mutex)保证了使用资源线程的唯一性和排他性,但是无法限制资源释放后其他线程申请的顺序问题,所以是无序的。
- 信号量(Semaphore)一般就是互斥的(少许情况读取是可以同时申请的),其保证了线程执行的有序性,可以理解为从一到多的进步,像比较有名的理发厅问题,我们就需要设一个Integer而非Boolean,因为理发厅里面的座椅不可能只有一张。
- 互斥锁必须由单个线程获取和释放。
- 信号量是由单个线程释放,另一个线程获取,保证线程同步。
- 信号量是互斥的进步,Semaphore=1时可以看成互斥锁。
生产者消费者问题
理发师问题
- 理发师问题的描述:
- 一个理发店接待室有n张椅子,工作室有1张椅子;
- 没有顾客时,理发师睡觉;第一个顾客来到时,必须将理发师唤醒;
- 顾客来时如果还有空座的话,他就坐在一个座位上等待;
- 如果顾客来时没有空座位了,他就离开,不理发了;
- 当理发师处理完所有顾客,而又没有新顾客来时,他又开始睡觉。
读者-写者问题
-
教材中对读者写者问题算法均有描述,但这个算法在不断地有读者流的情况下,写者会被阻塞。编写一个写者优先解决读者写者问题的程序,其中读者和写者均是多个进程,用信号量作为同步互斥机制。
-
读者优先
-
写者优先
-
//写者优先 int wcount = 0; //用于记录写者数量 int rcount = 0; //用于记录读者数量 semaphore rmutex = 1; //用于读者进程互斥修改rcount semaphore wmutex = 1; //用于写者进程互斥修改wcount semaphore file = 1; //用于读者写者互斥访问file semphore read = 1; //用于阻塞读者进程,实现写者优先 writer() { P(wmutex); if(wcount == 0) P(read); wcount++; V(wmutex); P(file); //写者互斥访问文件 do_writing(); V(file); P(wmutex) wcount--; if(wcount == 0) V(read); V(wmutex); } reader() { P(read); //检查写者队列是否为空。 P(rmutex); if(rcount == 0) P(file); //申请文件资源 rcount++; V(rmutex); V(read); do_reading(); P(rmutex); rcount--; if(rcount == 0) V(file); V(rmutex); }