LV5进程、线程和进程间通信(9)-消息队列和信号灯


前言


1.消息队列

概念:

消息队列是System V IPC对象的一种

1.1消息队列的使用:

1.1.1发送端:

1 申请Key--  key_t  ftok(const char *pathname, int proj_id);

函数ftok把一个已存在的路径名和一个整数标识符转换成一个key_t值,称为IPC键值(也称IPC key键值)

2打开/创建消息队列   msgget

3向消息队列发送消息   msgsnd

1.1.2接收端:

1打开/创建消息队列   msgget

2从消息队列接收消息   msgrcv

3 控制(删除)消息队列   msgctl

1.1.3打开/创建消息队列

 #include <sys/ipc.h>

 #include <sys/msg.h>

 int msgget(key_t key, int msgflg);

  成功时返回消息队列的id,失败时返回EOF

  key 和消息队列关联的key  IPC_PRIVATE 或 ftok

  msgflg  标志位  IPC_CREAT|0666  IPC_CREAT:没有创建,有则打开。

1.1.4发送消息

#include <sys/ipc.h>

 #include <sys/msg.h>

 int msgsnd(int msgid, const void *msgp, size_t size, int msgflg);

  成功时返回0,失败时返回-1

msgid   消息队列id

msgp    消息缓冲区地址

size    消息正文长度

msgflg   标志位 0 或 IPC_NOWAIT

msgflg:

0:当消息队列满时,msgsnd将会阻塞,直到消息能写进消息队列

IPC_NOWAIT:当消息队列已满的时候,msgsnd函数不等待立即返回

消息格式:

typedef struct{

long msg_type;

char buf[128];

}msgT;

注意:

1 消息结构必须有long类型的msg_type字段,表示消息的类型。

2消息长度不包括首类型 long

#include <stdio.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>

typedef struct{
    long msg_type;
    char buf[128];
}msgT;    

#define MSGLEN  (sizeof(msgT)-sizeof(long))

int main(){
    key_t key;
    int msgid;
    int ret;
    msgT msg;
    key = ftok(".",100);
    if(key<0){
        perror("ftok");
        return 0;
    }
    msgid = msgget(key,IPC_CREAT|0666);
    if(msgid<0){
        perror("msgget");
        return 0;
    }

    msg.msg_type = 1;
    strcpy(msg.buf,"this msg type 1");
    ret = msgsnd(msgid,&msg,MSGLEN,0);
    if(ret<0){
        perror("msgsnd");
        return 0;
    }    

    msg.msg_type = 2;
    strcpy(msg.buf,"this msg type 2");
    ret = msgsnd(msgid,&msg,MSGLEN,0);
    if(ret<0){
        perror("msgsnd");
        return 0;
    }
    msg.msg_type = 3;
    strcpy(msg.buf,"this msg type 3");
    ret = msgsnd(msgid,&msg,MSGLEN,0);
    if(ret<0){
        perror("msgsnd");
        return 0;
    }
    msg.msg_type = 4;
    strcpy(msg.buf,"this msg type 4");
    ret = msgsnd(msgid,&msg,MSGLEN,0);
    if(ret<0){
        perror("msgsnd");
        return 0;
    }
    msg.msg_type = 5;
    strcpy(msg.buf,"this msg type 5");
    ret = msgsnd(msgid,&msg,MSGLEN,0);
    if(ret<0){
        perror("msgsnd");
        return 0;
    }

}

1.1.5消息的接收:

#include <sys/ipc.h>

 #include <sys/msg.h>

 int msgrcv(int msgid, void *msgp, size_t size, long msgtype, int msgflg);

成功时返回收到的消息长度,失败时返回-1

msgid   消息队列id

msgp   消息缓冲区地址

size   指定接收的消息长度

msgtype   指定接收的消息类型   

msgflg   标志位  

msgtype:

msgtype=0:收到的第一条消息,任意类型。

msgtype>0:收到的第一条 msg_type类型的消息。

msgtype<0:接收类型等于或者小于msgtype绝对值的第一个消息。

例子:如果msgtype=-4,只接受类型是1、2、3、4的消息

msgflg:

0:阻塞式接收消息

IPC_NOWAIT:如果没有返回条件的消息调用立即返回,此时错误码为ENOMSG

MSG_EXCEPT:与msgtype配合使用返回队列中第一个类型不为msgtype的消息

#include <stdio.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>

typedef struct{
    long msg_type;
    char buf[128];
}msgT;    

#define MSGLEN  (sizeof(msgT)-sizeof(long))
int main(){
    
    int msgid;
    key_t key;
    msgT msg;
    int ret;
    key = ftok(".",100);
    if(key<0){
        perror("ftok");
        return 0;
    }    
    msgid = msgget(key,IPC_CREAT|0666);
    if(msgid<0){
        perror("msgget");
        return 0;
    }
    int count=0;
    while(1){
        ret = msgrcv(msgid,&msg,MSGLEN,0,0);
        if(ret<0){
            perror("msgrcv");
            return 0;
        } 
        count++;
        if(count>3){
            break;
        }
        printf("receiv msg type=%d,buf=%s\n",(int)msg.msg_type,msg.buf);
    }

    ret = msgctl(msgid,IPC_RMID,NULL);
    if(ret<0){
        perror("msgctl");
        return 0;
    }    
}

1.1.6消息队列的控制

#include <sys/ipc.h>

 #include <sys/msg.h>

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

  成功时返回0,失败时返回-1

  msgid    消息队列id

  cmd    要执行的操作  IPC_STAT / IPC_SET / IPC_RMID(删除)

  buf   存放消息队列属性的地址

2.信号灯/信号量(semaphore)

概念:是不同进程间或一个给定进程内部不同线程间同步的机制。类似我们的PV操作

2.1PV操作概念:

p操作v操作(申请资源,释放资源)

生产者和消费者场景

P(S) 含义如下:

     if  (信号量的值大于0) {  

           申请资源的任务继续运行;

           信号量的值减一;

} else {   

         申请资源的任务阻塞;

}

V(S) 含义如下:

     信号量的值加一;

     if (有任务在等待资源) {   

        唤醒等待的任务,让其继续运行

}

2.2 三种信号灯:

Posix 有名信号灯

Posix 无名信号灯 (linux只支持线程同步)

System V 信号灯

Posix 有名信号灯和无名信号灯使用:

2.2.1有名信号灯打开:

sem_t *sem_open(const char *name, int oflag);

sem_t *sem_open(const char *name, int oflag,mode_t mode, unsigned int value);

参数:

name:name是给信号灯起的名字

oflag:打开方式,常用O_CREAT

mode:文件权限。常用0666

value:信号量值。二元信号灯值为1,普通表示资源数目

信号灯文件位置:/dev/shm

有名信号灯关闭

int sem_close(sem_t *sem);

有名信号灯的删除

int sem_unlink(const char* name);

2.2.2无名信号灯初始化

int sem_init(sem_t *sem, int shared, unsigned int value);

参数:

sem:需要初始化的信号灯变量

shared: shared指定为0,表示信号量只能由初始化这个信号量的进程使用,不能在进程间使用,linux 不支持进程间同步。

Value:信号量的值

无名信号灯销毁

int sem_destroy(sem_t* sem);

2.2.3信号灯P操作

int sem_wait(sem_t *sem);

获取资源,如果信号量为0,表示这时没有相应资源空闲,那么调用线程就将挂起,直到有空闲资源可以获取

2.2.4信号灯V操作

int sem_post(sem_t *sem);

释放资源,如果没有线程阻塞在该sem上,表示没有线程等待该资源,这时该函数就对信号量的值进行增1操作,表示同类资源多增加了一个。如果至少有一个线程阻塞在该sem上,表示有线程等待资源,信号量为0,这时该函数保持信号量为0不变,并使某个阻塞在该sem上的线程从sem_wait函数中返回

注意:编译posix信号灯需要加pthread动态库。

1.有名信号灯代码案例

//********************************写**********************************//
#include <fcntl.h>           /* For O_* constants */
#include <sys/stat.h>        /* For mode constants */
#include <semaphore.h>

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

void handler(){
sem_unlink("mysem_w");
exit(0);
}

int main(int argc, const char *argv[])
{
	sem_t *sem_w,*sem_r;
	key_t key;
	int shmid;
	char*shmaddr;

	struct sigaction act;
	act.sa_handler = handler;
	act.sa_flags = 0;
	sigemptyset(&act.sa_mask);
	sigaction(SIGINT,&act,NULL);


	key = ftok(".",100);
	if(key < 0){
		perror("ftok: ");
		return 0;
	}
	//申请共享内存
	shmid = shmget(key,500,0666|IPC_CREAT);
	if(shmid < 0){
		perror("shmid: ");
		return 0;
	}
	//映射共享内存
	shmaddr = shmat(shmid,NULL,0);
	sem_r = sem_open("mysem_r",O_CREAT|O_RDWR,0666,0);
	sem_w = sem_open("mysem_w",O_CREAT|O_RDWR,0666,1);
	
	while(1){
	sem_wait(sem_w);
	printf(">");
	fgets(shmaddr,500,stdin);
	sem_post(sem_r);
	}

	return 0;
}
//********************************读**********************************//

#include <fcntl.h>           /* For O_* constants */
#include <sys/stat.h>        /* For mode constants */
#include <semaphore.h>

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

void handler(){
sem_unlink("mysem_r");
exit(0);
}

int main(int argc, const char *argv[])
{
	sem_t *sem_w,*sem_r;
	key_t key;
	int shmid;
	char*shmaddr;

	struct sigaction act;
	act.sa_handler = handler;
	act.sa_flags = 0;
	sigemptyset(&act.sa_mask);
	sigaction(SIGINT,&act,NULL);


	key = ftok(".",100);
	if(key < 0){
		perror("ftok: ");
		return 0;
	}
	//申请共享内存
	shmid = shmget(key,500,0666|IPC_CREAT);
	if(shmid < 0){
		perror("shmid: ");
		return 0;
	}
	//映射共享内存
	shmaddr = shmat(shmid,NULL,0);
	sem_r = sem_open("mysem_r",O_CREAT|O_RDWR,0666,0);
	sem_w = sem_open("mysem_w",O_CREAT|O_RDWR,0666,1);
	
	while(1){
    //申请读资源
	sem_wait(sem_r);
	printf("%s\n",shmaddr);
     //释放写资源
	sem_post(sem_w);
	}

	return 0;
}

2.无名信号灯代码案例(线程同步)

#include <fcntl.h>           /* For O_* constants */
#include <sys/stat.h>        /* For mode constants */
#include <semaphore.h>

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

sem_t sem_w,sem_r;

char*shmaddr;

void handler(){
	sem_unlink("mysem_w");
	exit(0);
}

void* readmem(void* arg){

	while(1){
		sem_wait(&sem_r);
		printf("%s\n",shmaddr);
		sem_post(&sem_w);
	}

}



int main(int argc, const char *argv[])
{
	key_t key;
	int shmid;

	struct sigaction act;
	act.sa_handler = handler;
	act.sa_flags = 0;
	sigemptyset(&act.sa_mask);
	sigaction(SIGINT,&act,NULL);


	key = ftok(".",100);
	if(key < 0){
		perror("ftok: ");
		return 0;
	}
	//申请共享内存
	shmid = shmget(key,500,0666|IPC_CREAT);
	if(shmid < 0){
		perror("shmid: ");
		return 0;
	}
	//映射共享内存
	shmaddr = shmat(shmid,NULL,0);
	//无名信号量初始化
	sem_init(&sem_r,0,0);	
	sem_init(&sem_w,0,1);	
	//创建线程
	pthread_t pid;
	pthread_create(&pid,NULL,readmem,NULL);


	while(1){
		sem_wait(&sem_w);
		printf(">");
		fgets(shmaddr,500,stdin);
		sem_post(&sem_r);
	}

	return 0;
}

2.3 System V 信号灯使用:

int semget(key_t key, int nsems, int semflg);

功能:创建/打开信号灯

参数:key:ftok产生的key值(和信号灯关联的key值)

  nsems:信号灯集中包含的信号灯数目

  semflg:信号灯集的访问权限,通常为IPC_CREAT |0666

返回值:成功:信号灯集ID ; 失败:-1

int semop ( int semid, struct sembuf *opsptr, size_t nops);

功能:对信号灯集合中的信号量进行P - V操作

参数:semid:信号灯集ID

struct sembuf {

short sem_num; // 要操作的信号灯的编号

short sem_op;  // 1 : 释放资源,V操作

 // -1 : 分配资源,P操作  

short sem_flg;  // 0(阻塞),IPC_NOWAIT, SEM_UNDO

};//对某一个信号灯的操作,如果同时对多个操作,则需要定义这种结构体数组

nops: 要操作的信号灯的个数 ,1个

返回值:成功 :0 ; 失败:-1

int semctl ( int semid, int semnum, int cmd…/*union semun arg*/);

功能:信号灯集合的控制(初始化/删除)

参数:semid:信号灯集ID

semnum: 要操作的集合中的信号灯编号

cmd:

GETVAL:获取信号灯的值,返回值是获得值

SETVAL:设置信号灯的值,需要用到第四个参数:共用体

IPC_RMID:从系统中删除信号灯集合

返回值:成功 0 ; 失败 -1

 功能: 主进程键盘输入,子进程打印

#include <semaphore.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

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



#define SEM_READ   0
#define SEM_WRITE  1

union semun {
   int val;
};               
//P操作
void Poperation(int semid,int semindex){
   struct sembuf sbuf;
   sbuf.sem_num =  semindex;
   sbuf.sem_op = -1;
   sbuf.sem_flg = 0;

   semop(semid,&sbuf,1);


}
//V操作
void Voperation(int semid,int semindex){
   struct sembuf sbuf;
   sbuf.sem_num =  semindex;
   sbuf.sem_op = 1;
   sbuf.sem_flg = 0;
              
   semop(semid,&sbuf,1);

}

int main(){

    key_t key;
    char *shmaddr;
    int semid,shmid;
    key = ftok(".",100);
    if(key<0){
        perror("ftok");
        return 0;
    }
    //信号灯创建
    semid = semget(key,2,IPC_CREAT |0666);
    if(semid<0){
        perror("semget");
        return 0;
    }
    //创建共享内存,返回其id
    shmid = shmget(key,500,IPC_CREAT |0666);
    //映射共享内存,返回其地址
    shmaddr = shmat(shmid,NULL,0);
    //信号灯初始化
    union semun mysem;
    mysem.val = 0;
    semctl(semid,SEM_READ,SETVAL,mysem);
    mysem.val = 1;
    semctl(semid,SEM_WRITE,SETVAL,mysem);
    //创建子进程
    pid_t pid;
    pid = fork();
    if(pid<0){
        perror("fork");
        shmctl(shmid,IPC_RMID,NULL);//共享内存删掉
        semctl(semid,0,IPC_RMID);//信号灯删掉
        exit(-1);
    }else if(pid == 0){
        while(1){
            Poperation(semid,SEM_READ);
            printf("%s\n",shmaddr);
            Voperation(semid,SEM_WRITE);
        }
    }else{
        while(1){
            Poperation(semid,SEM_WRITE);
            printf(">");
            fgets(shmaddr,32,stdin);
            Voperation(semid,SEM_READ);

        }

    }

}


总结

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值