消息队列和信号灯

消息队列

概念:
消息队列是System V IPC对象的一种
消息队列和信号灯_第1张图片

消息队列的使用:

发送端:

1 申请Key
2打开/创建消息队列   msgget
3向消息队列发送消息   msgsnd

接收端:

1打开/创建消息队列   msgget
2从消息队列接收消息   msgrcv
3 控制(删除)消息队列   msgctl

打开/创建消息队列

 #include 
 #include 
 int msgget(key_t key, int msgflg);
  成功时返回消息队列的id,失败时返回EOF
  key 和消息队列关联的key  IPC_PRIVATE 或 ftok
  msgflg  标志位  IPC_CREAT|0666  IPC_CREAT:没有创建,有则打开。

发送消息

#include 
 #include 
 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 
 #include 
 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 
 #include 
 int msgctl(int msgid, int cmd, struct msqid_ds *buf);

  成功时返回0,失败时返回-1
  msgid    消息队列id
  cmd    要执行的操作  IPC_STAT / IPC_SET / IPC_RMID(删除)
  buf   存放消息队列属性的地址

信号灯/信号量(semaphore)

概念:是不同进程间或一个给定进程内部不同线程间同步的机制。类似我们的
PV操作概念:
生产者和消费者场景

() 含义如下:
     if  (信号量的值大于0) {  
 申请资源的任务继续运行;
           信号量的值减一;
} else {   
申请资源的任务阻塞;
} 
V() 含义如下:
     信号量的值加一;
     if (有任务在等待资源) {   
唤醒等待的任务,让其继续运行 
}

三种信号灯:

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

Posix 有名信号灯和无名信号灯使用:
消息队列和信号灯_第2张图片

有名信号灯打开:

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

有名信号灯通讯示例 - (发送一个,接收一个)

//sem_w.c
#include           
#include        
#include 

#include 
#include 
#include 
#include 
#include 
#include 
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.c
#include            
#include         
#include 
#include 
#include 
#include 
#include 
#include 

#include 


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

}

无名信号灯初始化

int sem_init(sem_t *sem, int shared, unsigned int value);
参数:
sem:需要初始化的信号灯变量
shared: shared指定为0,表示信号量只能由初始化这个信号量的进程使用,不能在进程间使用,linux 不支持进程间同步。
Value:信号量的值

无名信号灯销毁

int sem_destroy(sem_t* sem);

信号灯P操作

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

信号灯V操作

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

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

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

你可能感兴趣的:(应用层,c语言)