【问题标题】:linux ipc. Why is msgrcv always blocking?linux ipc。为什么 msgrcv 总是阻塞?
【发布时间】:2021-02-02 21:05:21
【问题描述】:

客户端读取线写入共享内存。并向服务器发送消息。 服务器获取消息并从共享内存中读取。

但是服务器无法正确输出, 服务器没有输出任何东西,我不知道为什么。

手册页说: 如果没有请求类型的消息可用且 msgflg 中未指定 IPC_NOWAIT,则调用进程将被阻塞,直到出现以下情况之一

但服务器总是被阻塞。

我用gdb调试,发现std::cout不行

调试上下文

Breakpoint 1, main () at shared_mem_server.cpp:35
35      sem_init(reinterpret_cast<sem_t*>(shm),0,1);
(gdb) p shm
$1 = 0x7ffff7ff6000 ""
(gdb) x/10w 0x7ffff7ff6000
0x7ffff7ff6000: 0   0   0   0
0x7ffff7ff6010: 0   0   0   0
0x7ffff7ff6020: 0   0
(gdb) s
__new_sem_init (sem=0x7ffff7ff6000, pshared=0, value=1) at sem_init.c:31
31  sem_init.c: No such file or directory.
(gdb) return
Make __new_sem_init return now? (y or n) n
Not confirmed
(gdb) finish
Run till exit from #0  __new_sem_init (sem=0x7ffff7ff6000, pshared=0, value=1)
    at sem_init.c:31
main () at shared_mem_server.cpp:37
37          msgrcv(msgid,&msg,256,ret_type,0);
Value returned is $2 = 0
(gdb) x/10w 0x7ffff7ff6000
0x7ffff7ff6000: 1   0   0   0
0x7ffff7ff6010: 0   0   0   0
0x7ffff7ff6020: 0   0
(gdb) p sem_sz
$3 = 32
(gdb) n
38          sem_p(reinterpret_cast<sem_t*>(shm));
(gdb) n
39          if(shm + sem_sz == "q")
(gdb) x/10w 0x7ffff7ff6000
0x7ffff7ff6000: 0   0   0   0
0x7ffff7ff6010: 0   0   0   0
0x7ffff7ff6020: 3355185 0
(gdb) x/12w 0x7ffff7ff6000
0x7ffff7ff6000: 0   0   0   0
0x7ffff7ff6010: 0   0   0   0
0x7ffff7ff6020: 3355185 0   0   0
(gdb) n
41          std::cout << "shared memory " << shm + sem_sz;
(gdb) n
42          sem_v(reinterpret_cast<sem_t*>(shm));
(gdb) q

下面是代码

服务器代码:

#include <iostream>
#include <sys/shm.h>
#include <sys/msg.h>

#include "error.h"
#include "sempv.h"

const int SHM_SIZE=1024;

struct msg_form{
    long  msg_type;
    char msg_text[256];
};

int main(){
    key_t key;
    int shmid,msgid,ret_type = 888,sem_sz = sizeof(sem_t);
    char *shm;
    

    msg_form msg;

    if((key = ftok(".",'v')) < 0)
        unix_error("ftok error");
    
    if((shmid = shmget(key,SHM_SIZE,IPC_CREAT|0666)) == -1)
        unix_error("create shared memory error");

    if((shm = (char*)shmat(shmid,0,0)) == (void*)-1){
        unix_error("attach shared memeory error");
    }
    if((msgid = msgget(key,IPC_CREAT|07777)) == -1)
        unix_error("msgget error");
    sem_init(reinterpret_cast<sem_t*>(shm),0,1);
    while(true){
        msgrcv(msgid,&msg,256,ret_type,0);
        sem_p(reinterpret_cast<sem_t*>(shm));
        if(shm + sem_sz == "q")
            break;
        std::cout << "shared memory " << shm + sem_sz;
        sem_v(reinterpret_cast<sem_t*>(shm));
    }
    shmdt(shm);
   
    shmctl(shmid,IPC_RMID,0);
    shmctl(msgid,IPC_RMID,0);
    return 0;
}


客户端代码

#include <iostream>
#include <sys/shm.h>
#include <sys/msg.h>

#include "error.h"
#include "sempv.h"
#include <string>

using std::string;

const int SHM_SIZE=1024;

struct msg_form{
    long  msg_type;
    char msg_text[256];
};

int main(){
    key_t key;
    int shmid,msgid,sem_sz = sizeof(sem_t);
    char *shm;
    
    int err;

    msg_form msg;
    string s;

    if((key = ftok(".",'v')) < 0)
        unix_error("ftok error");
    
    if((shmid = shmget(key,SHM_SIZE,0)) == -1)
        unix_error("shmget error");
    if((shm = (char*)shmat(shmid,0,0)) == (void*)-1){
        unix_error("attach shared memeory error");
    }
    if((msgid = msgget(key,0777)) == -1)
        unix_error("msgget error");
    std::cout << "key is " << key << std::endl;
    
    while(getline(std::cin,s)){
        sem_p(reinterpret_cast<sem_t*>(shm));
        memset(shm+sem_sz,0,SHM_SIZE-sem_sz);
        memcpy(shm+sem_sz,s.c_str(),s.size());
       
        msg.msg_type = 888;
        sprintf(msg.msg_text,"shared memory write signal");

        if((err =  msgsnd(msgid,&msg,sizeof(msg.msg_text),0)) == -1)
            unix_error("msgsnd error");
         sem_v(reinterpret_cast<sem_t*>(shm));
        //std::cout << "message send\n";
    }
    return 0;
}

【问题讨论】:

  • 同步进程时 sem_init() 的第二个参数应为 1
  • if(shm + sem_sz == "q") 不正确。把: if (*(shm + sem_sz) == 'q' && *(shm + sem_sz + 1) == '\n')
  • 你需要在 cout 的末尾添加 endl 否则字符串被缓冲。

标签: c++ linux ipc sysv sysv-ipc


【解决方案1】:

一般说明:

  • System V IPC 已弃用,对于任何新项目,建议使用 POSIX 对应项(man 7 shm_overviewman 7 mq_overview

  • 客户端/服务器同步较弱:客户端可能会在服务器读取共享内存段时覆盖它。您应该使用互斥锁来读/写共享内存段(当一个正在读/写另一个被阻塞时):cf。 man 7 sem_overview

  • 由于您没有在服务器中进行任何清理,请确保在每次尝试应用程序之间使用 shell 下的 ipcs/ipcrm 删除队列和共享内存标识符

在服务器中:

  • 错误检查错误:如果 shmget()/msgget 返回 -1 并且 errno 等于 EEXIST,则继续,但没有得到任何 shm/msg 标识符,因为它是 -1!

在客户端:

  • 为了健壮性,最好使用 snprintf() 而不是 sprintf() 来强制检查缓冲区的边界

  • 客户端不应将 IPC_CREAT 用于 msgget()。通常,在服务器/客户端应用程序中创建资源是服务器的角色

这是您修改后代码的 C 版本:

服务器:

#include <stdio.h>
#include <sys/shm.h>
#include <sys/msg.h>
#include <stdlib.h>
#include <semaphore.h>


const int SHM_SIZE=1024;

struct msg_form{
    long  msg_type;
    char msg_text[256];
};


static void unix_error(const char *str)
{

  fprintf(stderr, "%s\n", str);

  exit(1);
}


int main(){
    key_t key;
    int shmid,msgid,ret_type = 888,sem_sz = sizeof(sem_t);
    char *shm;
    

    struct msg_form msg;

    if((key = ftok(".",'v')) < 0)
        unix_error("ftok error");
    
    if((shmid = shmget(key,SHM_SIZE,IPC_CREAT|0666)) == -1)
        unix_error("create shared memory error");

    if((shm = (char *)shmat(shmid,0,0)) == (void*)-1){
        unix_error("attach shared memory error");
    }
    if((msgid = msgget(key,IPC_CREAT|07777)) == -1)
        unix_error("msgget error");
    sem_init((sem_t *)(shm),1,1);
    while(1){
        msgrcv(msgid,&msg,256,ret_type,0);
        sem_wait((sem_t *)(shm));
        if (*(shm + sem_sz) == 'q' && *(shm + sem_sz + 1) == '\n') {
            sem_post((sem_t *)(shm));
            break;
        }
        printf("shared memory: %s", shm + sem_sz);
        sem_post((sem_t *)(shm));
    }
    shmdt(shm);
   
    shmctl(shmid,IPC_RMID,0);
    msgctl(msgid,IPC_RMID,0);
    return 0;
}

客户:

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


const int SHM_SIZE=1024;

struct msg_form{
    long  msg_type;
    char msg_text[256];
};


static void unix_error(const char *str)
{

  fprintf(stderr, "%s\n", str);

  exit(1);
}


int main(){
    key_t key;
    int shmid,msgid,sem_sz = sizeof(sem_t);
    char *shm;
    
    int err;

    struct msg_form msg;
    char s[256];

    if((key = ftok(".",'v')) < 0)
        unix_error("ftok error");
    
    if((shmid = shmget(key,SHM_SIZE,0)) == -1)
        unix_error("shmget error");
    if((shm = (char*)shmat(shmid,0,0)) == (void*)-1){
        unix_error("attach shared memeory error");
    }
    if((msgid = msgget(key,0777)) == -1)
        unix_error("msgget error");
    printf("key is 0x%x\n", (int)key);
    
    while(fgets(s, sizeof(s) - 1, stdin)) {
        sem_wait((sem_t *)(shm));
        memset(shm+sem_sz,0,SHM_SIZE-sem_sz);
        memcpy(shm+sem_sz,s, strlen(s));
       
        msg.msg_type = 888;
        snprintf(msg.msg_text, 256, "shared memory write signal");

        if((err =  msgsnd(msgid,&msg,sizeof(msg.msg_text),0)) == -1) {
            sem_post((sem_t *)(shm));
            unix_error("msgsnd error");
        }
        sem_post((sem_t *)(shm));
    }
    return 0;
}

【讨论】:

  • 你好,我改变了我的代码。但是服务器仍然没有输出
  • 我在答案中添加了您的代码的 C 版本。我试过了,效果很好。
  • 是的,我调试发现std::cout没有输出,是cout的原因吗?
  • 当服务器在“q”时退出时添加一个 sem_post() 以释放信号量。
  • 服务器清理出错:调用msgctl()而不是shmctl()
猜你喜欢
  • 2019-12-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-07-23
  • 2011-04-24
  • 2012-10-31
  • 2011-11-16
相关资源
最近更新 更多