下一篇 上一篇 目录

6. 进程间通信

我们在上面看到的示例程序被称为实时进程。并非应用程序的每个部分都需要以实时方式编写。人们发现,只有程序中需要精确时间限制的部分才应该编写为实时进程。其他部分可以在用户空间中编写和执行。与实时线程相比,用户空间进程通常更容易编写、执行和调试。但是,用户空间 Linux 进程和实时线程之间应该存在一种通信方式。

进程间通信有几种方式。我们将讨论最重要和最常用的通信方式 - 实时 FIFO。

6.1 实时 FIFO

实时 FIFO 是单向队列(先进先出)。因此,在一个端口,进程将数据写入 FIFO,而在 FIFO 的另一端口,信息由另一个进程读取。通常,这些进程之一是实时线程,另一个是用户空间进程。

实时 FIFO 实际上是主设备号为 150 的字符设备 (/dev/rtf*)。实时线程使用整数来引用每个 FIFO(例如,2 代表 /dev/rtf2)。FIFO 的数量有限制。有一些函数,例如 `rtf_create()`、`rtf_destroy()`、`rtf_get()`、`rtf_put()` 等,用于处理 FIFO。

另一方面,Linux 用户进程将实时 FIFO 视为普通的字符设备。因此,可以在这些设备上使用诸如 `open()`、`close()`、`read()` 和 `write()` 等函数。

6.2 使用 FIFO 的应用程序

首先,让我们考虑一个简单的 C 程序 (文件名 pcaudio.c),用于通过 PC 扬声器播放音乐(仅包含两种音调)。目前,我们假设为了播放音符,我们只需要写入字符设备 /dev/rtf3。(稍后,我们将看到一个实时时间进程,它从这个 FIFO (/dev/rtf3) 读取数据并发送到 PC 扬声器)

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#define DELAY 30000

void make_tone1(int fd)
{
        static char buf = 0;
        write (fd, &buf, 1);
}

void make_tone2(int fd)
{
        static char buf = 0xff;
        write (fd, &buf, 1);
}

main()
{
        int i, fd = open ("/dev/rtf3", O_WRONLY);
        while (1)
        {
                for (i=0;i<DELAY;i++);
                make_tone1(fd);
                for (i=0;i<DELAY;i++);
                make_tone2(fd);
        }
}

现在,如果编译并运行上面显示的程序 (pcaudio.c),它应该会创建与方波相对应的规则声音模式。但在那之前,我们需要一个模块来从 '/dev/rtf3' 读取数据并将相应的数据发送到 PC 扬声器。这个实时程序可以在 rtlinux 源代码树 (/usr/src/rtlinux/examples/sound/) 中找到。使用命令 `insmod` 插入模块 sound.o。

由于我们已经插入了一个用于从设备读取数据的模块,我们现在可以执行我们的程序(使用 `gcc` 编译,然后执行相应的 `a.out`)。因此,当系统中没有其他(耗时的)进程时,该进程会产生某种程度上规则的音调。但是,当在另一个控制台中启动 X 服务器时,音调中会出现更长时间的静音。最后,当执行 `find` 命令(在 /usr 目录中查找文件)时,声音模式完全失真。这背后的原因是,我们是以非实时方式将数据写入 FIFO。

现在,我们将看看如何以实时方式运行此进程,以便在没有任何干扰的情况下产生声音。首先,我们将把上面的程序转换为实时程序。(文件名 rtaudio.c)

#include <rtl.h>
#include <pthread.h>
#include <rtl_fifo.h>
#include <time.h>

#define FIFO_NO 3
#define DELAY 30000
pthread_t thread;

void * sound_thread(int fd)
{
        int i;
        static char buf = 0;
        while (1)
        {
                for(i=0; i<DELAY; i++);
                buf = 0xff;
                rtf_put(FIFO_NO, &buf, 1);

                for(i=0;i<DELAY;i++);
                buf = 0x0;
                rtf_put(FIFO_NO, &buf, 1);
        }
        return 0;
}

int init_module(void)
{
        return pthread_create(&thread, NULL, sound_thread, NULL);
}

void cleanup_module(void)
{
        pthread_delete_np(thread);
}

如果尚未完成,请将模块 sound.o“插入”到内核中。通过为其编写 Makefile(如前所述)来编译上述程序,从而生成模块 `rtaudio.o`。在插入此模块之前,还有一件事。请注意,上面的程序运行在一个无限循环中。由于我们没有包含使线程休眠或停止的代码,因此线程不会停止执行。简而言之,您的 PC 扬声器将继续发出声音,您将不得不重启计算机才能执行其他任何操作。

因此,让我们稍微修改一下代码(仅在函数 `sound_thread()` 中),让线程本身来控制音调之间的延迟。

void * sound_thread(int fd)
{
        static char buf = 0;
        pthread_make_periodic_np (pthread_self(), gethrtime(), 500000000);

        while (1)
        {
                pthread_wait_np();
                buf = (int)buf^0xff;
                rtf_put(FIFO_NO, &buf, 1);
        }
        return 0;

}

这次,我们可以通过使用 `rmmod` 命令删除模块来停止该进程。

在这里,我们已经看到了实时 FIFO 如何用于进程间通信。也可以从上面的例子中理解 RTLinux 的真正需求。


下一篇 上一篇 目录