Linux程序设计学习笔记----System V进程间通信(信号量)
关于System VUnix System V,是Unix操作系统众多版本中的一支。它最初由AT&T开发,在1983年第一次发布,因此也被称为AT&T System V。一共发行了4个System V的主要版本:版本1、2、3和4。System V Release 4,或者称为SVR4,是最成功的版本,成为一些UNIX共同特性的源头,例如“SysV 初始化脚本”(/etc/init.d),用来
关于System V
Unix System V,是Unix操作系统众多版本中的一支。它最初由AT&T开发,在1983年第一次发布,因此也被称为AT&T System V。一共发行了4个System V的主要版本:版本1、2、3和4。System V Release 4,或者称为SVR4,是最成功的版本,成为一些UNIX共同特性的源头,例如“SysV 初始化脚本”(/etc/init.d),用来控制系统启动和关闭,System V Interface Definition (SVID)是一个System V如何工作的标准定义。
System V是AT&T的第一个商业UNIX版本(UNIX System III)的加强。传统上,System V被看作是两种UNIX“风味”之一(另一个是BSD)。然而,随着一些并不基于这两者代码的UNIX实现的出现,例如Linux和QNX, 这一归纳不再准确,但不论如何,像POSIX这样的标准化努力一直在试图减少各种实现之间的不同。
System V 的IPC机制
基本概念
为了提供与其它系统的兼容性,Linux也支持三种system Ⅴ的进程间通信机制:消息、信号量(semaphores)和共享内存,Linux对这些机制的实施大同小异。我们把信号量、消息和共享内存统称System V IPC的对象,每一个对象都具有同样类型的接口,即系统调用。就像每个文件都有一个打开文件号一样,每个对象也都有唯一的识别号,进程可以通过系统调用传递的识别号来存取这些对象,与文件的存取一样,对这些对象的存取也要验证存取权限,System V IPC可以通过系统调用对对象的创建者设置这些对象的存取权限。
在Linux内核中,System V IPC的所有对象有一个公共的数据结构pc_perm结构,它是IPC对象的权限描述,在linux/ipc.h中定义如下:struct ipc_perm
{
key_t key; /* 键 */
ushort uid; /* 对象拥有者对应进程的有效用户识别号和有效组识别号 */
ushort gid;
ushort cuid; /* 对象创建者对应进程的有效用户识别号和有效组识别号 */
ushort cgid;
ushort mode; /* 存取模式 */
ushort seq; /* 序列号 */
};
在这个结构中,要进一步说明的是键(key)。键和识别号指的是不同的东西。系统支持两种键:公有和私有。如果键是公有的,则系统中所有的进程通过权限检查后,均可以找到System V IPC 对象的识别号。如果键是公有的,则键值为0,说明每个进程都可以用键值0建立一个专供其私用的对象。注意,对System V IPC对象的引用是通过识别号而不是通过键。
通过icps命令可以查看当前系统在使用的IPC工具:
从上面看出,一个IPC工具至少需要包含key值,ID值,拥有者,权限,和使用的大小等关键信息.
Key值/ID值/拥有者权限
Linux系统为每个IPC机制都提供了唯一的ID,针对该IPC机制的操作都是使用该ID值.因此通信的双方必须要通过某种方法获取对方的ID.因为Linux两个进程不能随意访问对方的空间(父子进程除外),也就不能直接获取这一ID值.
因此Linux的IPC在实现时约定使用KEY值作为参数创建,如果在创建时使用了相同的key值将得到同一个IPC对象的ID(即一方创建,另一方获取的是ID),这就保证了双方可以获取用于传递数据的IPC机制的IPC值. key值是一个32位整形数据.
同时为了尽可能体现Linux的系统信息的载体(file)关联,Linux提供函数ftok()来创建key值.参数中需要选取特定的文件作为参数.
key_t ftok(const char *path, int id);
DESCRIPTION
The ftok() function shall return a key based onpath andid that is usable in subsequent calls to msgget(), semget(), and shmget(). The application shall ensure that the path argument is the pathname of anexisting file that the process is able tostat().
The ftok() function shall return the same key value for all paths that name the same file, when called with the sameid value, and return different key values when called with differentid values or with paths that name differentfiles existing on the same file system at the same time. It is unspecified whetherftok() shall return the same key valuewhen called again after the file named bypath is removed and recreated with the same name.
Only the low-order 8-bits of id are significant. The behavior offtok() is unspecified if these bits are 0.
具体函数的说明参见:http://blog.csdn.net/hwz119/article/details/1613601
http://pubs.opengroup.org/onlinepubs/009695399/functions/ftok.html
/*************************************************************************
> File Name: ftok.c
> Author:SuooL
> Mail:1020935219@qq.com || hu1020935219@gmail.com
> Website:http://blog.csdn.net/suool | http://suool.net
> Created Time: 2014年08月11日 星期一 17时36分30秒
> Description:
************************************************************************/
#include<sys/ipc.h>
#include<stdio.h>
#include<sys/stat.h>
#include<stdlib.h>
int main(int argc,char *argv[])
{
key_t key;
int i;
struct stat buf;
// 参数检查
if(argc!=3)
{
printf("use: command path number\n");
return 1;
}
// 命令行参数转换
i=atoi(argv[2]);
// 文件检测
if((stat(argv[1],&buf))==-1)
{
perror("stat");
exit(EXIT_FAILURE);
}
// 文件stat位输出
printf("file st_dev=0x%x\n",buf.st_dev);
printf("file st_ino=0x%x\n",buf.st_ino);
printf("number=0x%x\n",i);
key=ftok(argv[1],i);
// key值各位输出
printf("key=0x%x \tkey>>24=%x \tkey&0xffff=%x \t(key>>16)&0xff=%x\n",key,key>>24,key&0xffff,(key>>16)&0xff);
}
信号量
信号量及信号量上的操作是E.W.Dijkstra在1965年提出的一种解决同步、互斥问题的较通用的方法,并在很多操作系统中得以实现, Linux改进并实现了这种机制。
信号量(semaphore )实际是一个整数,它的值由多个进程进行测试(test)和设置(set)。就每个进程所关心的测试和设置操作而言,这两个操作是不可中断的,或称“原子”操作,即一旦开始直到两个操作全部完成。测试和设置操作的结果是:信号量的当前值和设置值相加,其和或者是正或者为负。根据测试和设置操作的结果,一个进程可能必须睡眠,直到有另一个进程改变信号量的值。
信号量可用来实现所谓的“临界区”的互斥使用,临界区指同一时刻只能有一个进程执行其中代码的代码段。为了进一步理解信号量的使用,下面我们举例说明。
假设你有很多相互协作的进程,它们正在读或写一个数据文件中的记录。你可能希望严格协调对这个文件的存取,于是你使用初始值为1的信号量,在这个信号量上实施两个操作,首先测试并且给信号量的值减1,然后测试并给信号量的值加1。当第一个进程存取文件时,它把信号量的值减1,并获得成功,信号量的值现在变为0,这个进程可以继续执行并存取数据文件。但是,如果另外一个进程也希望存取这个文件,那么它也把信号量的值减1,结果是不能存取这个文件,因为信号量的值变为-1。这个进程将被挂起,直到第一个进程完成对数据文件的存取。当第一个进程完成对数据文件的存取,它将增加信号量的值,使它重新变为1,现在,等待的进程被唤醒,它对信号量的减1操作将获得成功。
上述的进程互斥问题,是针对进程之间要共享一个临界资源而言的,信号量的初值为1 。实际上,信号量作为资源计数器,它的初值可以是任何正整数,其初值不一定为0或1。另外,如果一个进程要先获得两个或多个的共享资源后才能执行的话,那么,相应地也需要多个信号量,而多个进程要分别获得多个临界资源后方能运行,这就是信号量集合机制,Linux 讨论的就是信号量集合问题。System V信号量是不属于POSIX标准,它属于SUS(Single UNIX Specification)单一UNIX规范中的扩展定义。它和POSIX信号量一样都提供基本的信号量功能操作。
System V信号量相对于POSIX信号量最大的区别是在信号量的操作复杂度。
对于 System V 信号量,系统内核还有很多限制,譬如下面:SEMMNS:系统中信号量的最大数目,等于SEMMNI*SEMMSL
SEMOPM:一次semopt()操作的最大信号量数目
SEMMNI:系统内核中信号量的最大数目
信号量的数据结构
对于系统中的每个System V信号量,即每个信号量集,内核都会维护一个semid_ds的信息结构,如下是Linux 2.6.18下的定义 :
struct semid_ds {
struct ipc_perm sem_perm; /* IPC权限 */
long sem_otime; /* 最后一次对信号量操作(semop)的时间 */
long sem_ctime; /* 对这个结构最后一次修改的时间 */
struct sem *sem_base; /* 在信号量数组中指向第一个信号量的指针 */
struct sem_queue *sem_pending; /* 待处理的挂起操作*/
struct sem_queue **sem_pending_last; /* 最后一个挂起操作 */
struct sem_undo *undo; /* 在这个数组上的undo 请求 */
ushort sem_nsems; /* 在信号量数组上的信号量号 */
};
在《 APUE 》 P423 提到,信号量集中每一个信号量由一个无名结构表示。书中提到该结构的定义如下
struct {
unsigned short semval; //信号量的值
pid_t sempid; //最后一次semctl操作的进程id
unsigned short semncnt; //等待semval变为大于当前值的线程数
unsigned short semzcnt; //等待semval变为0的线程数
};
同时
系统中每个信号量的数据结构(sem)
struct sem {
int semval; /* 信号量的当前值 */
int sempid; /*在信号量上最后一次操作的进程识别号 *
};
系统中每一信号量集合的队列结构(sem_queue)
struct sem_queue {
struct sem_queue * next; /* 队列中下一个节点 */
struct sem_queue ** prev; /* 队列中前一个节点, *(q->prev) == q */
struct wait_queue * sleeper; /* 正在睡眠的进程 */
struct sem_undo * undo; /* undo 结构*/
int pid; /* 请求进程的进程识别号 */
int status; /* 操作的完成状态 */
struct semid_ds * sma; /*有操作的信号量集合数组 */
struct sembuf * sops; /* 挂起操作的数组 */
int nsops; /* 操作的个数 */
};
几个主要数据结构之间的关系 从图可以看出,semid_ds结构的sem_base指向一个信号量数组,允许操作这些信号量集合的进程可以利用系统调用执行操作。注意, 信号量信号量集合的区别,从上面可以看出,信号量用“sem ” 结构描述,而信号量集合用“semid_ds"结构描述,实际上,我们以信号量集合为讨论的主要对象。下面我们给出这几个结构之间的关系,如图所示:
Linux对信号量的这种实现机制,是为了与消息和共享内存的实现机制保持一致,但信号量是这三者中最难理解的,因此我们将结合系统调用做进一步的介绍,通过对系统调用的深入分析,我们可以较清楚地了解内核对信号量的实现机制。
Linux信号量的管理操作
为了创建一个新的信号量集合,或者存取一个已存在的集合,要使用segget()系统调用,其描述如下:
原型: int semget( key_t key, int nsems, int semflg );
返回值:如果成功,则返回信号量集合的IPC识别号
如果为-1,则出现错误:
semget()中的第一个参数是键值,这个键值要与已有的键值进行比较,已有的键值指在内核中已存在的其它信号量集合的键值。对信号量集合的打开或存取操作依赖于semflg参数的取值:
IPC_CREAT :如果内核中没有新创建的信号量集合,则创建它。
IPC_EXCL :当与IPC_CREAT一起使用时,但信号量集合已经存在,则创建失败。
如果IPC_CREAT单独使用,semget()为一个新创建的集合返回标识号,或者返回具有相同键值的已存在集合的标识号。如果IPC_EXCL与IPC_CREAT一起使用,要么创建一个新的集合,要么对已存在的集合返回-1。IPC_EXCL单独是没有用的,当与IPC_CREAT结合起来使用时,可以保证新创建集合的打开和存取。
作为System V IPC的其它形式,一种可选项是把一个八进制与掩码或,形成信号量集合的存取权限。
第三个参数nsems指的是在新创建的集合中信号量的个数。其最大值在“linux/sem.h”中定义: #define SEMMSL 250 /* <= 8 000 max num of semaphores per id */
注意:如果你是显式地打开一个现有的集合,则nsems参数可以忽略。
下面是一个创建信号集的示例:使用semget和semctl函数.
/*************************************************************************
> File Name: sem_create.c
> Author:SuooL
> Mail:1020935219@qq.com || hu1020935219@gmail.com
> Website:http://blog.csdn.net/suool | http://suool.net
> Created Time: 2014年08月11日 星期一 18时52分26秒
> Description:
************************************************************************/
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/ipc.h>
#include<sys/sem.h>
#include<sys/types.h>
int main(int argc, char * argv[])
{
int sem;
key_t key; // key值
if((key=ftok(".",'B'))==-1)
{
perror("ftok");
exit(EXIT_FAILURE);
}
if((sem = semget(key, 3, IPC_CREAT|0770))==-1) // 创建信号量集合,包含三个信号量
{
perror("semget");
exit(EXIT_FAILURE);
}
printf("sem1 id is : %d\n", sem);
semctl(sem, 0, IPC_RMID, (struct msquid_ds *)0); // 删除信号量集合
return 0;
}
系统调用: semop()
原型: int semop (int semid, struct sembuf *sops, unsigned nsops);
返回: 如果所有的操作都执行,则成功返回0。
如果为-1,则出错。
semop()中的第一个参数(semid)是集合的识别号(可以由semget()系统调用得到)。第二个参数(sops)是一个指针,它指向在集合上执行操作的数组。而第三个参数(nsop)是在那个数组上操作的个数。
sops参数指向类型为sembuf的一个数组,这个结构在/inclide/linux/sem.h 中声明,是内核中的一个数据结构,描述如下:
struct sembuf {
ushort sem_num; /* 在数组中信号量的索引值 */
short sem_op; /* 信号量操作值(正数、负数或0) */
short sem_flg; /* 操作标志,为IPC_NOWAIT或SEM_UNDO*/
};
如果sem_op为负数,那么就从信号量的值中减去sem_op的绝对值,这意味着进程要获取资源,这些资源是由信号量控制或监控来存取的。如果没有指定IPC_NOWAIT,那么调用进程睡眠到请求的资源数得到满足(其它的进程可能释放一些资源)。
如果sem_op是正数,把它的值加到信号量,这意味着把资源归还给应用程序的集合。
最后,如果sem_op为0,那么调用进程将睡眠到信号量的值也为0,这相当于一个信号量到达了100%的利用。
综上所述,Linux按如下的规则判断是否所有的操作都可以成功:操作值和信号量的当前值相加大于 0,或操作值和当前值均为 0,则操作成功。如果系统调用中指定的所有操作中有一个操作不能成功时,则 Linux 会挂起这一进程。但是,如果操作标志指定这种情况下不能挂起进程的话,系统调用返回并指明信号量上的操作没有成功,而进程可以继续执行。如果进程被挂起,Linux 必须保存信号量的操作状态并将当前进程放入等待队列。为此,Linux 内核在堆栈中建立一个 sem_queue 结构并填充该结构。新的 sem_queue 结构添加到集合的等待队列中(利用 sem_pending 和 sem_pending_last 指针)。当前进程放入 sem_queue 结构的等待队列中(sleeper)后调用调度程序选择其他的进程运行。
为了进一步解释semop()调用,让我们来看一个例子。假设我们有一台打印机,一次只能打印一个作业。我们创建一个只有一个信号量的集合(仅一个打印机),并且给信号量的初值为1(因为一次只能有一个作业)。
每当我们希望把一个作业发送给打印机时,首先要确定这个资源是可用的,可以通过从信号量中获得一个单位而达到此目的。让我们装载一个sembuf数组来执行这个操作:
struct sembuf sem_lock = { 0, -1, IPC_NOWAIT };
从这个初始化结构可以看出,0表示集合中信号量数组的索引,即在集合中只有一个信号量,-1表示信号量操作(sem_op),操作标志为IPC_NOWAIT,表示或者调用进程不用等待可立即执行,或者失败(另一个进程正在打印)。下面是用初始化的sembuf结构进行semop()系统调用的例子:
if((semop(sid, &sem_lock, 1) == -1)
fprintf(stderr,"semop\n");
第三个参数(nsops)是说我们仅仅执行了一个操作(在我们的操作数组中只有一个sembuf结构),sid参数是我们集合的IPC识别号。
当我们使用完打印机,我们必须把资源返回给集合,以便其它的进程使用。
struct sembuf sem_unlock = { 0, 1, IPC_NOWAIT };
上面这个初始化结构表示,把1加到集合数组的第0个元素,换句话说,一个单位资源返回给集合。
系统调用 : semctl()
原型: int semctl( int semid, int semnum, int cmd, union semun arg );
返回值: 成功返回正数,出错返回-1。
注意:semctl()是在集合上执行控制操作。
semctl()的第一个参数(semid)是集合的标识号,第二个参数(semnn)是将要操作的信号量个数,从本质上说,它是集合的一个索引,对于集合上的第一个信号量,则该值为0。
·cmd参数表示在集合上执行的命令,这些命令及解释如表所示:
·arg参数的类型为semun,这个特殊的联合体在 include/linux/sem.h中声明,对它的描述如下:
/*arg for semctl system calls. */
union semun {
int val; /* value for SETVAL */
struct semid_ds *buf; /* buffer for IPC_STAT &IPC_SET */
ushort *array; /* array for GETALL & SETALL */
struct seminfo *__buf; /* buffer for IPC_INFO */
void *__pad;
};
cmd命令及解释
命令 | 解 释 |
IPC_STAT | 从信号量集合上检索semid_ds结构,并存到semun联合体参数的成员buf的地址中 |
IPC_SET | 设置一个信号量集合的semid_ds结构中ipc_perm域的值,并从semun的buf中取出值 |
IPC_RMID | 从内核中删除信号量集合 |
GETALL | 从信号量集合中获得所有信号量的值,并把其整数值存到semun联合体成员的一个指针数组中 |
GETNCNT | 返回当前等待资源的进程个数 |
GETPID | 返回最后一个执行系统调用semop()进程的PID |
GETVAL | 返回信号量集合内单个信号量的值 |
GETZCNT | 返回当前等待100%资源利用的进程个数 |
SETALL | 与GETALL正好相反 |
SETVAL | 用联合体中val成员的值设置信号量集合中单个信号量的值 |
这个联合体中,有三个成员已经在表7-1中提到,剩下的两个成员_buf 和_pad用在内核中信号量的实现代码,开发者很少用到。事实上,这两个成员是Linux操作系统所特有的,在UINX中没有。
这个系统调用比较复杂,我们举例说明。
下面这个程序段返回集合上索引为semnum对应信号量的值。当用GETVAL命令时,最后的参数(semnum)被忽略。
int get_sem_val( int sid, int semnum )
{
return( semctl(sid, semnum, GETVAL, 0));
}
关于信号量的三个系统调用,我们进行了详细的介绍。从中可以看出,这几个系统调用的实现和使用都和系统内核密切相关,因此,如果在了解内核的基础上,再理解系统调用,相对要简单地多,也深入地多。下面是使用semctl函数获取信号量值的示例:
/*************************************************************************
> File Name: sem_get_value.c
> Author:SuooL
> Mail:1020935219@qq.com || hu1020935219@gmail.com
> Website:http://blog.csdn.net/suool | http://suool.net
> Created Time: 2014年08月11日 星期一 17时36分30秒
> Description: 读取设置信号量集合的示例
************************************************************************/
#include <stdio.h>
#include <sys/types.h>
#include <sys/sem.h>
#include <errno.h>
#define MAX_SEMAPHORES 5
int main(int argc,char *argv[])
{
int i, ret, semid;
unsigned short sem_array[MAX_SEMAPHORES]; // 信号量组
unsigned short sem_read_array[MAX_SEMAPHORES];
union semun // 信号量组结构
{
int val;
struct semid_ds *buf;
unsigned short *array;
} arg;
semid = semget( IPC_PRIVATE, MAX_SEMAPHORES,IPC_CREAT | 0666 ); // 创建信号量集合
if (semid != -1)
{
/* Initialize the sem_array */
for ( i = 0 ; i < MAX_SEMAPHORES ; i++ ) // 初始化
{
sem_array[i] = (unsigned short)(i+1);
}
/* Update the arg union with the sem_array address */
arg.array = sem_array;
/* Set the values of the semaphore-array */
ret = semctl( semid, 0, SETALL, arg); // 设置所有信号量值
if (ret == -1) printf("SETALL failed (%d)\n", errno);
/* Update the arg union with another array for read */
arg.array = sem_read_array;
/* Read the values of the semaphore array */
ret = semctl( semid, 0, GETALL, arg ); // 获取所有信号量值
if (ret == -1) printf("GETALL failed (%d)\n", errno);
/* print the sem_read_array */
for ( i = 0 ; i < MAX_SEMAPHORES ; i++ ) // 输出
{
printf("Semaphore %d, value %d\n", i, sem_read_array[i] );
}
/* Use GETVAL in a similar manner */
for ( i = 0 ; i < MAX_SEMAPHORES ; i++ ) // 逐次获取信号量值
{
ret = semctl( semid, i, GETVAL );
printf("Semaphore %d, value %d\n", i, ret );
}
/* Delete the semaphore */
ret = semctl( semid, 0, IPC_RMID ); // 删除信号量集合
}
else
printf("Could not allocate semaphore (%d)\n", errno);
return 0;
}
使用信号量实现生产者消费者问题
这个问题是OS中的进程使用资源的经典问题.下面示例程序:
首先是编译结果:
代码如下:
消费者:
/*************************************************************************
> File Name: sem_customer.c
> Author:SuooL
> Mail:1020935219@qq.com || hu1020935219@gmail.com
> Website:http://blog.csdn.net/suool | http://suool.net
> Created Time: 2014年08月11日 星期一 19时27分26秒
> Description: customer
************************************************************************/
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <errno.h>
int sem_id;
void init()
{
key_t key;
key=ftok(".",'s');
sem_id=semget(key,2,IPC_CREAT|0644); // 获取ID,必须先执行生产者初始化
//printf("sem id is %d\n",sem_id);
}
int main(int argc,char *argv[])
{
init();
struct sembuf sops[2];
sops[0].sem_num = 0;
sops[0].sem_op = -1; // 执行加一操作,每消费一个产品,产品数减一
sops[0].sem_flg = 0;
sops[1].sem_num = 1;
sops[1].sem_op = 1; // 执行加一操作,每消费一个产品,对空间数加一
sops[1].sem_flg = 0;
init();
printf("this is customer\n");
while(1)
{
printf("\n\nbefore consume:\n");
printf("productor is %d\n",semctl(sem_id,0,GETVAL));
printf("space is %d\n",semctl(sem_id,1,GETVAL));
semop(sem_id,(struct sembuf *)&sops[0],1); //get the productor to cusume
printf("now consuming......\n");
semop(sem_id,(struct sembuf *)&sops[1],1); //now tell the productor can bu produce
printf("\nafter consume\n");
printf("products number is %d\n",semctl(sem_id,0,GETVAL));
printf("space number is %d\n",semctl(sem_id,1,GETVAL));
sleep(3);
}
}
生产者:
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <errno.h>
int sem_id;
void init()
{
key_t key;
int ret;
unsigned short sem_array[2];
union semun
{
int val;
struct semid_ds *buf;
unsigned short *array;
}arg;
key=ftok(".",'s');
sem_id=semget(key,2,IPC_CREAT|0644);
sem_array[0]=0; //identify the productor
sem_array[1]=100; //identify the space
//printf("set the productor init value is 0\nset the space init value is 100\n");
arg.array = sem_array;
ret = semctl(sem_id, 0, SETALL, arg);
if (ret == -1)
printf("SETALL failed (%d)\n", errno);
//printf("\nread the number\n");
printf("productor init is %d\n",semctl(sem_id,0,GETVAL));
printf("space init is %d\n\n",semctl(sem_id,1,GETVAL));
}
void del()
{
semctl(sem_id,IPC_RMID,0);
}
int main(int argc,char *argv[])
{
struct sembuf sops[2];
sops[0].sem_num = 0;
sops[0].sem_op = 1;
sops[0].sem_flg = 0;
sops[1].sem_num = 1;
sops[1].sem_op = -1;
sops[1].sem_flg = 0;
init();
printf("this is productor\n");
while(1)
{
printf("\n\nbefore produce:\n");
printf("productor number is %d\n",semctl(sem_id,0,GETVAL));
printf("space number is %d\n",semctl(sem_id,1,GETVAL));
semop(sem_id,(struct sembuf *)&sops[1],1); //get the space to instore the productor
printf("now producing......\n");
semop(sem_id,(struct sembuf *)&sops[0],1); //now tell the customer can bu cusume
printf("\nafter produce\n");
printf("spaces number is %d\n",semctl(sem_id,1,GETVAL));
printf("productor number is %d\n",semctl(sem_id,0,GETVAL));
sleep(4);
}
del();
}
NEXT:
消息队列
共享内存
更多推荐
所有评论(0)