暑假那会把UNPv2大概过了一遍,不过没有总结,除了pipe,所以现在都忘得差不多了,趁最近几天学操作系统,边学边回顾一下。
信号灯(semaphore)是一种用于提供不同进程间或一个给定进程的不同线程的同步手段的原语。
我所知一共有三种类型信号灯:
下面给出信号灯、互斥锁、条件变量之间的三个差异:
sem_t *sem_open(const char *name, int oflag);
sem_t *sem_open(const char *name, int oflag,
mode_t mode, unsigned int value);
name:打开信号灯,name是给信号灯起的名字,也就是信号灯对应在文件系统中的路径,加入你打开了一个信号灯,该信号灯是随内核持续的,就算你的程序结束了,信号灯 依然存在,ll /dev/shm即可看到。销毁信号灯需要程序中调用unlink函数(对于使用内存的信号灯而言是destroy函数)或者机器重启,调用close函数也不行。
int sem_close(sem_t *sem);
这个函数虽说close,实际没什么乱用,即便不使用该函数,eixt或_exit的时候也会自动关闭信号灯。但是不能从系统中删除。失败返回-1。
int sem_unlink(const char* name);
name:信号灯对应的文件系统名字。
int sem_wait(sem_t *sem);
int sem_trywait(sem_t *sem);
int sem_post(sem_t *sem);
与上面函数作用相反,加1函数。
int sem_getvalue(sem_t* sem, int *valp);
使用Int*获得当前信号灯的值。
#include "../unp.h"
#define NBUFF 10
#define SEM_MUTEX "mutex" //thers are args to ipc name.
#define SEM_NEMPTY "nempty"
#define SEM_NSTORED "nstored"
int nitems; //read-only by producer and consumer
struct {
int buff_[NBUFF];
sem_t* mutex_;
sem_t* nempty_;
sem_t* nstored_;
} shared;
void *produce(void *), *consume(void *);
int main(int argc, char** argv)
{
if(argc != 2)
err_quit("usage: prodcons1 <#items>");
nitems = atoi(argv[1]);
//create three semephore
shared.mutex_ = sem_open(SEM_MUTEX, O_CREAT | O_EXCL, FILE_MODE, 1); //1
assert(shared.mutex_ != (sem_t*)(-1));
shared.nempty_ = sem_open(SEM_NEMPTY, O_CREAT | O_EXCL, FILE_MODE, NBUFF); //NBUFF
assert(shared.nempty_ != (sem_t*)(-1));
shared.nstored_ = sem_open(SEM_NSTORED, O_CREAT | O_EXCL, FILE_MODE, 0); //0
assert(shared.nstored_ != (sem_t*)(-1));
//create one producer thread and one consumer thread
int ret = 0;
pthread_t tid_produce, tid_consume;
ret = pthread_create(&tid_produce, NULL, produce, NULL);
assert(ret != -1);
ret = pthread_create(&tid_consume, NULL, consume, NULL);
assert(ret != -1);
//wait for the two threads
pthread_join(tid_produce, NULL);
pthread_join(tid_consume, NULL);
sem_unlink(SEM_MUTEX);
sem_unlink(SEM_NEMPTY);
sem_unlink(SEM_NSTORED);
exit(0);
}
void *produce(void *)
{
for(int i=0; i
int sem_init(sem_t *sem, int shared, unsigned int value);
初始化信号灯。sem参数必须指向有应用程序分配的sem_t变量。如果shared为0,那么待初始化的信号灯是在同一进程中的各个线程间共享的,否则该信号灯是在进程间共享的。当shared为非零时,该信号灯必须存放在即将使用它的所有进程都能访问的某种类型的共享内存中。value是初始值。
int sem_destroy(sem_t* sem);
使用完一个基于内存的信号灯后,我们调用sem_destroy摧毁它。
struct semid_ds {
struct ipc_permsem_perm ;
structsem* sem_base ; //信号数组指针
ushort sem_nsem ; //此集中信号个数
time_t sem_otime ; //最后一次semop时间
time_t sem_ctime ; //最后一次创建时间
} ;
struct sem {
ushort_t semval ; //信号量的值
short sempid ; //最后一个调用semop的进程ID
ushort semncnt ; //等待该信号量值大于当前值的进程数(一有进程释放资源 就被唤醒)
ushort semzcnt ; //等待该信号量值等于0的进程数
} ;
key_t ftok(char *pathname, char proj);
根据参数pathname和proj来创建一个关键字,成功时返回与路径pathname相对应的一个键值,具有唯一性,失败时返回值为-1.
int semget (key_t key, int nsems , int semflg);
创建一个新信号量或者取得一个现有的信号量,key是一个关键字,可以是用ftok()函数创建的,也可以是IPC_PRIVATE(/usr/include/bits$ vi ipc.h),nsems表明创建的信号量个数,semflg是设置信号量的访问权限标志,函数调用成功时返回信号量ID,失败则返回-1.
int semop (int semid, struct sembuf *spos, int nspos);
对信号量进行操作的函数,用于改变信号量的键值,semid是信号量的标志,spos是指向一个结构体数组的指针,表明要进行什么操作,nspos表明数组的元素个数,调用成功则返回0,失败则返回-1。/usr/include/linux$ vi sem.h (sembuf \ semun)
Struct sembuf
{
Unsigned short sem_num; /*sem index in array*/
Short sem_op; /* sem operation */
Short sem_flg;/* operation flags */ sem_flg&IPC_RND 0
};
其中,如果sem_op大于0,那么操作值加入到信号量的值中,并唤醒等待信号增加的进程,如果sem_op为0,当信号量的值是0的时候,函数返回,否则阻塞直到信号量的值为0,如果sem_op小于0,函数判断信号量的值加上这个负值,如果结果为0唤醒等待信号量为0的进程,如果小于0函数阻塞,如果大于0,那么从信号量里面减去这个值并返回。
int semctl (int semid, int semnum, int cmd, union semun arg);
该函数得作用是对信号量进行一系列得控制,semid是要操作得信号量标志,semnum是信号量的下标,cmd是操作的命令,经常用的两个命令是:SETVAL、IPC_RMID,arg用于设置或返回信号量信息。
Union semun
{
int val;
struct semid_ds *buf;
unsigned short *array;
struct seminfo *__buf;
void *__pad;
}