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