共享内存和信号

来源:互联网 发布:鞋业进销存软件 编辑:程序博客网 时间:2024/05/16 07:09

作者:曾宏安,华清远见嵌入式学院讲师。

1.共享内存概述

共享内存允许两个或更多进程共享一给定的存储区。因为数据不需要在各个进程之间复制,所以这是最快的一种进程间通信方式。使用共享内存时的关键点在于如何在多个进程之间对一给定的存储区进行同步访问。

例如若一个进程正在将数据放入共享内存区,则在它做完这一操作之前,其他进程不应该去取这些数据。通常,信号量被用来实现对共享内存访问的同步。如果只有两个进程访问共享内存,那么我们可以使用信号来实现同步。

2.举例说明

如上图所示,读者和写者依次读出和写入数据。当共享内存不可读或写时,相关进程会等待,直到收到对方的信号。

3.要点分析

我们以写者为例分析一下实现步骤

1) 读者和写者通过信号同步的前提是必须知道对方的进程号。这里可以利用共享内存的前四个字节存放自己的进程号并获取对方的进程号。
        2) 由于两个进程运行的先后顺序不确定,因此约定先运行的进程创建共享内存,并写入自己的进程号。后运行的进程打开共享内存,获取对方的进程号后再写入自己的进程号。
        3) 写者将内容写入共享内存后给读者发信号通知对方可以读,然后将自己阻塞,直到读者给自己发信号为止

4.参考代码
        // writer.c
        #include <stdio.h>
        #include <stdlib.h>
        #include <unistd.h>
        #include <string.h>
        #include <signal.h>
        #include <errno.h>
        #include <sys/types.h>
        #include <sys/ipc.h>
        #include <sys/shm.h>

#define SIZE 64

typedef struct
        {
                pid_t pid;
                char buf[SIZE];
        } shm;

void handler(int signo) // 信号处理函数
        {
                return;
        }

int main(int argc, char *argv[])
        {
                int key, shmid;
                shm *p;
                pid_t pid;

        signal(SIGUSR1, handler); // 设置信号处理方式

        if ((key = ftok(“.”, ‘s’)) = = -1)
                {
                        perror(“fail to ftok”);
                        exit(-1);
                }
                if ((shmid = shmget(key, sizeof(shm), IPC_CREAT|IPC_EXCL|0666)) = = -1)
                {
                        if (EEXIST = = errno) // 共享内存已存在
                        {
                                shmid = shmget(key, sizeof(shm), 0666);
                                p = (shm *)shmat(shmid, 0, 0);
                                p->pid = getpid(); // 写入进程号
                                pause();
                                pid = p->pid; // 保存对方进程号
                        }
                }
                else // 共享内存被创建
                {
                        p = (shm *)shmat(shmid, 0, 0);
                        pid = p->pid; // 保存对方进程号
                        p->pid = getpid(); // 写入进程号
                }

        while ( 1 )
                {
                        fgets(p->buf, SIZE,stdin); // 读标准输入保存到共享内存中
                        usleep(100);
                        kill(pid, SIGUSR1); // 通知读者可以读共享内存
                        pause(); // 等待读者发信号
                }

        return 0;
        }

读者可以参考以上代码实现reader.c,并添加程序退出时的处理。