System V IPC对象


查看IPC对象 ipcs
查看共享内存对象 ipcs -m
查看消息队列对象 ipcs -q

  • ftok()系统IPC键值的格式转换函数
函数原型key_t ftok(const char *pathname,int proj_id);
头文件#include<sys/types.h>
#include<sys/ipc.h>
函数功能系统IPC键值的格式转换函数
函数参数const char *pathname: 文件路径名
int proj_id: 子序号
函数返回成功:返回生成的key
出错:-1
1.共享内存

共享内存是一种最为高效的进程间通信方式。进程可以直接读写内存,而不需要任何数据拷贝。

1.1 关键函数
  • shmget()创建打开共享内存
函数原型int shmget(key_t key,int size,int shmflg);
头文件#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>
函数功能得到一个共享内存标识符
或创建一个共享内存对象并返回共享内存标识符
函数参数key: IPC_PRIVATE或ftok的返回值,私有的key值为0x00000000
size: 共享内存大小
shmflg: 同open函数的权限位,也可以用8进制表示法
函数返回成功: 共享内存段标识符
出错: -1
  • shmat()映射共享内存
函数原型void *shmat(int shmid, const void *shmaddr, int shmflg);
头文件#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
函数功能把指定的共享内存映射到进程的地址空间用于访问
函数参数shmid:要映射的共享内存区标识符
shmaddr:将共享内存映射到指定地址(若为NULL,则表示由系统自动完成映射)
shmflg :
    SHM_RDONLY:共享内存只读
    默认0:共享内存可读写
函数返回成功:映射后的地址
出错:-1
  • shmdt()撤销共享内存映射
函数原型int shmdt(const void *shmaddr);
头文件#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
函数功能把指定的共享内存映射到进程的地址空间用于访问
函数参数shmaddr:共享内存映射后的地址
函数返回成功:0
出错:-1
  • shmctl()控制共享内存对象
函数原型int shmctl(int shmid, int cmd, struct shmid_ds *buf);
头文件#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
函数功能把指定的共享内存映射到进程的地址空间用于访问
函数参数shmid:要操作的共享内存标识符
cmd :
   IPC_STAT (获取对象属性)
   IPC_SET (设置对象属性)
   IPC_RMID (删除对象)
buf : 指定IPC_STAT/IPC_SET时用以保存/设置属性,cmd为IPC_RMID时也可以为NULL
函数返回成功:0
出错:-1
1.2 代码示例
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<unistd.h>
#include<string.h>
#include<sys/wait.h>
int main()
{	
	//根据文件路径和序列号,获得key值
	key_t key = ftok(".",'a');
	if(0 > key)
	{
		perror("ftok");
		return -1;
	}
	//创建或打开共享内存,通过ftok得到的key
	int shm_id = shmget(key,1024*1024,IPC_CREAT|0644);
	//创建或打开共享内存,设置私有的key
	//int shm_id = shmget(IPC_PRIVATE,1024*1024,IPC_CREAT|0644);
	if(0>shm_id)
	{
		perror("shmget");
		return -1;
	}

	//2.将共享内存映射到用户空间
	char *p = shmat(shm_id,NULL,0);
	if(p == (char *)-1){
		perror("shmat");
		return -1;
	}
	pid_t pid;
	if((pid = fork())<0){
		perror("fork");
		return -1;
	}else if(pid == 0){
		strcpy(p,"hello,world");
		strcpy(p,"mm");
		/*
			此时共享内存区域中字符是这样存放的:
			0  1  2  3  4  5  6  7  8  9  10  11
			m  m  \0 l  o  ,  w  o  r  l  d   \0  
		*/
	}else{
		char buf[100] = {0};
		wait(NULL);
		printf("p = %s\n",p);
		printf("p+3 = %s\n",p+3);
		
		/*
			打印结果:
			p = mm
			p+3 = lo,world 
		*/
	}
	//3.解除共享内存映射
	if(shmdt(p)<0){
		perror("shmdt");
		return -1;
	}
	//4.删除共享内存
	if(shmctl(shm_id,IPC_RMID,NULL)<0){
		perror("shmctl");
		return -1;
	}
	system("ipcs -m");
	return 0;
}
2.消息队列
2.1 关键函数
  • msgget()创建打开消息队列
函数原型int msgget(key_t key, int msgflg);
头文件#include <sys/ipc.h>
#include <sys/msg.h>
函数功能用于创建一个新的或打开一个已经存在的消息队列,此消息队列与key相对应。
函数参数key:消息队列关联的键。
msgflg:消息队列的建立标志和存取权限。
   IPC_CREAT :如果key不存在,则创建 (类似open函数的O_CREAT)
   IPC_EXCL :如果key存在,则返回失败 (类似open函数的O_EXCL)
   IPC_NOWAIT :如果需要等待,则直接返回错误
函数返回成功执行时,返回消息队列标识符
失败返回-1,errno被设为以下的某个值
    EACCES:指定的消息队列已存在,但调用进程没有权限访问它,而且不拥有CAP_IPC_OWNER权能
    EEXIST:key指定的消息队列已存在,而msgflg中同时指定IPC_CREAT和IPC_EXCL标志
    ENOENT:key指定的消息队列不存在同时msgflg中不指定IPC_CREAT标志
  • msgctl()控制消息队列对象
函数原型int msgctl(int msqid, int cmd, struct msqid_ds *buf);
头文件#include <sys/ipc.h>
#include <sys/msg.h>
函数功能用来对消息队列的基本属性进行控制、修改。
函数参数msqid:消息队列标识符。
cmd:执行的控制命令(在ipc.h中定义):
   IPC_RMID :删除消息队列。从系统中删除给消息队列以及仍在该队列上的所有数据,这种删除立即生效。仍在使用这一消息队列的其他进程在它们下一次试图对此队列进行操作时,将出错,并返回EIDRM。 此命令只能由如下两种进程执行:
           1.其有效用户ID等于msg_perm.cuid或msg_perm.guid的进程。
           2.另一种是具有超级用户特权的进程。  
   IPC_SET :设置消息队列的属性。按照buf指向的结构中的值,来设置此队列的msqid_id结构。该命令的执行特权与上一个相同。
   IPC_STAT:读取消息队列的属性。取得此队列的msqid_ds结构,并存放在buf*中。
   IPC_INFO:读取消息队列基本情况。
buf:
函数返回成功:0
失败:-1
  • 消息发送msgsnd()
函数原型int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
头文件#include <sys/ipc.h>
#include <sys/msg.h>
函数功能将新的消息添加到消息队列的尾端
函数参数msqid:消息队列标识符(由msgget生成)
msgp:指向用户自定义的缓冲区
msgsz:接收信息的大小。范围在0~系统对消息队列的限制值
msgflg:指定在达到系统为消息队列限定的界限时应采取的操作。
    IPC_NOWAIT: 如果需要等待,则不发送消息并且调用进程立即返回,errno为EAGAIN
    设置为0:则调用进程挂起执行,直到达到系统所规定的最大值为止,并发送消息
函数返回成功0,失败-1
  • 消息接收msgrcv()
函数原型ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
头文件#include <sys/ipc.h>
#include <sys/msg.h>
函数功能从消息队列msqid读取指定的消息(由msgtyp参数决定)
函数参数msqid:消息队列标识符
msgp:指向用户自定义的缓冲区(msgp)
msgsz:如果收到的消息大于msgsz,并且msgflg&MSG_NOERROR为真,则将该消息截至msgsz字节,并且不发送截断提示
msgtyp:用于指定请求的消息类型:
    msgtyp=0:收到的第一条消息,任意类型。
    msgtyp>0:收到的第一条msgtyp类型的消息。
    msgtyp<0:收到的第一条最低类型(小于或等于msgtyp的绝对值)的消息。
msgflg:用于指定所需类型的消息不再队列上时的将要采取的操作:
    IPC_NOWAIT:若需要等待,则调用进程立即返回,同时返回-1,并设置errno为ENOMSG
    未设置IPC_NOWAIT:则调用进程挂起执行,直至出现以下任何一种情况发生:
         1.某一所需类型的消息被放置到队列中。
         2.msqid从系统被删除,当该情况发生时,返回-1,并将errno设为EIDRM。
         3.调用进程收到一个要捕获的信号,在这种情况下,未收到消息,并且调用进程按signal(SIGTRAP)中指定的方式恢复执行。(疑问??)
函数返回成功0,失败-1
  • 自定义缓冲区msgp
struct msgbuf{
		/*type是必需的*/
    	long type;				//第一个必须为long type (消息类型)	
    	/*其他可以自定义*/
    	struct sockaddr_in addr;//地址结构
		int id;					//进程id
		int operate;			//操作
    	char buf[1024];  		//消息
};
2.2 代码示例
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>

struct msgbuf{
    long type;
    int data;
    char buf[32];
};

int main(int argc, char *argv[])
{
    //创建或打开消息队列
    key_t key = ftok(".", 'a');
    if (key < 0){
        perror("ftok");
        return -1;
    }
    int msg_id = msgget(key, IPC_CREAT | 0644);
    //int msg_id = msgget(IPC_PRIVATE, 0644);
    if (msg_id < 0){
        perror("msgget");
        return -1;
    }
    system("ipcs -q");
    pid_t pid;
    if ((pid = fork()) < 0){
        perror("ftok");
        return -1;
    }
    /*子进程send*/ 
    else if (pid == 0){
        //向结构体中添加 消息 和 消息的类型
        struct msgbuf msg_send;
        msg_send.type = 100;//本条消息的类型
        msg_send.data = 13;
        strcpy( msg_send.buf, "hello wrold");
        //发送消息到消息队列中
        msgsnd(msg_id, &msg_send, sizeof(msg_send), 0);
    }
    /*父进程recv*/ 
    else {
        struct msgbuf msg_recv;
        memset(&msg_recv, 0, sizeof(msg_recv));
        //读取消息队列中type为100的数据
        msgrcv(msg_id, &msg1, sizeof(msg_recv), 100, 0);
        printf("%d, %s\n", msg_recv.data, msg_recv.buf);
    }
    //删除消息队列
    msgctl(msg_id, IPC_RMID, NULL);
    system("ipcs -q");
    return 0;
}
3.信号灯
3.1 关键函数
  • semget()创建信号灯
函数原型int semget(key_t key, int nsems, int semflg);
头文件#include <sys/ipc.h>
#include <sys/sem.h>
函数功能创建信号灯
函数参数key:ftok函数创建。
nsems:创建的信号量的个数,每个信号量以数组方式存储。
semflg:信号量集合的权限。
函数返回成功:返回信号量集的IPC标识符
失败:返回-1
  • semctl()控制信号灯
函数原型int semctl(int semid, int semnum, int cmd, union semun arg)
头文件#include <sys/ipc.h>
#include <sys/sem.h>
函数功能控制信号灯
函数参数semid:信号量集标识符
semnum:信号量集数组上的下标,表示某一个信号量
cmd:见下表
arg: 见union semun
函数返回成功:0
失败:-1
union semun {
	short val;          	/*SETVAL用的值*/
	struct semid_ds* buf; 	/*IPC_STAT、IPC_SET用的semid_ds结构*/
	unsigned short* array; 	/*SETALL、GETALL用的数组值*/
	struct seminfo *buf;   	/*为控制IPC_INFO提供的缓存*/
 } arg;

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成员的值设置信号量集合中单个信号量的值
3.2 代码示例
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
union semun {
    int              val;    /* Value for SETVAL */
    struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
    unsigned short  *array;  /* Array for GETALL, SETALL */
    struct seminfo  *__buf;  /* Buffer for IPC_INFO
    (Linux-specific) */
};

int main()
{
    int semid = semget(IPC_PRIVATE, 2, IPC_CREAT|0666);  //创建信号灯集2个灯
    if (semid < 0)
    {
        perror("semget");
        return -1;
    }

    union semun semval1 = {1};   
    semctl(semid, 0, SETVAL, semval1); //设置灯集中第一个灯初始值为1
    union semun semval2 = {0};
    semctl(semid, 1, SETVAL, semval2); //设置灯集中第二个灯为0

    int shmid = shmget(IPC_PRIVATE, 100, IPC_CREAT|0666);
    if (shmid < 0)
    {
        perror("shmget");
        return -1;
    
    void *p = shmat(shmid, NULL, 0);  
    if ((void *)-1 == p)
    {
        perror("shmat");
        return -1;
    }
    pid_t pid;
    if (0 > (pid = fork()))
    {
        perror("fork");
        return -1;
    }
    else if (0 == pid)
    {
        while(1){
            //p(0);   0为灯编号
            struct sembuf buf1 = {
                .sem_num = 0,    //信号灯编号
                .sem_op = -1,    //p操作
                .sem_flg = 0     //阻塞
            };
            semop(semid, &buf1, 1);

            strcpy((char*)p, "hello world!");
            //v(1);
            struct sembuf buf2 = {
                .sem_num = 1,   //信号灯编号
                .sem_op = 1,    //v操作
                .sem_flg = 0   //阻塞
            };
            semop(semid, &buf2, 1);

        sleep(1);
        }
    }
    else
    {
        while(1){
            //p(1);
            struct sembuf buf1 = {
                .sem_num = 1,    //信号灯编号
                .sem_op = -1,    //p操作
                .sem_flg = 0     //阻塞
            };
            semop(semid, &buf1, 1);
            printf("p = %s\n", (char*)p);
            //v(0);
            struct sembuf buf2 = {
                .sem_num = 0,   //信号灯编号
                .sem_op = 1,    //v操作
                .sem_flg = 0   //阻塞
            };
            semop(semid, &buf2, 1);
            sleep(1);
        }
        if (0 > shmdt(p))
        {
            perror("shmdt");
            return -1;
        }
        if (0 > shmctl(shmid, IPC_RMID, NULL))
        {
            perror("shmctl");
            return -1;
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值