本文编写时参考了大量博文,如有侵权,联系删除。
消息队列 | 信号灯 | 共享内存 | |
---|---|---|---|
头文件 | |||
创建或打开IPC函数 | msgget | semget | shmget |
控制IPC操作的函数 | msgctl | semctl | shmctl |
IPC操作函数 | msgsnd msgrcv |
semop | shmat shmdt |
头文件:
函数 |
key_t ftok(const char *pathname, int proj_id) |
|
函数 |
利用已存在的pathname的信息与>0的整数id的低序8位组合成一个唯一的整数IPC键 |
|
函数 |
const char *pathname |
指定的带路径文件名,此文件必须已存在且可存取 |
int proj_id |
大于0的整数,计划代号(project ID) |
|
函数 |
成功:返回key_t值(即IPC 键值) |
|
出错:-1,错误原因存于error中 |
||
附加 |
key_t一般为32位的int型的重定义 |
共享内存是一种最为高效的进程间通信方式,进程可以直接读写内存,而不需要任何数据的拷贝。为了在多个进程间交换信息,内核专门留出了一块内存区,可以由需要访问的进程将其映射到自己的私有地址空间。进程就可以直接读写这一内存区而不需要进行数据的拷贝,从而大大提高的效率。由于多个进程共享一段内存,因此也需要依靠某种同步机制,如互斥锁和信号量等。共享内存是所有进程间通信方式中效率最高的
步骤:
头文件:
LINUX终端下命令:
ipcs -m 查看内核中的共享内存
ipcrm -m shmid 删除共享内存
函数 |
int shmget(key_t key, size_t size, int shmflg) |
|
函数 |
得到一个共享内存标识符或创建一个共享内存对象并返回共享内存标识符 |
|
函数 |
key_t key |
IPC_PRIVATE(0):会建立新共享内存对象 |
大于0的 int 型整数 : 通常要求此值来源于ftok函数返回的IPC键值 |
||
size_t size |
0:只获取共享内存时指定为0 |
|
大于0的整数:新建的共享内存大小,以字节为单位(实际共享内存的开辟是以页为单位的,4k 4094字节) |
||
int shmflg |
0:取共享内存标识符,若不存在则函数会报错 |
|
IPC_CREAT:如果内核中不存在键值与key相等的消息队列,则新建一个共享内存;如果存在这样的共享内存,返回此共享内存的标识符 |
||
IPC_CREAT|IPC_EXCL:如果内核中不存在键值与key相等的共享内存,则新建一个共享内存;如果存在这样的共享内存则报错 |
||
函数 |
成功:返回共享内存的标识符 |
|
出错:-1,错误原因存于error中 |
||
附加说明 |
上述shmflg参数为模式标志参数,使用时需要与IPC对象存取权限(如0600)进行|运算来确定共享内存的存取权限 |
|
错误代码 |
EINVAL:参数size小于SHMMIN或大于SHMMAX |
函数 |
void *shmat(int shmid, const void *shmaddr, int shmflg) |
|
函数 |
连接共享内存标识符为shmid的共享内存,连接成功后把共享内存区对象映射到调用进程的地址空间,随后可像本地空间一样访问 |
|
函数 |
int shmid |
共享内存标识符 |
const void *shmaddr |
指定共享内存出现在进程内存地址的什么位置,直接指定为NULL让内核自己决定一个合适的地址位置 |
|
int shmflg |
SHM_RDONLY:为只读模式 |
|
函数 |
成功:映射好的共享内存地址 |
|
出错:-1,错误原因存于error中 |
||
附加说明 |
fork后子进程继承已连接的共享内存地址。exec后该子进程与已连接的共享内存地址自动脱离(detach)。进程结束后,已连接的共享内存地址会自动脱离(detach) |
|
错误代码 |
EACCESS:权限不够 |
函数 |
int shmdt(const void *shmaddr) |
|
函数 |
与shmat函数相反,是用来断开与共享内存附加点的地址,禁止本进程访问此片共享内存 |
|
函数 |
const void *shmaddr : 连接的共享内存的起始地址 |
|
函数 |
成功:0 |
|
出错:-1,错误原因存于error中 |
||
附加说明 |
本函数调用并不删除所指定的共享内存区,而只是将先前用shmat函数连接(attach)好的共享内存脱离(detach)目前的进程 |
|
错误代码 |
EINVAL:无效的参数shmaddr |
函数 |
int shmctl(int shmid, int cmd, struct shmid_ds *buf) |
|
函数 |
对共享内存对象的控制 |
|
函数 |
int shmid |
共享内存标识符 |
int cmd |
IPC_STAT:得到共享内存的状态,把共享内存的shmid_ds结构复制到buf中 |
|
IPC_SET:改变共享内存的状态,把buf所指的shmid_ds结构中的uid、gid、mode复制到共享内存的shmid_ds结构内 |
||
IPC_CREAT|IPC_EXCL:如果内核中不存在键值与key相等的消息队列,则新建一个消息队列;如果存在这样的消息队列则报错 |
||
struct shmid_ds *buf |
IPC_RMID:删除共享内存对象 |
|
函数 |
成功:0 |
|
出错:-1,错误原因存于error中 |
||
附加说明 |
上述msgflg参数为模式标志参数,使用时需要与IPC对象存取权限(如0600)进行|运算来确定消息队列的存取权限 |
|
错误代码 |
EACCESS:参数cmd为IPC_STAT,确无权限读取该共享内存 |
struct shmid_ds
{
struct ipc_perm shm_perm; /* 所有者和权限 */
size_t shm_segsz; /* 段大小,以字节为单位 */
time_t shm_atime; /* 最后挂接时间 */
time_t shm_dtime; /* 最后取出时间 */
time_t e_t shm_ctime; /* 最后修改时间 */
pid_t shm_cpid; /* 建立者PID */
pid_t shm_lpid; /* 最后调用shmat()/shmdt()的PID */
shmatt_t shm_nattch; /* 现在挂接的数量 */
__syscall_ulong_t __glibc_reserved4;
__syscall_ulong_t __glibc_reserved5;
};
struct ipc_perm
{
key_t __key; /* Key提供给semget(2) */
uid_t uid; /* owner的有效UID */
gid_t gid; /* owner的有效GID */
uid_t cuid; /* 创建器的有效UID */
gid_t cgid; /* E创建者的有效GID */
unsigned short mode; /* 权限 */
unsigned short __seq; /* 序列号 */
};
如果用shmget创建了一个新的消息队列对象时,则shmid_ds结构成员变量的值设置如下:
进程a 将数据写入共享内存后 用信号通知进程b
进程b捕捉到信号后 将信息打印输出
/****************进程a******************/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define SIZE 4096
int createShm();
int main()
{
int shmid = createShm();
int* p = shmat(shmid,NULL,0);
if(p == (void*)-1)
{
perror("shmat");
exit(3);
}
printf("等待读取进程b的pid\n");
while(*p == 0);
pid_t pid = *p;//取到进程b的pid
char data[40] = "\0";
printf("输入要写入共享内存的字符串:\n");
gets(data);
strcpy((char*)p,data);//写入数据
printf("已读取进程b的pid,数据已写入共享内存\n");
kill(pid,SIGUSR1);
printf("信号已发送给进程b\n");
int retval = shmdt(p);
if(retval == -1)
{
perror("shmdt");
exit(4);
}
return 0;
}
int createShm()
{
key_t key = ftok("/home",1);
if(key == -1)
{
perror("ftok");
exit(1);
}
printf("key:%d\n",key);
int shmid = shmget(key,50,0);
if(shmid == -1)
{
perror("shmget");
exit(2);
}
printf("shmid:%d\n",shmid);
return shmid;
}
/****************进程b******************/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define SIZE 4096
int createShm();
void sigFun(int s);
int main()
{
signal(SIGUSR1,sigFun);
int shmid = createShm();
int* p = (int*)shmat(shmid,NULL,0);
if(p == (int*)-1)
{
perror("shmat");
exit(3);
}
*p = getpid();//把pid写入共享内存
printf("已经将pid写入共享内存,等待进程a给我发信号\n");
pause();
printf("read:%s\n",(char*)p);
int retval = shmdt(p);
if(retval == -1)
{
perror("shmdt");
exit(4);
}
shmctl(shmid,IPC_RMID,NULL);
printf("共享空间对象已删除\n");
return 0;
}
int createShm()
{
key_t key = ftok("/home",1);
if(key == -1)
{
perror("ftok");
exit(1);
}
printf("key:%#x\n",key);
int shmid = shmget(key,50,0);
if(shmid == -1)
{
perror("shmget");
exit(2);
}
printf("shmid:%d\n",shmid);
return shmid;
}
void sigFun(int s)
{
printf("收到信号\n");
}
消息队列是内核地址空间中的内部链表,每个消息队列都有一个队列头,用结构struct msg_queue来描述。队列头中包含了该队列的大量信息,包括消息队列的键值、用户ID、组ID、消息数目、读写进程ID等。通过Linux内核在各个进程之间传递内容。
步骤:
头文件
LINUX终端下命令:
ipcs -q 查看内核中消息队列
ipcrm -q msqid 删除消息队列
函数 |
int msgget(key_t key, int msgflg) |
|
函数 |
得到消息队列标识符或创建一个消息队列对象并返回消息队列标识符 |
|
函数 |
key_t key |
IPC_PRIVATE(0):建立新的消息队列 |
大于0的 int 型整数 : 通常要求此值来源于ftok函数返回的IPC键值 |
||
int msgflg |
0:取消息队列标识符,若不存在则函数会报错 |
|
IPC_CREAT:如果内核中不存在键值与key相等的消息队列,则新建一个消息队列;如果存在这样的消息队列,返回此消息队列的标识符 |
||
IPC_CREAT|IPC_EXCL:如果内核中不存在键值与key相等的消息队列,则新建一个消息队列;如果存在这样的消息队列则报错 |
||
函数 |
成功:返回消息队列的标识符 |
|
出错:-1,错误原因存于error中 |
||
附加说明 |
上述msgflg参数为模式标志参数,使用时需要与IPC对象存取权限(如0600)进行|运算来确定消息队列的存取权限 |
|
错误代码 |
EACCES:指定的消息队列已存在,但调用进程没有权限访问它 |
函数 |
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg) |
|
函数 |
将msgp消息写入到标识符为msqid的消息队列 |
|
函数 |
int msqid |
消息队列标识符 |
const void *msgp |
发送给队列的消息。msgp可以是任何类型的结构体,但第一个字段必须为long类型,即表明此发送消息的类型,msgrcv根据此接收消息。msgp定义的参照格式如下: |
|
size_t msgsz |
要发送消息的大小,不含消息类型占用的4个字节,即 text 的长度 |
|
int msgflg |
0:当消息队列满时,msgsnd将会阻塞,直到消息能写进消息队列 |
|
IPC_NOWAIT:当消息队列已满的时候,msgsnd函数不等待立即返回,出错并返回-1 |
||
IPC_NOERROR:若发送的消息大于参数2的msgsz字节,则把该消息截断,截断部分将被丢弃,且不通知发送进程。 |
||
函数 |
成功:0 |
|
出错:-1,错误原因存于error中 |
||
附加说明 |
发送到消息队列中的实际字节数遵循结构体的内存对齐规则 |
|
错误代码 |
EAGAIN:参数msgflg设为IPC_NOWAIT,而消息队列已满 |
函数 |
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg) |
|
函数 |
从标识符为msqid的消息队列读取消息并存于msgp中,读取后把此消息从消息队列中删除 |
|
函数 |
int msqid |
消息队列标识符 |
const void *msgp |
存放消息的结构体,结构体类型要与msgsnd函数发送的类型相同 |
|
size_t msgsz |
要接收消息的大小,不含消息结构体第一个成员即消息类型占用的sizeof(long)个字节 |
|
long msgtyp |
0:接收第一个消息 |
|
>0:接收类型等于msgtyp的第一个消息 |
||
<0:接收类型小于或者等于msgtyp绝对值的第一个消息 |
||
int msgflg |
0: 阻塞式接收消息,没有该类型的消息msgrcv函数一直阻塞等待 |
|
IPC_NOWAIT:如果没有返回条件的消息调用立即返回,此时错误码为ENOMSG |
||
IPC_EXCEPT:与msgtyp配合使用返回队列中第一个类型不为msgtype的消息 |
||
IPC_NOERROR:如果队列中满足条件的消息内容大于参数3所请求的msgsz字节,则把该消息截断,截断部分将被丢弃,且不通知接收进程 |
||
函数 |
成功:实际读取到的消息字节数 |
|
出错:-1,错误原因存于error中 |
||
附加说明 |
接收消息时,即使截断了消息,消息队列中这条消息的所有字节也会被删除 |
|
错误 |
E2BIG:消息数据长度大于msgsz而msgflag没有设置IPC_NOERROR |
函数 |
int msgctl(int msqid, int cmd, struct msqid_ds *buf) |
|
函数 |
获取或者设置消息队列的属性 |
|
函数 |
int msqid |
消息队列标识符 |
int cmd |
IPC_STAT:获得msqid的消息队列头信息到参数3 buf中 |
|
IPC_SET:设置消息队列的属性,要设置的属性需先存储在buf中,可设置的属性包括:msg_perm.uid、msg_perm.gid、msg_perm.mode以及msg_qbytes |
||
struct msqid_ds *buf |
消息队列管理结构体,结构体类型定义在msq.h中 |
|
函数 |
成功:0 |
|
出错:-1,错误原因存于error中 |
||
错误代码 |
EACCESS:参数cmd为IPC_STAT,确无权限读取该消息队列 |
struct msqid_ds {
struct ipc_perm msg_perm; /* 用于存放消息队列的许可权限信息,其中包括访问许可信息,队列创建者有关信息*/
struct msg *msg_first; /* 队列上的第一个消息,未使用 */
struct msg *msg_last; /* 队列中的最后一条消息,未使用 */
__kernel_time_t msg_stime; /* 最后msgsnd时间,发送到队列的最后一个消息的时间戳 */
__kernel_time_t msg_rtime; /* 最后msgrcv时间,从队列获取的最后一个消息的时间戳 */
__kernel_time_t msg_ctime; /* 最后更改时间,对队列进程最后一次变动的时间戳 */
unsigned long msg_lcbytes; /* 重用垃圾字段为32位 */
unsigned long msg_lqbytes; /* 同上 */
unsigned short msg_cbytes; /* 当前队列上的字节数 */
unsigned short msg_qnum; /* 队列中的消息数 */
unsigned short msg_qbytes; /* 队列上的最大字节数 */
__kernel_ipc_pid_t msg_lspid; /* 发送最后一个消息进程的PID */
__kernel_ipc_pid_t msg_lrpid; /* 接收最后一个消息进程的PID */
};
struct ipc_perm
{
__kernel_key_t key; /* 函数msgget()使用的键值,用于区分消息队列 */
__kernel_uid_t uid; /* 用户者的UID */
__kernel_gid_t gid; /* 用户者的GID */
__kernel_uid_t cuid; /* 建立者的UID */
__kernel_gid_t cgid; /* 建立者的GID */
__kernel_mode_t mode; /* 权限 */
unsigned short seq; /* 序列号 */
};
如果用msgget创建了一个新的消息队列对象时,则msqid_ds结构成员变量的值设置如下:
运行两个进程,进程A命令行传参传入普通文件名,进程B打印出文件内容
/***************queSend.c*******************/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
typedef struct
{
long msgType;
char msgText[300];
}Msg_t;
int createMQ();
void getMsgStat(int msqid,struct msqid_ds msgInfo);
void setMsgStat(int msqid,struct msqid_ds* msgInfo);
int main(int argc,char* argv[])
{
if(argc != 2)
{
printf("argc error\n");
exit(0);
}
printf("PID:%d\n",getpid());
int msqid = createMQ();
struct msqid_ds msgInfo;
getMsgStat(msqid,msgInfo);
FILE* fp = fopen(argv[1],"r");
if(fp == NULL)
{
perror("fopen");
exit(7);
}
Msg_t message = {1,"\0"};
while(fgets(message.msgText,sizeof(message.msgText),fp) != NULL)//读一行
{
int ret = msgsnd(msqid,&message,strlen(message.msgText)+1,0);//队列满 阻塞
bzero(message.msgText,sizeof(message.msgText));
if(ret == -1)
{
perror("msgsnd");
exit(3);
}
}
fclose(fp);
printf("Message send over\n");
getMsgStat(msqid,msgInfo);
printf("touch Enter to destroy the Message queue\n");
getchar();
int ret = msgctl(msqid,IPC_RMID,NULL);
if(ret == -1)
{
perror("msgctl");
exit(4);
}
return 0;
}
int createMQ()
{
key_t key = ftok(".",50);
if(key == -1)
{
perror("ftok");
exit(1);
}
printf("key = %d\n",key);
int msqid = msgget(key,0666|IPC_CREAT);
if(msqid == -1)
{
perror("msgget");
exit(2);
}
printf("msqid = %d\n",msqid);
return msqid;
}
void getMsgStat(int msqid,struct msqid_ds msgInfo)
{
int ret = msgctl(msqid,IPC_STAT,&msgInfo);
if(ret<0)
{
perror("msgctl");
exit(5);
}
printf("\n");
printf("队列中能容纳的字节的最大数目: %ld bytes\n",msgInfo.msg_qbytes);
printf("当前在队列上所驻留的字节数: %ld bytes\n",msgInfo.msg_cbytes);
printf("当前处于队列中的消息数目: %ld \n",msgInfo.msg_qnum);
printf("当前消息队列的读写权限: %#x\n",msgInfo.msg_perm.mode);
printf("发送最后一个消息进程的PID: %d\n",msgInfo.msg_lspid);
printf("接收最后一个消息进程的PID: %d\n",msgInfo.msg_lrpid);
printf("发送到队列的最后一个消息的时间戳: %s",ctime(&(msgInfo.msg_stime)));
printf("从队列中获取的最后一个消息的时间戮:%s",ctime(&(msgInfo.msg_rtime)));
printf("对队列进行最后一次变动的时间戳: %s",ctime(&(msgInfo.msg_ctime)));
printf("消息队列用户ID: %d\n",msgInfo.msg_perm.uid);
printf("消息队列用户组ID: %d\n",msgInfo.msg_perm.gid);
printf("\n");
}
void setMsgStat(int msqid,struct msqid_ds* msgInfo)
{
(*msgInfo).msg_perm.uid = 8;
(*msgInfo).msg_perm.gid = 8;
(*msgInfo).msg_qbytes = 16388;
int ret = msgctl(msqid,IPC_SET,msgInfo);
if(ret == -1)
{
perror("msqctl");
exit(6);
}
}
/***************queRece.c*******************/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
typedef struct
{
long msgType;
char msgText[200];
}Msg_t;
int getMQ();
void getMsgStat(int msqid,struct msqid_ds msgInfo);
int main()
{
printf("PID:%d\n",getpid());
int msqid = getMQ();
struct msqid_ds msgInfo;
Msg_t message = {.msgText = "\0"};
getMsgStat(msqid,msgInfo);
while(1)
{
sleep(1); //接收队列第一个消息,不阻塞,截断为指定长度
int ret = msgrcv(msqid,&message,sizeof(message.msgText),0,IPC_NOWAIT|MSG_NOERROR);
printf("%s",message.msgText);
bzero(message.msgText,sizeof(message.msgText));
if(ret == -1)
{
printf("Message Queue is Empty\n");
break;
}
}
getMsgStat(msqid,msgInfo);
printf("After Destroy Message Queue Touch Enter\n");
getchar();
getMQ();
return 0;
}
void printMessage(Msg_t message,int bytes)
{
printf("read from Message Queue %d bytes:\ntype:%ld msgText:%s\n",bytes,message.msgType,message.msgText);
}
int getMQ()
{
key_t key = ftok(".",50);
if(key == -1)
{
perror("ftok");
exit(1);
}
printf("key = %d\n",key);
int msqid = msgget(key,0);
if(msqid == -1)
{
perror("msgget");
exit(2);
}
printf("msqid = %d\n",msqid);
return msqid;
}
void getMsgStat(int msqid,struct msqid_ds msgInfo)
{
int ret = msgctl(msqid,IPC_STAT,&msgInfo);
if(ret<0)
{
perror("msgctl");
exit(5);
}
printf("\n");
printf("队列中能容纳的字节的最大数目: %ld bytes\n",msgInfo.msg_qbytes);
printf("当前在队列上所驻留的字节数: %ld bytes\n",msgInfo.msg_cbytes);
printf("当前处于队列中的消息数目: %ld \n",msgInfo.msg_qnum);
printf("当前消息队列的读写权限: %#x\n",msgInfo.msg_perm.mode);
printf("发送最后一个消息进程的PID: %d\n",msgInfo.msg_lspid);
printf("接收最后一个消息进程的PID: %d\n",msgInfo.msg_lrpid);
printf("发送到队列的最后一个消息的时间戳: %s",ctime(&(msgInfo.msg_stime)));
printf("从队列中获取的最后一个消息的时间戮:%s",ctime(&(msgInfo.msg_rtime)));
printf("对队列进行最后一次变动的时间戳: %s",ctime(&(msgInfo.msg_ctime)));
printf("消息队列用户ID: %d\n",msgInfo.msg_perm.uid);
printf("消息队列用户组ID: %d\n",msgInfo.msg_perm.gid);
printf("\n");
}
信号量是一种计数器,用来控制对多个进程共享的资源所进行的访问,为获取共享资源,进程需执行以下操作:
①P:如果sv的值大于零,就给它减1;如果它的值为零,就挂起该进程的执行
②V:如果有其他进程因等待sv而被挂起,就让它恢复运行,如果没有进程因等待sv而挂起,就给它加1
PV都是原子操作:不能进行分割的操作,一次性完成的操作执行期间不允许有中断的发生
头文件
步骤:
LINUX终端下命令:
ipcs -s 查看内核中信号量
ipcrm -s semid 删除信号量
函数 |
int semget(key_t key, int nsems, int semflg) |
|
函数 |
得到一个信号量集标识符或创建一个信号量集对象并返回信号量集标识符 |
|
函数 |
key_t key |
0(IPC_PRIVATE):会建立新信号量集对象 |
大于0的 int 型整数 : 通常要求此值来源于ftok函数返回的IPC键值 |
||
int nsems |
创建信号量集中信号量的个数,该参数只在创建信号量集时有效 |
|
int semflg |
0:取信号量集标识符,若不存在则函数会报错 |
|
IPC_CREAT:如果内核中不存在键值与key相等的信号量集,则新建一个信号量集;如果存在这样的信号量集,返回此信号量集的标识符 |
||
IPC_CREAT|IPC_EXCL:如果内核中不存在键值与key相等的信号量集,则新建一个消息队列;如果存在这样的信号量集则报错 |
||
函数 |
成功:返回信号量集的标识符 |
|
出错:-1,错误原因存于error中 |
||
附加说明 |
上述semflg参数为模式标志参数,使用时需要与IPC对象存取权限(如0600)进行|运算来确定信号量集的存取权限 |
|
错误代码 |
EACCESS:没有权限 |
函数 |
int semop(int semid, struct sembuf *sops, unsigned nsops) |
||
函数 |
对信号量集标识符为semid中的一个或多个信号量进行P(-1)操作或V(+1)操作 |
||
函数 |
int semid |
信号量集标识符 |
|
struct sembuf *sops |
指向进行操作的信号量集结构体数组的首地址,此结构的具体说明如下: |
short semnum:信号量集合中的信号量编号,0代表第1个信号量 |
|
short val: |
|||
short flag: |
|||
unsigned nsops |
进行操作信号量的个数,即sops结构变量的个数,需大于或等于1。最常见设置此值等于1,只完成对一个信号量的操作 |
||
函数 |
成功:返回信号量集的标识符 |
||
出错:-1,错误原因存于error中 |
|||
错误代码 |
E2BIG:一次对信号量个数的操作超过了系统限制 |
函数 |
int semctl(int semid, int semnum, int cmd, union semun arg) |
|
函数 |
在指定的信号集或信号集内的某个信号上执行控制操作 |
|
函数 |
int semid |
信号量集标识符 |
int semnum |
信号量集数组上的下标,表示某一个信号量 |
|
int cmd |
见下表 |
|
union semun arg |
union semun |
|
函数 |
成功:大于或等于0,具体说明见下表 |
|
出错:-1,错误原因存于error中 |
||
附加说明 |
semid_ds结构见下文信号量集内核结构定义 |
|
错误代码 |
EACCESS:权限不够 |
上表中int 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成员的值设置信号量集合中单个信号量的值 |
struct semid_ds {
struct ipc_perm sem_perm; /* permissions .. see ipc.h */
__kernel_time_t sem_otime; /* last semop time */
__kernel_time_t sem_ctime; /* last change time */
struct sem *sem_base; /* ptr to first semaphore in array */
struct sem_queue *sem_pending; /* pending operations to be processed */
struct sem_queue **sem_pending_last; /* last pending operation */
struct sem_undo *undo; /* undo requests on this array */
unsigned short sem_nsems; /* no. of semaphores in array */
};
struct ipc_perm
{
__kernel_key_t key;
__kernel_uid_t uid;
__kernel_gid_t gid;
__kernel_uid_t cuid;
__kernel_gid_t cgid;
__kernel_mode_t mode;
unsigned short seq;
};
如果用semget创建了一个新的信号量集对象时,则semid_ds结构成员变量的值设置如下:
进程A运行时阻塞,进程B给进程A解除阻塞
/***************进程A*******************/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
int createSem();
int main()
{
//创建
int semid = createSem();
//设置信号量的值
int retval = semctl(semid,0,SETVAL,0);//0灯集下标,0,初始值
struct sembuf s;
s.sem_num = 0;
s.sem_op = -1;
s.sem_flg = SEM_UNDO;
printf("我想要继续执行,但是被信号量阻塞了\n");
semop(semid,&s,1);//子阻塞
printf("信号量已允许我操作,继续执行\n");
semctl(semid,0,IPC_RMID,0);
return 0;
}
int createSem()
{
key_t key = ftok("/home",1);
if(key == -1)
{
perror("ftok");
exit(1);
}
int semid = semget(key,1,IPC_CREAT|0777);// 1 信号量集中信号量个数
if(semid ==-1)
{
perror("semget");
exit(1);
}
printf("semid:%d\n",semid);
return semid;
}
/***************进程B*******************/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
int createSem();
int main()
{
int semid = createSem();
int retval = semctl(semid,0,SETVAL,0);//设置初始值0
struct sembuf s;
s.sem_num = 0;
s.sem_op = 1;
s.sem_flg = SEM_UNDO;
semop(semid,&s,1);
printf("已经给阻塞的进程放行\n");
return 0;
}
int createSem()
{
key_t key = ftok("/home",1);
if(key == -1)
{
perror("ftok");
exit(1);
}
int semid = semget(key,1,IPC_CREAT|0777);// 1 信号量集中信号量个数
if(semid ==-1)
{
perror("semget");
exit(1);
}
printf("semid:%d\n",semid);
return semid;
}