分享

关于posix系统IPC

 幸福的乐土 2012-08-22
beijibing@bjb-desktop:~$ cat /mnt/mymq
QSIZE:1          NOTIFY:2     SIGNO:0     NOTIFY_PID:2719  
beijibing@bjb-desktop:~$ ls -al /mnt
总用量 4
drwxrwxrwt  2 root      root        60 2012-08-22 21:18 .
drwxr-xr-x 22 root      root      4096 2012-05-08 00:16 ..
-rw-r--r--  1 beijibing beijibing   80 2012-08-22 21:20 mymq

咱们的UC课程中讲的一般是System V的IPC,总是有同学关心POSIX的IPC,今天不经意看到一篇文章我觉得还不错,转过来跟大家分享吧。

原文链接:http://home./home-space-uid-56821-do-blog-id-233077.html

POSIX IPC为每个对象都使用文件描述符,这与System V的IPC不同,System V的每个对象都使用键值(keys).由于每个IPC对象可以写到一个纯文本文件,因此,在纯文本文件上使用的工具对于操作POSIX IPC对象来说通常已经足够了.


1)POSIX共享内存
在Linux中,POSIX共享内存对象驻留在tmpfs伪文件系统中.系统默认挂载在/dev/shm目录下.
当调用shm_open函数创建或打开POSIX共享内存对象时,系统会将创建/打开的共享内存文件放到/dev/shm目录下.

同样也可以手工在/dev/shm目录下创建文件,以供POSIX共享内存程序连接使用.

我们用下面的源程序对POSIX共享内存进行测试,如下:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/file.h>
#include <sys/mman.h>
#include <sys/wait.h>
void error_out(const char *msg)
{
        perror(msg);
        exit(EXIT_FAILURE);
}

int main (int argc, char *argv[])
{
        int r;
        const char *memname = "/mymem";
        const size_t region_size = sysconf(_SC_PAGE_SIZE);
        int fd = shm_open(memname, O_CREAT|O_TRUNC|O_RDWR, 0666);
        if (fd == -1)
                error_out("shm_open");
        r = ftruncate(fd, region_size);
        if (r != 0)
                error_out("ftruncate");
        void *ptr = mmap(0, region_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
        if (ptr == MAP_FAILED)
                error_out("MMAP");
        close(fd);
        pid_t pid = fork();
        if (pid == 0){
                u_long *d = (u_long *)ptr;
                *d = 0xdeadbeef;
                exit(0);
        }
        else{
                int status;
                waitpid(pid, &status, 0);
                printf("child wrote %#lx\n", *(u_long *)ptr);
        }
        sleep(50);
        r = munmap(ptr, region_size);
        if (r != 0)
                error_out("munmap");
        r = shm_unlink(memname);
        if (r != 0)
                error_out("shm_unlink");
        return 0;
}

编译程序pmem.c
gcc pmem.c -o pmem -lrt


执行程序pmem
./pmem
child wrote 0xdeadbeef

注:程序会通过shm_open函数创建一个共享内存对象,并通过mmap函数将文件映射到内存,此时修改内存,即是修改文件.
子进程向共享内存写数据,父进程将共享内存数据打印输出,在输出完成后,会等待50秒钟.我们可以在另一个终端看到/dev/shm/mymem文件,如下:

ls -l /dev/shm/mymem
-rw-r--r-- 1 root root 4096 2011-03-16 15:22 /dev/shm/mymem



2)POSIX消息队列

在Linux中通过mqueue伪文件系统来显示POSIX消息队列,与POSIX共享内存不同的是,它没有一个标准的挂载点来为这个文件系统服务.
如果需要调试来自shell的POSIX消息队列,则需要手动挂载文件系统,例如,为了将其挂载在命名为/mnt/mqs的目录下,可以使用以下命令:

mkdir /mnt/mqs
mount -t mqueue none /mnt/mqs


我们用下面的源程序对POSIX消息队列进行测试,如下:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <mqueue.h>
#include <sys/stat.h>
#include <sys/wait.h>

struct message{
char mtext[128];
};

int send_msg(int qid, int pri, const char text[])
{
int r = mq_send(qid, text, strlen(text) + 1,pri);
if (r == -1){
  perror("mq_send");
}
return r;
}

void producer(mqd_t qid)
{
send_msg(qid, 1, "This is my first message.");
send_msg(qid, 1, "This is my second message.");

send_msg(qid, 3, "No more messages.");
}

void consumer(mqd_t qid)
{
struct mq_attr mattr;
do{
  u_int pri;
  struct message msg;
  ssize_t len;

  len = mq_receive(qid, (char *)&msg, sizeof(msg), &pri);
  if (len == -1){
   perror("mq_receive");
   break;
  }
  printf("got pri %d '%s' len=%d\n", pri, msg.mtext, len);

  int r = mq_getattr(qid, &mattr);
  if (r == -1){
   perror("mq_getattr");
   break;
  }
}while(mattr.mq_curmsgs);
}

int
main (int argc, char *argv[])
{
struct mq_attr mattr = {
  .mq_maxmsg = 10,
  .mq_msgsize = sizeof(struct message)
};

mqd_t mqid = mq_open("/myq",
    O_CREAT|O_RDWR,
    S_IREAD|S_IWRITE,
    &mattr);
if (mqid == (mqd_t) -1){
  perror("mq_open");
  exit (1);
}

pid_t pid = fork();
if (pid == 0){
  producer(mqid);
  mq_close(mqid);
  exit(0);
}
else
{
  int status;
  wait(&status);
  sleep(50);
  consumer(mqid);
  mq_close(mqid);
}
mq_unlink("/myq");
return 0;
}
编译:
gcc posix_mqs.c -o posix_mqs -lrt

运行程序:
./posix_mqs
got pri 3 'No more messages.' len=18
got pri 1 'This is my first message.' len=26
got pri 1 'This is my second message.' len=27


注:程序通过fork使子进程发送三条消息到消息队列,父进程从消息队列中接收这三条消息.
/mnt/mqs/myq文件是程序创建的消息队列文件,程序会自动在伪文件系统mqueue中创建消息队列文件.

如果我们挂载伪文件系统到多个目录,而在每个挂载目录下都会生成消息队列文件.
例如:
mkdir /dev/mqs/
mount -t mqueue none /dev/mqs

df -aTh
Filesystem    Type    Size  Used Avail Use% Mounted on
/dev/sda1     ext3     19G  3.3G   15G  19% /
proc          proc       0     0     0   -  /proc
sysfs        sysfs       0     0     0   -  /sys
devpts      devpts       0     0     0   -  /dev/pts
tmpfs        tmpfs    512M     0  512M   0% /dev/shm
none   binfmt_misc       0     0     0   -  /proc/sys/fs/binfmt_misc
sunrpc  rpc_pipefs       0     0     0   -  /var/lib/nfs/rpc_pipefs
none        mqueue       0     0     0   -  /mnt/mqs
none        mqueue       0     0     0   -  /dev/mqs

再次运行程序
./posix_mqs

我们看到在两个mqueue伪文件系统挂载的目录下都生成了myq文件
cat /mnt/mqs/myq /dev/mqs/myq
QSIZE:71         NOTIFY:0     SIGNO:0     NOTIFY_PID:0     
QSIZE:71         NOTIFY:0     SIGNO:0     NOTIFY_PID:0  

QSIZE:71表示/mnt/mqs/myq消息队列中一共有71个字节.
NOTIFY,SIGNO,NOTIFY_PID都和mq_notify函数有关.


3)POSIX信号量

在Linux中,POSIX信号量对象驻留在tmpfs伪文件系统中,同POSIX共享内存一样,有名字的信号量作为文件被创建在/dev/shm里.

我们用下面的源程序来测试POSIX信号量,如下:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/file.h>
#include <sys/times.h>
#include <sys/stat.h>
#include <semaphore.h>
#include <assert.h>
void busywait(void)
{
clock_t t1 = times(NULL);
while(times(NULL) - t1 < 2);
}
int main (int argc, char *argv[])
{
const char *message = "Hello World\n";
int n = strlen(message)/2;
sem_t *sem = sem_open("/thesem", O_CREAT, S_IRUSR|S_IWUSR);
sleep(30);
assert(sem != NULL);
int r = sem_init(sem, 1, 0);
assert(r == 0);
pid_t pid = fork();
int i0 = (pid == 0) ? 0 :n ;
int i;

if (pid)
  sem_wait(sem);
for (i = 0;i < n;i++){
  write (1,message+i0+i,1);
  busywait();
}
if (pid == 0)
  sem_post(sem);
}

编译:
gcc posix-sem.c -o posix-sem -lrt

执行posix-sem程序:
./posix-sem

在另一个终端查看信号量文件,如下:
ls -l /dev/shm/
total 4
-rw------- 1 root root 16 Mar 18 10:32 sem.thesem

一个被命名为thesem的信号量出现在/dev/shm/sem.thesem

Posix消息队列和System V消息队列主要的差别在于:
   1.对Posix消息队列的读总是返回最高优先级的最早消息,对System V消息队列的读则可以返回任意指定优先级的消息。
  2.当往一个空队列放置一个消息时,Posix消息队列允许产生一个信号或者启动一个线程,System V消息队列则不提供类似的机制。
Posix队列中消息具有以下的属性:
  1.一个无符号整数优先级;2.消息的数据部分的长度;3.数据本身
可将消息队列认为是一个消息链表,该链表的头中含有当前队列的两个属性:队列中允许的最大消息数以及每个消息的最大大小。消息队列具有虽内核的持续性。Posix消息队列常用的几个函数:
 1.mq_open (用于创建一个新的消息队列或打开一个已存在的消息队列)
#include<mqueue.h>
mqd_t mq_open(const char *name,int oflag,...        );
当实际操作是创建一个新的队列时,则需要mode和attr参数。attr参数是用于给新的消息队列指定某些属性。
struct mq_attr
{
    long mq_flags;    // 0或者O_NONBLOCK
   long mq_maxmsg; //队列中包含的消息数的最大限制数
   long mq_msgsize; //每个消息大小的最大限制数
   long mq_curmsgs;  //当前队列中的消息数
}
当attr参数为NULL时则使用默认值msg_max 10,msgsize_max 8192。而使用mq_open创建一个新的队列时,attr只能给它指定mq_maxmsg,mq_msgsize这两个属性。mq_open忽略 attr结构中的另外两个成员。在创建过程中需要注意的是,指定的这两个属性都必须小于等于msg_max或者msgsize_max的。查看系统中消息 队列的这两个限制值的方法是:
cat /proc/sys/fs/mqueue/msg_max
cat /proc/sys/fs/mqueue/msgsize_max
 系统默认值有些时候不够大,需要我们对这个限制数进行修改,方法如下:
修改/etc/sysctl.conf 在这个文件中添加 #mqueue max fs.mqueue.msg_max=100 fs.mqueue.msgsize_max=9000 fs.mqueue.queues_max=520 保存好后重启系统就好了。

下面编写一个创建Posix消息队列的程序:
#define FILE_MODE S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH
int main(int argc,char **argv)
{
    int c,flags;
    mqd_t mqd;
    struct mq_attr attr;
    flags = O_RDWR | O_CREAT;
    while( ( c = getopt(argc,argv,"em:z:")) != -1 )
    {
        switch(c)
        {
            case 'e':
                flags |= O_EXCL;
                break;
            case 'm':
                attr.mq_maxmsg =atol(optarg);
                break;
            case 'z':
                attr.mq_msgsize = atol(optarg);
                break;
        }
    }
    if(optind != (argc - 1 ))
    {
        printf("命令行参数个数出错!n");
        exit(0);
    }
    if(attr.mq_maxmsg == 0 | attr.mq_msgsize == 0)
    {
        printf("给的数值为零,不合法!n");
        attr.mq_maxmsg = 0;
    }
    mqd = mq_open(argv[optind],flags,FILE_MODE,((attr.mq_maxmsg != 0) ? &attr:NULL));
   
    mq_close(mqd);
    exit(0);
}
 在对这个程序编译时需要加上-lrt 如:gcc mqcreate.c -o mqcreate -lrt .那么怎么查看创建好的消息队列呢。在创建System V消息队列时可以使用ipcs命令来查看。而在这里则需要挂载。因为消息队列是创建在虚拟文件系统上的。该消息队列需要挂载后才能看见,并且像文件一样操作。创建过程方法如下:
[zd@zhengdan 消息队列]$ sudo mkdir /mnt
[zd@zhengdan 消息队列]$ sudo mount -t mqueue none /mnt
这样就可以在/mnt下看到所创建的消息队列了,此时是以文件形式存在了。
[zd@zhengdan 消息队列]$ ls -l /mnt
总用量 0
-rw-r--r-- 1 zd zd 80  7月 22 19:03 12
-rw-r--r-- 1 zd zd 80  7月 22 19:01 124
-rw-r--r-- 1 zd zd 80  7月 22 17:34 et
-rw-r--r-- 1 zd zd 80  7月 22 17:25 temp
 本人在这个地方比较疑惑的是每一个消息队列的占用的空间大小都为80个字节,而在下面的获取消息队列时候明显发现消息大小不止80。而在这里显示的文件大小不管是否写入消息,总是保持着八十个字节不变。本人认为可能是由于挂载的原因,毕竟不是真实的文件。具体原因有待以后研究。

1:在编译的时候要加上 -lrt 中的rt.so连接库,gcc mqcreatel.c -o mqcreatel -lrt

2:如果要查看创建的消息队列的文件, 需要挂载后才能看见,sudo mount -t mqueue none /mnt 则相应的消息队列文件在/mnt文件目录下.

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多