13--信号量,共享内存和消息队列

进程间的通信

         (1) 基于文件的通信:

                            1. 普通文件(io/mmap)

                            2.有名管道文件

                            3.匿名管道文件

                            4.socket

       (2)基于内存的通信:

                            1.信号量 :用于管理对资源的访问。

                            2.共享内存:用于进程之间高校的地共享数据。

                             3.消息队列:在程序之间传递数据的一种简单方法。

使用到的命命令:

—————————————————————————

$ ipcs

$ ipcs -s    --------- 查看信号量

$ ipcs -m  ---------- 共享内存

$ ipcs -q   ----------- 消息队列

$ ipcrm -q 编号ID    ---------删除消息队列

——————————————————————————



一.信号量

1.semget函数

       #include <sys/types.h>
       #include <sys/ipc.h>
       #include <sys/sem.h>


       int semget(key_t key,  /// 为一个整数值,

                          int nsems,  为信号量数目,通常取值为1.

                          int semflg);/// 一组标志,通常使用 IPC_CREAT | IPC_EXCL 来创建新的,唯一的信号量。

   说明:创建一个信号量,获得一个信号量ID。

              如果成功:

                               返回信号量标识符。

             如果失败:

                             返回-1.

2. semop函数

       int semop(int semid,   有semget返回的信号量标识符。

                        struct sembuf *sops, 指向结构体数组的指针

                       unsigned nsops);   ///

         说明: 用于改变信号量的值


第二个参数指向的结构体数组中,每个数组元素至少包含以下成员

struct sembuf

{

           unsigned short sem_num; /* semaphore number */  信号编号,除非使用一组信号,否则一般为0
           short          sem_op;   /* semaphore operation */信号操作,一般有两种取值:-1(p操作,等待信号变为可用); +1(v操作,发送信号表示信号量现在已可用)
           short          sem_flg;  /* operation flags */通常设置为SEM_UNDO,
};

3. semctl 函数

         int semctl(int semid,    ///有semget返回的信号量标识符

                         int semnum,   /// 信号量编号,一般取值为0(表示是唯一信号量)

                         int cmd, ...);   ///   采取的行动

     说明: 直接控制信号量信息。

cmd有两个最常用的值:

   SETVAL: 用来把信号量初始化为一个已知的值。

  IPC_RMID:  用于删除一个已知无需继续使用的信号量标识符。

二.共享内存

1.shmget函数

         #include <sys/ipc.h>
       #include <sys/shm.h>

       int shmget(key_t key, /// ftok 函数可以取得唯一的key值

                         size_t size, /// 共享内存容量

                         int shmflg)/// 9个比特的权限标志

   说明: 创建共享内存,返回共享内存标识符。

第三个参数的常见两种方式:

     创建: (IPC_CREAT | IPC_EXCL)

     打开: 0

2.shmat 函数

       void *shmat(int shmid,/// 由shmget返回的共享内存标识符。

                            const void *shmaddr, ///

                               int shmflg);

       说明: 根据ID得到共享,访问内存数据。

3.shmdt函数

       int shmdt(const void *shmaddr);

      说明: 将共享内存从当前进程中分离,参数为shmat返回的地址指针。

4. shmctl 函数

       int shmctl(int shmid,    ///  shmget返回的共享内存标识符

                                int cmd,   ///  要采取的动作

                               struct shmid_ds *buf); /// 指向包含共享内存模式和访问权限的当前关联值

第二个参数cmd的可取值:

————————————————————————————

IPC_STAT    ------

IPC_SET      ------

IPC_RMID   --------删除共享内存段 


  第三个参数的struct  shmid_ds 结构体:

——————————————————————————————————

                 struct shmid_ds {
               struct ipc_perm shm_perm;    /* Ownership and permissions */
               size_t          shm_segsz;   /* Size of segment (bytes) */
               time_t          shm_atime;   /* Last attach time */
               time_t          shm_dtime;   /* Last detach time */
               time_t          shm_ctime;   /* Last change time */
               pid_t           shm_cpid;    /* PID of creator */
               pid_t           shm_lpid;    /* PID of last shmat(2)/shmdt(2) */
               shmatt_t        shm_nattch;  /* No. of current attaches */
               ...

           };
——————————————————————————————————————

编程模型:
               (1)  创建共享内存,得到一个ID;

               (2)把ID映射成虚拟地址(挂载);

               (3)使用虚拟地址访问内核共享内存

               (4)卸载(分离)虚拟地址, shmdt

               (5) 删除共享内存    shctl(修改/获取共享内存的属性)

案例:
shmA.c

/// shmA.c

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/shm.h>
#include <sys/ipc.h>

key_t  key;
int shmid;
int *p;
int i = 0;

void deal(int s)
{
	if(s==SIGINT)
	{
		//4.卸载共享内存shmdt
		shmdt(p);
		//5.删除共享内存shctl
		shmctl(shmid,IPC_RMID,0);
		exit(0);
	}
}


main()
{
	signal(SIGINT,deal);
	//1.创建共享内存, shmget
	key = ftok("/tmp",255);
	if(key == -1) printf("ftok error:%m\n"),exit(-1);
	
	shmid = shmget(key,4,IPC_CREAT | IPC_EXCL |0666);
	if(shmid == -1) printf("get error:%m\n"),exit(-1);
	//2.挂载共享内存shmat
	p = shmat(shmid,0,0);
	if(p == (int*)-1) printf("get error:%m\n"),exit(-1);
	
	//3.访问共享内存
	while(1)
	{
		*p = i;
		sleep(1);
		++i;
	}
}

shmB.c

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/shm.h>
#include <sys/ipc.h>

key_t key;
int shmid;
int *p;

void deal(int s)
{
	if(s==SIGINT)
	{
		//4. 卸载共享内存, shmdt
		shmdt(p);
		exit(0);
	}
}

main()
{
	signal(SIGINT,deal);
	//1.创建共享内存
	key = ftok("/tmp",255);
	if(key == -1) printf("ftok error:%m\n"),exit(-1);
	
	shmid = shmget(key,4,0);
	if(shmid == -1) printf("get error:%m\n"),exit(-1);
	//2.挂载共享内存shmat
	p = shmat(shmid,0,0);
	if(p == (int*)-1) printf("ar error:%m\n"),exit(-1);
	//3.访问共享内存
	while(1)
	{
		sleep(1);
		printf("%d\n",*p);
	}
}

补充:

——————————————————

$ ipcs -m  ------ 查看共享内存

$ ipcrm -m  555****(文件ID)   ------ 删除共享内存(这个文件ID是随机的)

三. 消息队列

       消息队列是在两个不相关进程之间传递数据的,优势是独立于发送和接收进程而存在

1. msgget函数-----------------创建消息队列函数

       int msgget(key_t key, int msgflg);

2. msgsnd函数-------------------发送消息

       int msgsnd(int msqid,   消息队列标识符

                  const void *msgp, /// 要发送的消息

                  size_t msgsz,   消息的长度

                  int msgflg);  / 发送消息的方式,建议为0


3.msgrcv函数 ---------------从消息队列中获取消息

       int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);

4.msgctl 函数

       int msgctl(int msqid, int cmd, struct msqid_ds *buf);

编程模型:

       创建消息队列

       使用消息队列

       删除队列

 msgA.c   发射部分
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/msg.h>

#define MAX_TEXT 	512

struct msgbuf
{
	long mtype;
	char mtext[MAX_TEXT];
};

int main()
{
	int flag = 1;
	key_t key;
	struct msgbuf  msg;
	int msgid;
	char buffer[BUFSIZ];
	
	// 1.创建消息队列
	key = ftok(".",200);
	if(key == -1) printf("frok err:%m\n"),exit(-1);
	printf("key=%d\n",key);
	
	msgid = msgget(key,0666 | IPC_CREAT);
	if(msgid == -1) printf("msgget err:%m\n"),exit(-1);
	
	while(flag)
	{
		printf("请输入您要发送的字符串(以“#”结束输入):\n");
		fgets(buffer,BUFSIZ,stdin);
		msg.mtype = 1;
		bzero(msg.mtext,sizeof(msg.mtext));
		strcpy(msg.mtext,buffer);
		if(msgsnd(msgid,&msg,MAX_TEXT,0) == -1) printf("msgsnd err:%m\n") ,exit(-1);
		
		if(strncmp(buffer,"#",1) == 0)
			flag = 0;
		
	}
	msgctl(msgid,IPC_RMID,0); // 删除队列
	exit(0);
}

 msgB.c  接收部分

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/msg.h>

struct msgbuf
{
	long mtype;
	char mtext[BUFSIZ];
};

int main()
{
	int flag = 1;
	key_t key;
	int msgid;
	struct msgbuf msg;
	
	key = ftok(".",200);
	if(key == -1) printf("ftok err:%m\n"),exit(-1);
	printf("key=%d\n",key);
	
	msgid = msgget(key,0666 | IPC_CREAT);
	if(msgid == -1) printf("msgget err:%m\n"),exit(-1);
	
	while(flag)
	{
		bzero(msg.mtext,sizeof(msg.mtext));
		if(msgrcv(msgid,(void *)&msg,BUFSIZ,0,0) == -1)
		 	printf("msgrcv err:%m\n"),exit(-1);
		printf("你接收到的数据是:%s\n",msg.mtext);
		if(strncmp(msg.mtext,"#",1) ==0 )
			flag = 0;
	}
	// 删除消息队列
	msgctl(msgid,IPC_RMID,0); 
	
}




评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值