进程间通信之信号量semaphore--linux内核剖析( 五 )


有名信号量相关函数说明有名信号量在使用的时候 , 和无名信号量共享sem_wait和sem_post函数 。区别是有名信号量使用sem_open代替sem_init , 另外在结束的时候要像关闭文件一样去关闭这个有名信号量 。

  • 打开一个已存在的有名信号量 , 或创建并初始化一个有名信号量 。 一个单一的调用就完 成了信号量的创建、初始化和权限的设置 。
sem_t *sem_open(const char *name, int oflag, mode_t mode , int value);参数描述name文件的路径名;Oflag有O_CREAT或O_CREATmode_t控制新的信号量的访问权限;Value指定信号量的初始化值 。
注意:
这里的name不能写成/tmp/aaa.sem这样的格式 , 因为在linux下 , sem都是创建在/dev/shm目录下 。 你可以将name写成“/mysem”或“mysem” , 创建出来的文件都是“/dev/shm/sem.mysem” , 千万不要写路径 。 也千万不要写“/tmp/mysem”之类的 。
当oflag = O_CREAT时 , 若name指定的信号量不存在时 , 则会创建一个 , 而且后面的mode和value参数必须有效 。 若name指定的信号量已存在 , 则直接打开该信号量 ,
同时忽略mode和value参数 。
当oflag = O_CREAT|O_EXCL时 , 若name指定的信号量已存在 , 该函数会直接返回error 。
  • 一旦你使用了一信号量 , 销毁它们就变得很重要 。在做这个之前 , 要确定所有对这个有名信号量的引用都已经通过sem_close()函数关闭了 , 然后只需在退出或是退出处理函数中调用sem_unlink()去删除系统中的信号量 ,注意如果有任何的处理器或是线程引用这个信号量 , sem_unlink()函数不会起到任何的作用 。
也就是说 , 必须是最后一个使用该信号量的进程来执行sem_unlick才有效 。 因为每个信号灯有一个引用计数器记录当前的打开次数 , sem_unlink必须等待这个数为0时才能把name所指的信号灯从文件系统中删除 。 也就是要等待最后一个sem_close发生 。
有名信号量在无相关进程间的同步前面已经说过 , 有名信号量是位于共享内存区的 , 那么它要保护的资源也必须是位于共享内存区 , 只有这样才能被无相关的进程所共享 。 在下面这个例子中 , 服务进程和客户进程都使用shmget和shmat来获取得一块共享内存资源 。 然后利用有名信号量来对这块共享内存资源进行互斥保护 。
服务器程序
//server.c#include #include #include #include #include #include #include #include #include #include #define SHMSZ 27char SEM_NAME[]= "vik";int main(){charch;intshmid;key_tkey;char*shm,*s;sem_t*mutex;//name the shared memory segmentkey = 1000;//createif(mutex == SEM_FAILED){perror("unable to create semaphore");sem_unlink(SEM_NAME);exit(-1);}//create the shared memory segment with this keyshmid = shmget(key, SHMSZ, IPC_CREAT | 0666);if(shmid < 0){perror("failure in shmget");exit(-1);}//attach this segment to virtual memoryshm = shmat(shmid, NULL, 0);//start writing into memorys = shm;for(ch = 'A'; ch <= 'Z'; ch++){sem_wait(mutex);*s++ = ch;sem_post(mutex);}//the below loop could be replaced by binary semaphorewhile(*shm != '*'){sleep(1);}sem_close(mutex);sem_unlink(SEM_NAME);shmctl(shmid, IPC_RMID, 0);return EXIT_SUCCESS;}客户端程序
// client.c#include #include #include #include #include #include #include #include #include #include #define SHMSZ 27char SEM_NAME[]= "vik";int main(){intshmid;key_tkey;char*shm, *s;sem_t*mutex;//name the shared memory segmentkey = 1000;//createif(mutex == SEM_FAILED){perror("reader:unable to execute semaphore");sem_close(mutex);exit(-1);}//create the shared memory segment with this keyshmid = shmget(key, SHMSZ, 0666);if(shmid < 0){perror("reader:failure in shmget");exit(-1);}//attach this segment to virtual memoryshm = shmat(shmid, NULL, 0);//start readings = shm;for(s = shm; *s != '\0'; s++){sem_wait(mutex);putchar(*s);sem_post(mutex);}//once done signal exiting of reader:This can be replaced by another semaphore*shm = '*';sem_close(mutex);shmctl(shmid, IPC_RMID, 0);return EXIT_SUCCESS;}SYSTEM V信号量这是信号量值的集合 , 而不是单个信号量 。 相关的信号量操作函数由引用 。
ystem V 信号量在内核中维护 , 其中包括二值信号量 、计数信号量、计数信号量集 。
  • 二值信号量 : 其值只有0、1 两种选择 , 0表示资源被锁 , 1表示资源可用;
  • 计数信号量:其值在0 和某个限定值之间 , 不限定资源数只在0 1 之间;
  • 计数信号量集 :多个信号量的集合组成信号量集
信号量结构体内核为每个信号量集维护一个信号量结构体 , 可在
struct semid_ds{struct ipc_perm sem_perm; /* 信号量集的操作许可权限 */struct sem *sem_base; /* 某个信号量sem结构数组的指针 , 当前信号量集中的每个信号量对应其中一个数组元素 */ushort sem_nsems; /* sem_base 数组的个数 */time_t sem_otime; /* 最后一次成功修改信号量数组的时间 */time_t sem_ctime; /* 成功创建时间 */};