PostgreSQL—数据库领域的 Linux
Linux 在通用领域普及的一个里程碑是办公应用程序的可用性。除了文本处理、电子表格和图形应用程序外,数据库也是这类应用程序的重要组成部分。到目前为止,数据库市场上的巨头如 Sybase 和 Oracle 尚未发布其产品的 Linux 版本;然而,有几个数据库软件包可用于 Linux,其中大多数是商业应用程序。有一个数据库值得特别关注,因为它现在的开发方式与 Linux 类似。这个数据库就是 PostgreSQL,以前被称为 Postgres95。去年 10 月,发布了 6.2.1 版本,现在正是仔细研究这个项目的好时机。
PostgreSQL 的历史悠久,始于 1986 年,当时 Michael Stonebraker 开始开发它作为 Ingres 数据库系统的继任者。他的项目的主要目标是表明关系数据库系统可以应对现代可扩展性需求以及面向对象的系统。由此产生的产品被称为对象关系数据库,因为它是一个关系数据库系统,具有一些面向对象的特性,如继承和用户定义的函数、运算符和访问方法。1994 年,随着 4.2 版本的发布,Postgres 的工作停止了,因为 Stonebraker 的项目结束了。然而,一些工作人员决定继续他们的工作,并在 1995 年发布了 Postgres95,使用 SQL 作为查询语言而不是 PostQuel。现在,开发工作以 PostgreSQL 的名义继续进行,由 Marc G. Fournier 协调的互联网志愿者团队负责。
该软件包并非在 GPL 下发布。它有自己的许可证,允许分发修改后的版本,即使没有源代码,只要版权声明保持不变。
多用户环境的客户端/服务器数据库
使用 TCP/IP 进行网络连接
三种身份验证方法:Kerberos,基于主机/用户的身份验证和用户名/密码身份验证
SQL 作为查询语言(它并非完全符合 ANSI SQL92 标准;不可用的选项包括嵌套子查询、ORDER BY 语句中的 HAVING 子句、OUTER JOIN、表创建期间的 PRIMARY 和 FOREIGN KEY 语句。)
多种索引类型,唯一索引和多列索引
用户定义的函数(SQL,C),运算符和数据类型
用户定义的序列和触发器函数
C,C ++,Objective-C,Java,Perl,Tcl/Tk 和 Python 的语言接口
可用的第三方 ODBC 驱动程序
移植到 Linux/Intel,Linux/Solaris,Linux/Alpha,AIX,DEC Alpha AXP,FreeBSD,BSDI,DG/UX,HP-UX,Nextstep,Solaris x86,Solaris Sparc,SunOS Sparc,SGI Irix,SCO,Intel SVR4,Ultrix
客户端/服务器数据库与 MS-DOS 和 Windows 机器用户熟悉的单体系统不同。仅启动一个程序来使用数据库是不够的。至少有三个不同的应用程序同时运行。一个是服务器,在 PostgreSQL 中称为 postmaster。它是一个守护进程,它监视 TCP 端口(通常是端口 5432)以等待来自第二个应用程序的连接:用户客户端。这个用户客户端可以是 psql,这是一个用于发送 SQL 查询的命令行工具,它随 PostgreSQL 一起提供,也可以是专门的应用程序,例如希望存储从 HTML 表单收集的数据的 CGI 程序。一旦客户端连接,postmaster 就会启动第三个应用程序:后端服务器。这是实际的数据库引擎,因为只有后端服务器才能直接访问数据库的存储记录。postmaster 将客户端与后端服务器连接,然后等待其他连接。与此同时,客户端将其查询发送到后端并接收其答复,通常是大量数据。当客户端没有其他查询时,连接断开,后端服务器退出。
此过程的后果是,客户端(用户使用的程序)无法直接访问数据库。它不知道后端发送多少记录以响应查询。除非后端发出成功信号,否则它不知道插入的数据是否已成功处理。因此,诸如 dBase 或 Access 中的花哨的表编辑器很难实现,并且目前不存在。
PostgreSQL 的安装目前有点繁琐,但随着每个版本的发布,它变得越来越容易。6.2.1 版本带有 configure 支持以及详细而全面的安装说明,其中包含针对 Linux 的特定说明。我将提到安装过程中可能遇到的一些陷阱。确保您的系统上安装了最新版本的 flex。特别是 v2.5.3 与 PostgreSQL 不兼容,而 v2.5.2 和 v2.5.4 工作正常。对于客户端 psql,您需要来自 GNU Readline 的开发库,这些库在某些 Linux 发行版上默认未安装。
在 Linux 上,PostgreSQL 是使用名为 libpq.so 的共享库编译的,所有客户端应用程序都使用该库。您需要通过将目录 $POSTGRESDIR/lib 添加到文件 ld.so.config 中,以确保动态链接器找到此库。特别是对于旧版本的 ld-linux.so(例如 v1.8.5),还需要将此目录添加到环境变量 LD_LIBRARY_PATH 中。我在我的 rc.local 文件中执行此操作,该文件启动 postmaster,因此它看起来像这样
echo "starting PostgreSQL postmaster..." export LD_LIBRARY_PATH=/opt/lib/pgsql/lib su postgres -c "postmaster -D/opt/lib/pgsql/data \ -d 1 &> /opt/lib/pgsql/postmaster.log &"
在源发行版的 contrib/linux 目录中,可以找到一个更复杂的启动脚本,名为 postgres.init,它将 postmaster 启动集成到 RedHat 运行级别系统中。
在启动 postmaster 之前,编辑数据目录中的 pg_hba.conf 文件。此文件包含指示哪些主机和用户被允许连接到数据库后端的信息。本地操作所需的最小文件是
# TYPE DATABASE IP_ADDRESS MASK USERAUTH MAP host all 127.0.0.1 255.255.255.255 trust # The above allows any user on the local system to # connect to any database under any user name.
如文档中所述,必须不要以 root 用户身份启动 postmaster,而是以特殊的 postgres 用户 ID 启动它。
为了使客户端能够工作,它们必须知道在哪里找到数据库。因此,您必须为每个用户设置环境变量 PGDATA 和 PGLIB。我在 /etc/profile 中执行此操作。此外,每个应该有权访问 PostgreSQL 的用户都需要在系统数据库中具有适当的访问权限。这是通过 createuser 程序完成的,该程序由 postgres 帐户运行。createuser 为您提供了授予用户创建数据库和/或添加其他用户的权利的选项。可以对这两个问题都说“否”,从而仅允许用户访问现有数据库。如果您将 PostgreSQL 作为 WWW 数据库后端运行,则必须为 Web 服务器的用户 ID 设置访问权限。
开始使用 PostgreSQL 的最佳方法是阅读用户手册。它是为 Postgres95 v1.0 编写的,可以追溯到 1995 年 9 月,但其中提供的信息仍然有用。其他有价值的信息来源是教程、手册页和 /doc 目录中的各种文件。在本文中,我将尽可能使用教程中的示例。
首先,创建一个数据库,即几个表以及诸如索引和视图之类的随附数据的命名容器。要使用名为 tutorial 的数据库,请键入以下命令
createdb tutorial
现在,您自动成为数据库的所有者,并拥有对它的完全访问权限。其他用户只有在您授予他们适当的权限后才能访问。
接下来,将该数据库连接到客户端程序。我们将使用 psql,它随 PostgreSQL 一起提供。如果您喜欢图形界面,还有一个名为 mpsql 的 Motif 客户端可用(带有适用于 Linux 的预编译二进制文件)。mpsql 具有强大的编辑功能,但不提供用于列出现有表和数据库的特殊本地命令。它还缺少 psql 中可用的帮助系统。要使用 psql,请键入
psql tutorial
此命令为您提供了一个类似 shell 的环境,您可以在其中发出 SQL 命令。由于 Readline 支持,您具有命令历史记录和文件名完成功能,并且键绑定与 bash shell 相同。您还可以输入本地命令,如果您在它们前面加上反斜杠,则客户端会首先处理它们。输入 \? 以获取本地命令列表。所有其他命令都直接发送到后端。命令可以键入在单独的行上。它们将存储在本地缓冲区中,直到您输入以 ; (分号)结尾的行,然后缓冲区将发送到后端。有关 SQL 命令的帮助,请键入 \h。
SQL 命令可以在命令行上传递以进行 shell 脚本编写。\i 命令从磁盘读取文件并将其内容作为 SQL 命令执行。请务必始终对 psql 使用绝对路径名,因为后端不知道客户端的当前工作目录。
接下来,创建以下两个表
create table cities ( name text, population float8, altitude int--this is a comment ); create table capitals ( state char2 ) inherits (cities);
text 类型是可变长度的字符串。如果您输入 int,则会得到一个四字节的整数值。PostgreSQL 带有 43 个预定义的数据类型,包括几种用于时间和日期类型的值,许多用于几何对象的类型,例如点、圆和多边形,以及布尔类型。也支持数组。所有类型都在 pgbuiltin(l) 手册页中描述。如果您需要其他类型,可以添加自己的类型。请注意,自 v6.1 起,标识符名称不再区分大小写。
此示例还说明了 PostgreSQL 的一个特殊功能:对象继承。第二个表继承了 cities 表中的所有字段,并添加了一个字段。稍后我将展示如何利用该功能。
PostgreSQL 缺少一个经常需要的功能,即在 create 子句中定义主键的能力。主键用于定义元组的默认排序顺序,并确保具有该键的字段不能包含重复值。由于用于存储记录(或元组)的方法,因此不支持此功能。数据库中的每个元组都获得一个唯一的对象标识符 (oid) 值,该值不仅在表中是唯一的,而且在整个数据库中也是唯一的。无法保证表中的特定顺序。从 PostgreSQL 6.0 版开始,可以创建唯一索引,因此可以通过索引实现相同的效果。要为我们的示例表创建唯一索引,请键入
create unique index on cities using btree (name);
有三种方法可用于索引创建:btree,rtree 和 hash。可以在关键字“using”之后指定方法。只有 btree 方法允许最多七个键的多个键索引。请注意,并非所有数据类型都受所有索引类型支持。特别是,rtree 索引仅适用于几何类型。如果未指定索引类型,则默认使用 btree。索引显着提高了表访问速度,应尽可能使用。
元组的最大大小为 8192 字节。实际上,它略小一些,因为 PostgreSQL 需要一些空间来存储内部数据。此空间量因平台而异。如果您需要更大的字段,请使用大对象接口,该接口提供无限的透明数据字段,例如其他数据库中的 MEMO 或 BLOB 字段;但是,您需要特殊的函数来访问它们。
要在表中输入数据,请使用 insert 命令
INSERT INTO cities VALUES ('San Francisco', 7.24E+5, 63); INSERT INTO cities VALUES ('Las Vegas', 2.583E+5, 2174); INSERT INTO cities VALUES ('Mariposa', 1200, 1953); INSERT INTO capitals VALUES ('Sacramento', 3.694E+5, 30, 'CA'); INSERT INTO capitals VALUES ('Madison', 1.913E+5, 845, 'WI');
要从表中获取数据,请使用 select 命令。这是一个非常强大的命令,因此我将仅演示其某些特性。
-- this will return all records in the table select * from cities; select * from capitals; -- to get also the records of the -- inherited tables, use this syntax: select * from cities*; -- here are some variants to limit the returned -- data: select name, altitude from cities where altitude > 500;要更改表中的某些值,请使用 update 命令,其语法与 select 命令类似
update cities -- population grows by 10% set population = population * 1.1 where name = 'Mariposa';当您定期更新数据时,您会注意到表不断增长,即使您没有添加新元组也是如此。这不是错误,这是另一个称为时间旅行的特殊功能。PostgreSQL 保留表中所有数据更改的历史记录。要访问此数据,您必须使用特殊限定符
select name, population from cities['epoch', 'now'] where name = 'Mariposa';此示例将列出 Mariposa 的两个字段(名称和人口)的所有值,从数据库创建到目前为止。如果您不希望保留历史数据,可以使用 vacuum 命令将其删除。在未来的 7.0 版本中,时间旅行功能将消失,但此时您需要定期清理数据库。vacuum 命令还具有更新内部数据的附加目的,以便可以更快地进行查询。因此,最好定义一个 cron 作业,该作业每晚运行 vacuum。
时间和日期值的处理方式非常灵活。我将在一个数据库中演示这一点,该数据库用于跟踪我连接到 Internet 提供商的电话费用。我的数据库结构和示例查询如下所示
create table telephone ( datum abstime, online_secs int4, units int2, costs float8 ); select * from pppcosts where datum >= 'yesterday'::abstime; datum |online_secs|units|cost Tue Jul 08 00:16:22 1997 MET DST| 486| 3|0.36 Tue Jul 08 20:18:52 1997 MET DST| 1476| 10| 1.2 Wed Jul 09 01:06:33 1997 MET DST| 3317| 14|1.68
此查询返回我从昨天 00:00:00 到现在的在线事件。术语 'yesterday'::abstime 是一种显式类型转换,可确保解析器获得文字值的正确类型。在这种情况下,它不是必需的,但有时输入类型可能会被错误地猜测,并返回错误。显式表示法避免了此类错误。
也可以获取数据的聚合。从我的电话费用表中,我可以获取过去 30 天的费用,方法是给出以下查询
select sum(online_secs) as seconds, sum(units) as units, sum(costs) as costs where datum > ('now'::abstime - '30 days'::reltime);
关键字 as 将列标题设置为指定的值;否则,它们都将读取 sum。
还支持 SQL date 类型,该类型接受 mm/dd/yyyy 格式的日期。甚至有一种机制可以更改外语的日期格式(目前仅支持欧洲和美国格式)。使用 set 命令,如下所示
set datestyle to 'european'; set datestyle to 'us';
日期格式已更改为 dd/mm/yyyy,然后又改回 mm/dd/yyyy。在 v6.1 中,逗号必须跟随关键字“european”和“us”。在以后的版本中,此错误已修复。
另一个巧妙的功能是序列命令。数据库的常见需求是某些列包含数字序列,该序列在添加新记录时自动递增。为此,请通过给出以下语句来创建序列
create sequence id start 1000 increment 10; insert into table values (nextval(id),...);
此示例将创建一个名为 ID 的序列,该序列从 1000 开始,并且每次函数 nextval() 返回唯一数字时,该序列都会增加 10。
下一个示例显示如何实现用户定义的函数。此技术可用于克服 PostgreSQL 的限制—数据库系统无法处理子查询。包含另一个查询的查询(通常在 where 子句中)称为子查询。PostgreSQL 仅间接支持它们,即,当子查询隐藏在函数中时。
-- I will use the cities table again create function big_city() returns set of cities as 'select * from cities* where population > 700000' language 'sql'; select name(big_city()) as highpop;
在示例中,该函数从 cities 表返回一个元组。也可以仅通过编写例如 returns int4 来返回单个值。示例中的 language 命令告诉解析器这是一个查询语言函数。使用编程语言函数(例如 language 'c'),您可以使用 C 编写自己的函数,并将它们作为动态模块直接加载到后端。此机制也用于创建用户定义的类型。(示例可以在源树的 tutorial 目录中找到。)函数与表一样永久存储。因此,当不再需要它时,必须通过给出 drop function 命令显式删除它。
我在这里仅介绍了一些 SQL 语言的亮点,重点是 PostgreSQL 的特殊功能。我还省略了从不同表中联接数据、创建视图以及将操作封装在事务块中以确保执行所有操作或不执行任何操作的示例。我还省略了提及游标,游标允许您限制从查询返回的元组数量。这些和其他功能最好从有关 SQL 查询语言的书籍中学习。我使用了 J.H. Trimble、D. Chappel 等人撰写的 A Visual Introduction to SQL 这本书,由 Wiley 于 1989 年出版,用于我的学习。它是针对新手的,并且易于理解。
PostgreSQL 悠久历史的一个优势是提供了许多编程接口。C,C ++,Perl5,Tcl 和 Java JDBC 的接口随版本一起发布。Python 和 Objective-C 的软件包也可用(后者来自 GNUStep 项目)。使用这些库,您可以开发自己的客户端应用程序。
C 库是 PostgreSQL 的基本接口,因为它被大多数其他库使用。使用它的最佳示例是 psql 客户端。如果您使用 C 编写客户端,则此程序将教您 C-API 的许多内部工作方法。
我现在将讨论一个小型程序,该程序将 cities 表中的所有元组写入屏幕。(源代码可以从 ftp://ftp.linuxjournal.com/pub/lj/listings/issue46/2245.tgz 获取。)
C 接口位于 libpq 库中。如果您已正确安装 PostgreSQL,则链接器应找到该库。使用设置的 -lpq 选项链接您的程序。在源代码中,您必须包含带有以下行的头文件
#include <libpq-fe.h>
确保编译器找到头文件;它们通常位于 $POSTGRESDIR/include 目录中。
与后端对话的第一步是建立与数据库的连接。这是通过命令完成的
char* dbname; strcpy(dbname, "tutorial"); conn = PQsetdb(host, port, options, tty, dbname);
所有参数的类型均为 char*。如果其中一个为 NULL,则使用默认值。通常只需要指定数据库名称和主机名(如果它不是本地主机)。该函数返回一个指针,该指针必须用于进一步访问此连接。
要测试 PQsetdb 操作是否成功,可以使用以下代码
if (PQstatus(conn) == CONNECTION_BAD) { printf(stderr, "Connection to database '%s' failed.\n", dbname); fprintf(stderr, "%s", PQerrorMessage(conn)); PQfinish(conn); exit(1); }
PQerrorMessage 返回详细的错误消息。使用 PQfinish 终止连接并释放所有内部缓冲区。
成功连接后,可以通过以下方式将查询发送到数据库
result = PQexec(conn, "select * from cities"); if ((!result) || (PGRES_TUPLES_OK != PQresultStatus(result))) { fprintf(stderr, "Error sending query.\nDetailed report: %s\n", PQerrorMessage(conn)); PQfinish(conn); exit(1); }
同样,必须检查操作的结果,现在使用函数 PQresultStatus,该函数根据要执行的数据库操作返回不同的代码。在我们的例子中,我们期望返回一些元组,因此我们针对此条件进行测试。另一个可能的值是 PGRES_COMMAND_OK,当发送诸如 INSERT 之类的查询并且不返回数据时,将返回该值。返回的指针指向记录数据中的结构。该结构可能非常大。不要尝试直接访问此结构;请改用 libpq 的函数,因为该库的内部工作方法可能会发生变化。作为演示,以下是打印出城市数据库内容的代碼
printf("name population altitude\n\n"); for (i = PQntuples(result)-1; i >= 0; i--) { printf("%s %s %s\n", PQgetvalue(result,i,0), PQgetvalue(result,i,1), PQgetvalue(result,i,2)); }函数 PQgetvalue 返回以 null 结尾的 ASCII 字符串形式的数据,而与字段类型无关。要确定字段类型,可以使用函数 PQftype。此函数是该库的弱点,因为它仅返回难以处理的类型的内部编码。为了正确解释它,必须知道所有数据类型都作为单独的元组存储在 PostgreSQL 的系统数据库中。PQftype 的返回值是该元组的 OID,即唯一标识符。要标识类型,您必须查询系统数据库
-- get the type with internal number 16 (bool) select oid, typname from pg_type where oid = '16'::oid;
如果您喜欢 C 以外的语言,则 PostgreSQL 很可能支持它。编程技术与 C 库非常相似;实际上,一些面向对象的接口使编程更简单。
其中一个接口是 libpq++ 库,它是用 C++ 编写的,并包含在发行版中。在 v6.1 版本中,它经历了重大修订。PgEnv 类用于操作环境,例如设置连接的端口号以及库和数据库的路径名。主要类是 PgDatabase,用于建立连接和执行查询。它提供了非常方便的方法来对成功操作进行内部检查。还有用于访问大对象 (PgLargeObject)、使用事务块 (PgTransaction) 和定义游标 (PgCursor) 的类。默认情况下不构建 libpq++,您必须使用提供的 Makefile 对其进行编译。
Perl5 接口 Pgsql_perl5 也必须使用 Makefile 进行编译。它提供了两个不同的接口。一个几乎与 libpq 相同,因此将 C 应用程序移植到 Perl 很容易。另一个接口使用 Perl5 的面向对象的功能。清单 1 演示了该接口的使用。其目的是列出所有作为数据库所有者的用户。要运行此脚本,您需要在系统数据库中具有适当的访问权限。如您所见,使用面向对象的接口,无需显式关闭连接。当连接对象被销毁时,模块会自动处理此问题。
与 Apache Web 服务器和一些单独可用的 Apache 模块一起,PostgreSQL 可用于为 Web 提供数据库服务。这些模块之一是 mod_auth_pg95,它允许使用 PostgreSQL 数据库进行用户身份验证。为了使其工作,您必须使用自己的用户和组 ID 而不是默认的“nouser”和“nogroup”安装 Web 服务器。可以使用文件 httpd.conf 中的配置语句 User 和 Group 完成此操作。然后有必要将此帐户告知 PostgreSQL,以便它接受来自 Web 服务器的连接,使用 createuser。在 Apache 配置文件 access.conf 中,您必须告诉 Web 服务器在哪里可以找到身份验证数据。以下是示例配置
<Directory /DocumentRoot/MySecrets> Auth_PGhost localhost Auth_PGport 5432 Auth_PGdatabase www Auth_PGpwd_table apache_user Auth_PGuid_field user Auth_PGpwd_field password AuthType Basic AuthName My Secrets require valid-user </Directory>
现在,创建一个名为“www”的数据库,其中包含一个名为“apache_user”的表,该表包含字段 user 和 password。我建议您不要在 Web 服务器帐户下创建表,以确保安全。如果您在另一个帐户下创建表并使用命令授予访问权限
grant select on apache_user to apache;那么服务器不能用于插入新用户或删除整个表。要填充表,可以使用 Perl 脚本 pg95passwd.pl,该脚本随 mod_auth_pg95 一起提供。它还会为您加密密码。
我要介绍的第二个 Apache 模块是 PHP/FI。它也可以作为独立的 CGI 脚本使用,但将其编译为模块更安全。PHP/FI 允许您以类似于 Microsoft 的 Active Server Pages 的方式将脚本嵌入到您的网页中。可以将其配置为允许脚本访问 PostgreSQL。PHP/FI 使用其自己的脚本语言,该语言易于学习。PostgreSQL 部分基于 libpq。如果您了解 libpq,则应该在使用 PHP/FI 进行 PostgreSQL 访问时没有问题。我无法介绍 PHP/FI 的所有功能,但我将在 清单 2 中提供一个示例脚本,该脚本读取 cities 表的内容并将其放入 HTML 表格环境中。
PHP/FI 为某些数据类型提供自动转换。与 C 接口不同,表值以其正确的格式返回,类型为 integer,boolean,oid,float 和 real。数组始终作为字符串返回。
当您编译 PHP/FI 以与 PostgreSQL 一起使用时,在 php.h 中定义编译器开关 MAGIC_QUOTES 使生活更轻松。此选项会导致 HTML 表单中 GET 和 POST 数据中的所有单引号和双引号字符自动转义。
PostgreSQL 现在由几个志愿者开发,他们通过互联网协调他们的工作。邮件列表用于讨论实施细节。在 FTP 站点 (ftp://ftp.postgresql.org/) 上,每晚都会提供包含最新源代码的 tar 存档文件。程序员使用 FreeBSD 实用程序 sup 来同步他们的源代码树。还有一个文档项目,其中包含手册页和用户指南
计划在未来实现完全符合 SQL92 标准,并不断加快数据库操作速度。于 1997 年 10 月发布的 6.2.1 版本使 PostgreSQL 离此目标更近了一步。现在可以在表创建时指定默认值和约束,以检查新插入的数据是否符合特定条件。也可以编写触发器函数,每当选择、插入或更新表数据行时都会执行这些函数。还有新的字符串函数,如 trim() substring() 和 position(),这使得字符串操作非常方便。新的服务器编程接口为用户提供了编写服务器存储过程和使用触发器实现完整性检查的能力。
与广泛使用的 mSQL 相比,只要仅涉及简单查询,PostgreSQL 就会慢得多。但是对于包含多个表的复杂联接的查询,PostgreSQL 应该更快。mSQL 被编写为一个程序,为程序员或网站管理员提供一个小型且快速的工具,用于简单的数据库操作。与此相反,PostgreSQL 是一个功能齐全的数据库系统。它提供了更多的数据类型,更好的可扩展性,并且通过两个可用的查询工具 psql 和 mpsql,提供了更友好的用户体验。
有一些商业数据库系统可以免费作为个人版本使用,例如 Solid 和 Yard-SQL。这些系统完全符合 SQL92 标准,与它们相比,PostgreSQL 的功能受到限制。使 PostgreSQL 在这些竞争对手中脱颖而出的功能是它是免费提供的,并且任何有兴趣的人都可以参与其进一步开发。
Rolf Herzog 自从他使用早期的 TRS-80 迈出第一步以来就对计算机着迷。当寻找 Windows 的替代品时,他使用内核版本 1.2.3 进入了 Linux。现在,他在德国杜伊斯堡的 Steinheim 研究所担任计算机顾问。他正在攻读政治学学位,因为社会系统理论像计算机系统一样激发着他。可以通过电子邮件 rolf@culthea.gun.de 与他联系。