我们在上面看到的示例程序被称为实时进程。并非应用程序的每个部分都需要以实时方式编写。人们发现,只有程序中需要精确时间限制的部分才应该编写为实时进程。其他部分可以在用户空间中编写和执行。与实时线程相比,用户空间进程通常更容易编写、执行和调试。但是,用户空间 Linux 进程和实时线程之间应该存在一种通信方式。
进程间通信有几种方式。我们将讨论最重要和最常用的通信方式 - 实时 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()` 等函数。
首先,让我们考虑一个简单的 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 的真正需求。