进程间通讯-消息队列和信号灯

消息队列

  • 消息队列是System V IPC对象的一种
  • 消息队列由消息队列ID来唯一标识
  • 消息队列就是一个消息的列表。用户可以在消息队列中添加消息、读取消息等
  • 消息队列可以按照类型来发送/接收消息

消息队列结构

消息队列使用步骤

发送端:

        1 申请Key

        2打开/创建消息队列   msgget

        3向消息队列发送消息   msgsnd

接收端:

        1打开/创建消息队列   msgget

        2从消息队列接收消息   msgrcv

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

消息队列创建/打开 – msgget

 #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

消息发送 – msgsnd

 #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

消息格式

  • 通信双方首先定义好统一的消息格式
  • 用户根据应用需求定义结构体类型
  • 首成员类型必须为long,代表消息类型(正整数)
  • 其他成员都属于消息正文
  • 消息长度不包括首类型 long

typedef struct{

                long msg_type;

                char buf[128];

}msgT;

消息接收 – msgrcv

 #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   指定接收的消息类型  

  •         msgtype=0:收到的第一条消息,任意类型。
  •         msgtype>0:收到的第一条 msg_type类型的消息。
  •         msgtype<0:接收类型等于或者小于msgtype绝对值的第一个消息。

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

 

msgflg   标志位   0 或 IPC_NOWAIT

  •         0:阻塞式接收消息
  •         IPC_NOWAIT:如果没有返回条件的消息调用立即返回,此时错误码为ENOMSG
  •         MSG_EXCEPT:与msgtype配合使用返回队列中第一个类型不为msgtype的消息

控制消息队列 – msgctl

 #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   存放消息队列属性的地址

消息队列收发数据代码实例

发送数据-msgsnd.c

#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;
    }
}

接收数据-msgrcv.c

#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;
    }    

}

信号灯/信号量(semaphore)

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

PV操作概念:生产者和消费者场景

P(S) 含义如下:

 if  (信号量的值大于0)

{  

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

        信号量的值减一;

}

else

{   

        申请资源的任务阻塞;

}

V(S) 含义如下:

信号量的值加一;

if (有任务在等待资源)

{   

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

}

Posix  信号量

posix中定义了两类信号量:

  • 无名信号量(基于内存的信号量,linux仅支持线程同步)
  • 有名信号量

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

有名信号灯的使用

有名信号灯打开:

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,普通表示资源数目

信号量 – P / V 操作

 #include  <semaphore.h>  

int  sem_wait(sem_t  *sem);       P操作  

int  sem_post(sem_t  *sem);       V操作  

成功时返回0,失败时返回EOF  

sem  指向要操作的信号量对象

信号灯文件位置:

/dev/shm

有名信号灯关闭

int sem_close(sem_t *sem);

有名信号灯的删除

int sem_unlink(const char* name);

有名信号量实现两个进程间对同一个共享内存的读写同步的代码实例

sem_w.c

#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 delsemfile(int sig){
    sem_unlink("mysem_w");
    exit(0);

}

int main(){


   sem_t *sem_r,*sem_w;
   key_t key;
   int shmid;
   char *shmaddr;

   struct sigaction act;
   act.sa_handler = delsemfile;
   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("shmget");
       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);
   }

}

sem_r.cr

#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 delsemfile(int sig){
    sem_unlink("mysem_r");
    exit(0);

}


int main(){

   sem_t *sem_r,*sem_w;
   key_t key;
   int shmid;
   char *shmaddr;
   struct sigaction act;
   act.sa_handler = delsemfile;
   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("shmget");
       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);
   }

}

无名信号灯的使用(只能线程间使用)

pthread库常用的信号量操作函数如下:

  • int sem_init(sem_t *sem,  int pshared,  unsigned int value);
  • int sem_wait(sem_t *sem);   //  P操作
  • int sem_post(sem_t *sem);  // V操作

信号量初始化 – sem_init

 #include  <semaphore.h>  

int  sem_init(sem_t *sem, int pshared, unsigned int val);  

成功时返回0,失败时EOF  

sem  指向要初始化的信号量对象  

pshared   0 – 线程间   1 – 进程间  

val  信号量初值


无名信号灯销毁

int sem_destroy(sem_t* sem);

无名信号量实现两个线程间对同一个共享内存的读写同步的代码实例

#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>
#include <pthread.h>


sem_t sem_r,sem_w;
char *shmaddr;
void destroysem(int sig){
//    sem_unlink("mysem_w");
   sem_destroy(&sem_r);
   sem_destroy(&sem_w);
   exit(0);

}

void *readmem(void *arg){
    while(1){
        sem_wait(&sem_r);
        printf("%s\n",shmaddr);
        sem_post(&sem_w);

    }

}


int main(){


   key_t key;
   int shmid;

   struct sigaction act;
   act.sa_handler = destroysem;
   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("shmget");
       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);
   sem_init(&sem_r,0,0);
   sem_init(&sem_w,0,1);

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

}

System V IPC - 信号灯

        信号灯也叫信号量,用于进程/线程同步或互斥的机制

信号灯的类型  

  • Posix 无名信号灯  
  • Posix有名信号灯  
  • System V  信号灯

信号灯的含义  

  • 计数信号灯

System V IPC - 信号灯特点

  • System V 信号灯是一个或多个计数信号灯的集合
  • 可同时操作集合中的多个信号灯
  • 申请多个资源时避免死锁

System V信号灯使用步骤

  1. 打开/创建信号灯  
  2. semget 信号灯初始化  
  3. semctl P/V操作   semop
  4. 删除信号灯  semctl

信号灯创建/打开 – semget

 #include <sys/ipc.h>  

#include <sys/sem.h>  

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

成功时返回信号灯的id,失败时返回-1  

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

nsems   集合中包含的计数信号灯个数  

semflg   标志位  IPC_CREAT|0666   IPC_EXCL

信号灯初始化 – semctl

#include <sys/ipc.h>

#include <sys/sem.h>

int semctl(int semid, int semnum, int cmd, …);  

成功时返回0,失败时返回EOF  

semid    要操作的信号灯集id  

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

cmd   执行的操作  SETVAL  IPC_RMID  

union  semun      取决于cmd

信号灯P/V操作 – semop

#include <sys/ipc.h>

#include <sys/sem.h>

int semop(int semid, struct sembuf *sops, unsigned nsops);  

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

semid    要操作的信号灯集id  

sops    描述对信号灯操作的结构体(数组)  

nsops   要操作的信号灯的个数

信号灯操作 – sembuf

 struct  sembuf  

{      

        short  sem_num;      

        short  sem_op;      

        short  sem_flg;

 };  

semnum         信号灯编号  

sem_op           -1:P操作   1:V操作

sem_flg           0 / IPC_NOWAIT

#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;
};               

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);


}
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;
    }
    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);

        }

    }

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值