linux ipc tools

来源:互联网 发布:云数据查询平台 编辑:程序博客网 时间:2024/05/29 10:45
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
原创粉丝点击