在GNU Hurd中感受Mach微内核的进程通信(IPC)

微内核之——Mach、L4 、SeL4 1.微内核发展历程 操作系统微内核技术起始于20世纪80年代,起初被设计用来解决传统一体化操作系统在可维护性、拓展性、可靠性和稳定性方面的诸多问题。但是随着微内核思想的进一步发展,最终形成一套操作系统设计的核心理念。操作系统微内核仅提供最简单、基本的服务,如通信功能。操作系统的普通服务,如文件管理、设备管理、网络管理等则在we内核以外提供。这种设计思路简化了核心系统的维护压力以提高系统整体的可... 阅读详情

什么是GNU Hurd

具体的时间线已经在官方维基页面得到详细描述[0],笔者在此就简单叙述一下。在1983年Richard Stallman开启了GNU项目,目的是创建一个自由的操作系统[1]。在接下来的开发中各种软件都已经到位了,其中包括编译器GNU Compiler Collection,编辑器Emacs,C库GNU C Library,调试器GNU Debugger,完整的列表可以查看这个链接。我相信绝大部分的基于Linux的distribution都或多或少的有安装这些软件,虽然可能注意到的人并不多。而其中最重要的问题就是这些应用应该在什么上面运行,答案就是GNU Hurd。正如官方维基说的那样[0],在1983宣布项目开始后,GNU准备使用的内核在很长时间内都没有确定下来,之后在1991年才有了个比较详细的计划:决定使用CMU开发的Mach微内核并且在此之上编写Hurd系统[0]。

一点题外话:在当时其实被看好会做到如今Linux系统规模的主要有两个系统,一个是FreeBSD而另一个就是GNU Hurd。但FreeBSD有一段时间陷入了法律纠纷而GNU Hurd则是开发方面比较缓慢。上一句的信息来源笔者没记错的话是来自《UNIX 编程艺术》。Linus也表示过“如果在1991春天GNU内核已经能在生产中使用了,他是不会启动Linux项目的:但事实是没有。”[3]

微内核(microkernel)

微内核简单来说就是把大部分功能放在用户空间(userspace)。拿Linux举例子就是这个链接指向RTC(Real-Time Clock)的一个驱动代码,而对于GNU Hurd而言,Mach才是它的内核,而跟Linux的RTC驱动代码类似功能的代码[2]并不属于Mach,即并不在内核空间中而是在用户空间内。可以看出好处之一即是如果RTC驱动有Bug会导致崩溃,在Hurd中只有运行着那段RTC驱动代码的进程会崩溃,而Linux则会直接整个崩溃。这也是普遍认为的微内核的好处之一。微内核的好处因为笔者认为已经听得耳朵要起茧了,所以在这里就不多说太多了,接下来就说说坏处。笔者最常听到的是由于IPC的频繁使用而导致频繁发生上下文切换(context switch)而带来的性能下降问题。这个问题在这篇论文中有提及,虽然论文中使用的是L4这个第二代微内核而不是Mach这个第一代微内核成员来和单内核比较,不过L4在进程通信(IPC)方面做了十分强大的优化,所以笔者认为会比Mach更能体现单内核和微内核之间的性能差距。实验使用了基于L4的Linux即L4Linux与纯Linux进行性能比较,结论是微内核给应用带来的性能下降大概在5%到10%。

一点题外话:因为笔者对微内核的了解不深,但有找到这一篇很不错的文章,讲述了三代微内核的特点。推荐给感兴趣的读者。

多服务器(multi-servers)和进程通信(IPC)

正如Hurd主界面介绍的那样,Hurd是一系列在Mach上运行的servers(服务器),并且由这些servers来实现文件系统,网络协议,等等Unix内核或类似内核所拥有的功能。拿上一段提到的RTC驱动来举例子,这个RTC驱动就是像服务器一样由进程在运行,有message(信息)发送到RTC驱动服务器进程后,RTC服务器就会根据信息要求来读取或写入RTC硬件,如果是读取就会把读到的东西再发回去。而传递信息(即Inter-Process Communication)的重任则是由微内核Mach来完成。

Mach Interface Generator (Mach接口生成器)

Mach有着自己的接口来让别的程序使用IPC功能,因为Mach的接口需要尽量考虑到大部分情况,因此其实直接使用是相对复杂的。为了缓解这个问题,一般开发会使用Mach Interface Generator (Mach接口生成器)[5]。它能隐藏掉构造message(信息)的复杂。不过写这篇文章的起因是因为下面这个链接:

http://walfield.org/pub/people/neal/papers/hurd-misc/mach-ipc-without-mig.txt

这个练习的目标是在不使用MIG的前提下写一个利用Mach来完成一次信息发送再得到返回的程序,是用来给人加深对Mach IPC的印象的。

对于想自己捣鼓的读者,笔者就将对练习有用的链接放在这里,GNU Hurd官网的搜索栏十分有用,请好好利用:
https://darnassus.sceen.net/~hurd-web/
https://darnassus.sceen.net/~hurd-web/microkernel/mach/documentation/
http://www.cs.cmu.edu/afs/cs/project/mach/public/doc/osf/kernel_principles.ps
http://www.cs.cmu.edu/afs/cs/project/mach/public/doc/osf/kernel_interface.ps
http://www.cs.cmu.edu/afs/cs/project/mach/public/doc/osf/server_writer.ps

在虚拟机中运行GNU Hurd

GNU Hurd是可以在真实硬件上运行的,但因为设备驱动的不足所以目前GNU Hurd只能在几款比较老款的Thinkpad笔记本上运行[6]。只为体验或完成这份练习的话虚拟机是完全足够的了。

目前相对比较稳定的Distribution是Debian。64位系统还不是非常完善,所以我们先使用32位的。可以在这个链接中查看所有可用Images。我们能运行以下指令来获得32位的Debian GNU Hurd Image。

wget https://cdimage.debian.org/cdimage/ports/latest/hurd-i386/current/debian-hurd.img.gz

然后把文件解压:

gzip -d debian-hurd.img.gz

笔者使用的是qemu,所以用这个指令来运行:

qemu-system-i386 -enable-kvm -m 2G -drive \
    format=raw,cache=writeback,file=debian-hurd.img \
    -nic user,hostfwd=tcp::2222-:22 -display curses -vga std

启动完成后会看到

Debian GNU/Hurd 12 debian tty3

login:

直接输入root然后Enter就能进入bash了。使用passwd给root加个密码。用exit退出去然后再用demo登录,然后再用passwd改个密码[7]。用vim或者个人喜欢的编辑器打开/etc/ssh/ssh_config把PasswordAuthentication yes那行的#去掉。接下来我们就可以直接再开一个新的terminal然后用ssh来在GNU Hurd上操作了:

ssh -p 2222 demo@localhost

然后准备工作就都完成了,接下来就可以开始研究练习了。

Mach IPC without MIG

练习目标是要在一个进程中创建两个线程,一个是客户一个是伺服器。客户要给伺服器发送一个信息并且要收到一个伺服器的回应。而信息的传递要使用Mach微内核的接口。

两个线程

我们就先写出两个线程吧。

#include <threads.h>
#include <stdio.h>

int
server (void *arg)
{
    printf ("I'm server\n");
}

int
client (void *arg)
{
    printf ("I'm client\n");
}

int
main (void)
{
    thrd_t server_thrd, client_thrd;

    thrd_create (&server_thrd, server, NULL);
    thrd_create (&client_thrd, client, NULL);
    thrd_join (server_thrd, NULL);
    thrd_join (client_thrd, NULL);
    return 0;
}

编译时要用这条指令:

gcc main.c -lpthread -o main

Ports

我们所编译出来的的main可执行文件在刚开始会由一个线程(thread)运行,在运行到两个thrd_create时又会多生出两个线程,那么总共就会有三个线程运行我们的代码,当然运行的路线(routine)是不同的,一个是main(),一个是server()而剩下的那个则是client()。正如Mach 3 Kernel Principles[9]中所描述的那样,这三个线程都是属于同一个task的。每个task会有一个port name space,每个port name代表一个port right,port name是在用户空间中看到的,而port right则是由Mach管理,就像文件描述符(file descriptor)一样。每个port right又代表了port和right。port是一个单向沟通通道(unidirectional communication channel),right则代表了task能对跟right一起的port所进行的操作。right的类型包括接收权限(receive right),发送权限(send right),单次发送权限(send-once right)等等[10]。简单来说就是如果想要用Mach沟通,那就先要有一个port。我们可以使用mach_port_allocate来获得一个port。

git diff:

diff --git a/main.c b/main.c
index f73724a..2eb9f38 100644
--- a/main.c
+++ b/main.c
@@ -1,5 +1,8 @@
 #include <threads.h>
 #include <stdio.h>
+#include <mach.h>
+
+static mach_port_t port;

 int
 server (void *arg)
@@ -17,6 +20,7 @@ int
 main (void)
 {
     thrd_t server_thrd, client_thrd;
+    mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE, &port);

     thrd_create (&server_thrd, server, NULL);
     thrd_create (&client_thrd, client, NULL);

这样子我们就获得了一个在自己的task中的port,而我们对这个port则拥有接收权利。读者可能会想问为什么要把client和server放在同一个task中而不是分开两个放。正如练习中描述的那样[8],放在同一个task中是为了简化实现,不然就会涉及到另一个关于central name server的课题了。

Message

练习描述中有提到信息的结构[8]:

Inline messages have the following general structure:

 [ [mach_msg_header_t] [[mach_msg_type_t][data]] [[mach_msg_type_t][data]]... ]
 ^ ^                                      ^^                               ^             ^
 | |                                       ||                              |                \- Second set of arguments
 | |                                       ||                              \- First set of arguments
 | \- Message header        |\- Header for first set of arguments
 \- Message                         \- Pay load

When data is marked out of line, the data section is detached.

我们打算让Client发送:"Alice\0"给Server并且让Server回复"Hello Alice\0",我们可以创建一个这样的结构:
git diff

diff --git a/main.c b/main.c
index 2eb9f38..2307de1 100644
--- a/main.c
+++ b/main.c
@@ -2,6 +2,13 @@
 #include <stdio.h>
 #include <mach.h>

+struct message
+{
+    mach_msg_header_t msg_header;
+    mach_msg_type_t first_header;
+    char string[100];
+};
+
 static mach_port_t port;

 int
@@ -13,6 +20,7 @@ server (void *arg)
 int
 client (void *arg)
 {
+    struct message m;
     printf ("I'm client\n");
 }

Client

在Mach kernel interface中能看到mach_msg就将是我们用来发送和接收message的接口函数了。头文件声明则在这里:

extern mach_msg_return_t
mach_msg
   (mach_msg_header_t *msg,
    mach_msg_option_t option,
    mach_msg_size_t send_size,
    mach_msg_size_t rcv_size,
    mach_port_name_t rcv_name,
    mach_msg_timeout_t timeout,
    mach_port_name_t notify);

可见参数还是蛮多的,参数在Mach 3 Kernel Interface中有介绍,笔者在此斗胆翻译一下:

msg: A message buffer.

即该变量指向存放着信息消息头的区域。在我们目前的代码中其实就是&(m.msg_header)。(笔者认为这里就很像网络协议中的消息头)

option: Message options are bit values, combined with bitwise-or. One or both of MACH_SEND_MSG and MACH_RCV_MSG should be used.

正如上面所言mach_msg既能发送也能用来接收,可能会有读者好奇 MACH_SEND_MSG | MACH_RCV_MSG代表了什么。它意味的发送了信息后还会期待一个信息返回回来,而它正是我们所要在Client里使用的了。

send_size: When sending a message, specifies the size of the message buffer. Otherwise zero should be supplied.

请注意,这里提到的message并不是"Alice\0"或Hello Alice\0,而是在上一节所提到的message。所以就是sizeof (struct message)。

rcv_size: When receiving a message, specifies the size of the message buffer. Otherwise zero should be supplied.

因为我们也打算收到一个回应,所以我们要填的值就是sizeof (struct message)。

rcv_name: When receiving a message, specifies the port or port set. Otherwise MACH_PORT_NULL should be supplied.

我们会从变量port接收信息,所以就填它。

timeout 和 notify

我们在这次练习中不怎么在乎所以就填MACH_MSG_TIMEOUT_NONE和MACH_PORT_NULL。

所以我们在Client中使用的mach_msg就会如下所示:

mach_msg (&(m.msg_header), MACH_SEND_MSG | MACH_RCV_MSG, sizeof (struct message), sizeof (struct message), port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);

当然在这之前还要加上变量m的一些初始化。

先是记录在Mach 3 Kernel Interface 327页的mach_msg_header:

typedef struct mach_msg_header {
    mach_msg_bits_t       msgh_bits;
    mach_msg_size_t      msgh_size;
    mach_port_t               msgh_remote_port;
    mach_port_t               msgh_local_port;
    mach_port_seqno_t  msgh_seqno;
    mach_msg_id_t          msgh_id;
} mach_msg_header_t;

可见参数也不少。

msgh_bits

还记得我们在main中分配的那个right吗,那是个发送权限,而正如练习中提到的:

Mach ports: how to allocate send and receive rights (the former implicitly in mach_msg and the latter directly via mach_port_allocate).

我们将会在msgh_bits中分配发送权限。我们会给msgh_bits填MACH_MSGH_BITS (MACH_MSG_TYPE_MAKE_SEND, 0)。这个操作意味着放一个之后变量msgh_remote_port所提及的port的发送权限到message中。没错在Mach中权限是可以通过IPC传来传去的,这也是capability-based的体现。因为在前面mach_msg中填的port的接收方还是我们这个task,因此我们可以获得一个发送权限。当然分享权限是有前提的,在源码中能看到MACH_MSG_TYPE_MAKE_SEND的前提是持有发送权限,而我们则已经在main中获取了发送权限[12]。

msgh_size

填入sizeof (struct message)就行。

msgh_remote_port: When sending, specifies the destination port of the message. The field must carry a legitimate send or send-once right for a port. When received, this field is swapped with msgh_local_port.

在这个练习中就是变量port。因为我们在msgh_bits中的操作,我们会有一个发送权限。

msgh_local_port: When sending, specifies an auxiliary port right, which is conventionally used as a reply port by the recipient of the message. ......

接收port我们就填个MACH_PORT_NULL。

msgh_seqno: The sequence number of this message relative to the port from which it is received. This field is ignored on sent messages.

因为就一个message,就填入0。

msgh_id: Not set or read by the mach_msg call. ......

我们用不到所以不管。

因此结果会是如下:

m.msg_header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0);
m.msg_header.msgh_size = sizeof (struct message);
m.msg_header.msgh_remote_port = port;
m.msg_header.msgh_local_port = MACH_PORT_NULL;
m.msg_header.msgh_seqno = 0;

再接下来就是m.first_header,描述在Mach 3 Kernel Interface 330页。

typedef struct {
    unsigned int        msgt_name : 8,
                                   msgt_size : 8,
                                   msgt_number : 12,
                                   msgt_inline : 1,
                                   msgt_longform : 1,
                                   msgt_deallocate : 1,
                                   msgt_unused : 1;
} mach_msg_type_t

msgt_name

我们要传输的数据是"Alice\0"和"Hello Alice\0",所以是MACH_MSG_TYPE_STRING_C。

msgt_size: Specifies the size of each datum, in bits.

一个char是一字节也就是8 bits,所以是8.

msgt_number: Specifies how many data elements comprise the data item.

我们就选择100。

msgt_inline: When FALSE, specifies that the data actucally resides in an out-of-line region. ......

我们的信息比较短能直接存在message里所以是true。

msgt_longform: Specifies, when TRUE, that this type descriptor is a mach_msg_type_long_t instead of a mach_msg_type_t.

我们是mach_msg_type_t所以是false。

msgt_unused: Not used, should be zero.

那就是0。

所以结果如下:

m.first_header.msgt_name = MACH_MSG_TYPE_STRING_C;
m.first_header.msgt_size = 8;
m.first_header.msgt_number = 100;
m.first_header.msgt_inline = true;
m.first_header.msgt_longform = false;
m.first_header.msgt_unused = 0;

最后再整合一下:
git diff

diff --git a/main.c b/main.c
index 2307de1..fcea63d 100644
--- a/main.c
+++ b/main.c
@@ -1,6 +1,8 @@
 #include <threads.h>
 #include <stdio.h>
 #include <mach.h>
+#include <string.h>
+#include <stdbool.h>
 
 struct message
 {
@@ -21,7 +23,28 @@ int
 client (void *arg)
 {
     struct message m;
-    printf ("I'm client\n");
+    mach_msg_return_t err;
+
+    m.msg_header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0);
+    m.msg_header.msgh_size = sizeof (struct message);
+    m.msg_header.msgh_remote_port = port;
+    m.msg_header.msgh_local_port = MACH_PORT_NULL;
+    m.msg_header.msgh_seqno = 0;
+
+    m.first_header.msgt_name = MACH_MSG_TYPE_STRING_C;
+    m.first_header.msgt_size = 8;
+    m.first_header.msgt_number = 100;
+    m.first_header.msgt_inline = true;
+    m.first_header.msgt_longform = false;
+    m.first_header.msgt_unused = 0;
+
+    strcpy (m.string, "Alice\0");
+
+    err = mach_msg (&(m.msg_header), MACH_SEND_MSG | MACH_RCV_MSG, sizeof (struct message), sizeof (struct message), port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
+
+    printf ("Client error code: %d\n", err);
+
+    printf ("From Server: %s\n", m.string);
 }
 
 int

笔者增加了一个err变量来检查mach_msg是否成功,现在编译后运行的话应该会看到如下的输出:

I'm server
Client error code: 0
From Server: Alice

err的值是0说明成功了,当然这不是我们最终想要的,我们还要把Server给写了,出现这个输出的原因笔者认为是Client接收了自己发出去的message所以mach_msg成功并且打印了"Alice\0"。

Server

比较艰难的阶段已经过去了,接下来的操作都跟之前写Client时有关了。Server要先接收信息,所以只要MACH_RCV_MSG而不是MACH_SEND_MSG | MACH_RCV_MSG。

git diff:

diff --git a/main.c b/main.c
index fcea63d..8200286 100644
--- a/main.c
+++ b/main.c
@@ -16,7 +16,13 @@ static mach_port_t port;
 int
 server (void *arg)
 {
-    printf ("I'm server\n");
+    struct message m;
+    mach_msg_return_t err;
+
+    err = mach_msg (&(m.msg_header), MACH_RCV_MSG, sizeof (struct message), sizeof (struct message), port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
+
+    printf ("Server receive error code: %d\n", err);
+    printf ("Server: from client: %s\n", m.string);
 }

 int

现在再编译运行的话我们就会看到:

Server receive error code: 0
Server: from client: Alice
<卡在这>

先庆祝一下!这意味着Server成功从Client接收了一个信息,而卡着说明Client正在等待着来自Server的回复。当然Client这辈子都等不到它的回复了,因为我们还没写。那就Ctrl-C结束进程然后把最后一步给写上去吧。

其实剩下的只要复制Client的发送方式就行啦:
git diff

diff --git a/main.c b/main.c
index 8200286..03ed4e2 100644
--- a/main.c
+++ b/main.c
@@ -18,11 +18,32 @@ server (void *arg)
 {
     struct message m;
     mach_msg_return_t err;
+    char s[100];
 
     err = mach_msg (&(m.msg_header), MACH_RCV_MSG, sizeof (struct message), sizeof (struct message), port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
 
     printf ("Server receive error code: %d\n", err);
     printf ("Server: from client: %s\n", m.string);
+
+    m.msg_header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0);
+    m.msg_header.msgh_size = sizeof (struct message);
+    m.msg_header.msgh_remote_port = port;
+    m.msg_header.msgh_local_port = MACH_PORT_NULL;
+    m.msg_header.msgh_seqno = 0;
+
+    m.first_header.msgt_name = MACH_MSG_TYPE_STRING_C;
+    m.first_header.msgt_size = 8;
+    m.first_header.msgt_number = 100;
+    m.first_header.msgt_inline = true;
+    m.first_header.msgt_longform = false;
+    m.first_header.msgt_unused = 0;
+
+    sprintf (s, "Hello %s\n\0", m.string);
+    strcpy (m.string, s);
+
+    err = mach_msg (&(m.msg_header), MACH_SEND_MSG, sizeof (struct message), sizeof (struct message), port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
+
+    printf ("Server send error code: %d\n", err);
 }
 
 int

最后编译运行后的输出:

Server receive error code: 0
Server: from client: Alice
Server send error code: 0
Server send error code: 0
Client error code: 0
From Server: Hello Alice

Client成功接收到了Server的回复!成功了!笔者就是不太清楚为什么会出现两次Server send error code: 0。不过至少是成功了。

最后再放个完整的代码:

#include <threads.h>
#include <stdio.h>
#include <mach.h>
#include <string.h>
#include <stdbool.h>

struct message
{
    mach_msg_header_t msg_header;
    mach_msg_type_t first_header;
    char string[100];
};

static mach_port_t port;

int
server (void *arg)
{
    struct message m;
    mach_msg_return_t err;
    char s[100];

    err = mach_msg (&(m.msg_header), MACH_RCV_MSG, sizeof (struct message), sizeof (struct message), port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);

    printf ("Server receive error code: %d\n", err);
    printf ("Server: from client: %s\n", m.string);

    m.msg_header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0);
    m.msg_header.msgh_size = sizeof (struct message);
    m.msg_header.msgh_remote_port = port;
    m.msg_header.msgh_local_port = MACH_PORT_NULL;
    m.msg_header.msgh_seqno = 0;

    m.first_header.msgt_name = MACH_MSG_TYPE_STRING_C;
    m.first_header.msgt_size = 8;
    m.first_header.msgt_number = 100;
    m.first_header.msgt_inline = true;
    m.first_header.msgt_longform = false;
    m.first_header.msgt_unused = 0;

    sprintf (s, "Hello %s\n\0", m.string);
    strcpy (m.string, s);

    err = mach_msg (&(m.msg_header), MACH_SEND_MSG, sizeof (struct message), sizeof (struct message), port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);

    printf ("Server send error code: %d\n", err);
}

int
client (void *arg)
{
    struct message m;
    mach_msg_return_t err;

    m.msg_header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0);
    m.msg_header.msgh_size = sizeof (struct message);
    m.msg_header.msgh_remote_port = port;
    m.msg_header.msgh_local_port = MACH_PORT_NULL;
    m.msg_header.msgh_seqno = 0;

    m.first_header.msgt_name = MACH_MSG_TYPE_STRING_C;
    m.first_header.msgt_size = 8;
    m.first_header.msgt_number = 100;
    m.first_header.msgt_inline = true;
    m.first_header.msgt_longform = false;
    m.first_header.msgt_unused = 0;

    strcpy (m.string, "Alice\0");

    err = mach_msg (&(m.msg_header), MACH_SEND_MSG | MACH_RCV_MSG, sizeof (struct message), sizeof (struct message), port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);

    printf ("Client error code: %d\n", err);

    printf ("From Server: %s\n", m.string);
}

int
main (void)
{
    thrd_t server_thrd, client_thrd;
    mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE, &port);

    thrd_create (&server_thrd, server, NULL);
    thrd_create (&client_thrd, client, NULL);
    thrd_join (server_thrd, NULL);
    thrd_join (client_thrd, NULL);
    return 0;
}

总结

可以看到MIG还是帮开发者减少了很多烦恼的。在练习中也能更加清晰的感受到微内核的IPC是怎么一回事。在这里也感谢Hurd的开发者们和练习的作者Neal H Walfield的付出。有任何不正确的地方欢迎指出。


作者:chenw1
链接:https://www.cnblogs.com/chenw1/p/18768256
本文来自博客园,欢迎转载,但请注明原文链接,并保留此段声明,否则保留追究法律责任的权利。
All right reserved.

原创作者: chenw1 转载于: https://www.cnblogs.com/chenw1/p/18768256
GNU Hurd微内核项目实战:hurd-0.9版本源码解析与构建 GNU Hurd采用微内核架构,将传统内核功能拆分为用户态的多个“服务器”(如文件系统、权限管理等),通过底层Mach微内核提供的进程间通信(IPC)机制协同工作。这种设计实现了高度模块化,提升了系统的可维护性与安全性。与Linux宏内核不同,Hurd的核心不直接处理硬件逻辑,而是依赖消息传递调度服务,体现了“机制与策略分离”的操作系统哲学,为构建灵活、可扩展的系统提供了理论基础和实践路径。一个典型的文件由一系列以AC_或AM_开头的宏调用组成,按特定顺序排列,形成层次清晰的配置流程。 阅读详情

相关推荐

LCD驱动芯片——BL55072A驱动程序

初始化子程序: START 0x7C; I2C子地址 0xEA;ICSET,软件复位芯片 0xC0;MODSET,关显示 0xF0;BLKCTL,关闪烁 0xA3;DISCTL,80Hz Line inversion,High Power mode 0xE8;ICSET,清复位bit1 STOP 显示刷新子程序: START 0x7C;I2C子地址 0xF0;BLKCTL,关闪烁 0xA3;DISCTL,80Hz Line inversion,High Power mode 0xE8或0xEC;ICSE

love421的博客 3175

GNU简介

GNU简介

Jackson_LCY的博客 1052

JMeter脚本录制-快速上手篇

有的项目应用场景下,压测时,需要考虑带宽因素,压测不只是压测接口,静态资源也要一起压测。一般来说,静态资源比较多,如果一个个手工去写请求,是比较繁琐的,此时,可以使用Jmeter的脚本录制功能。 步骤1,创建脚本录制模版。 JMeter菜单:文件>模版,下拉选择到Recording,点击“Create”按钮; 填写录制参数: hostToRecord:填写要录制的主机域名,例如,我们这里填入:www.baidu.com schemeToRecord:填写要录制的模式,例如,htt...

软件测试大叔 5734

GNU/Hurd深度解析:微内核的乌托邦与自由软件的永恒实验

在 Linux 占据主导地位的今天,Hurd 依然存在,并在少数贡献者的努力下缓慢前行。它或许永远不会成为主流,但它的存在本身就具有深刻的意义。它是对现有秩序的挑战,是对另一种可能性的探索,是对自由理想的执着坚守。GNU/Hurd,这个理想与现实交织的产物,将继续作为自由软件的永恒实验场,在计算机历史的角落里,静静地书写着其独特的篇章。

bulita great world 1461

Linux 桌面虚拟化技术 KVM

Linux 桌面虚拟化技术 KVM

高飞的博客 36万+

免安装版Notepad++终极配置:右键菜单+图标美化+多文件批量处理(2024新版)

本文详细介绍了如何为免安装版Notepad++进行终极配置,包括右键菜单添加、图标美化以及多文件批量处理功能。通过注册表编辑和高级定制,用户可以在便携版中实现与安装版相同的功能体验,同时保持系统整洁。特别适合开发者、系统管理员和效率追求者使用。

nut55的博客 830

GNU Hurd 0.6 发布,几十年后仍在早期开发阶段

Thomas Bushnell,BSG,Hurd 的主设计师日前正式发布了 GNU Hurd 0.6。Hurd内核设计取代Unix内核,最早的开发始于1986年,1991年5月正式公布,名称是“HIRD of Unix Replacing Daemons”的递归缩写。 在几十年开发之后,GNU Hurd仍然处于早期开发阶段,还是不稳定的版本,可用于 32-bit x86 设备,运行于 64-...

博客 1118

27、VXLAN BGP EVPN网络的自动化部署与运维管理

本文深入探讨了VXLAN BGP EVPN网络从初始配置到运维管理的全生命周期自动化流程,涵盖Day-0设备自动上线、Day-0.5增量变更与连接验证、Day-1覆盖服务配置与计算集成,以及Day-2监控与故障排查。详细介绍了推送与拉取两种配置模型、主流工具如Cisco VTS、NFM、DCNM的功能特点,以及开源自动化工具的应用。通过流程图和命令示例,系统展示了MAC/IP学习、BGP通告和VXLAN封装的验证方法,为构建高效、可扩展的数据中心网络提供了全面的技术指导。

banana的博客 80

@[TOC](Linux发展史-2)

GPL v1。

qq_38641599的博客 430

AutosarMCAL开发——基于TC367、EBTresos 开发之PORT

下图为开漏输出与推挽输出电路区别,推挽输出有两个极性相反的mos构成,一个管子导通另一个管子关闭以实现输出高低电平(电流较小不具备驱动能力),开漏输出仅使用一个MOS管,所以在这种模式下只能输出低电平,想要输出高电平需要外部提供(输入电流不可超过引脚最大电流显示否则会烧MCU)开漏输出:输出寄存器为低电平时,经过输出控制电路变成一个高电平,N-MOS导通,低电平从I/O引脚输出,输出寄存器为高电平时,P-MOS和N-MOS都是截止状态,电平无法通过,想要输出高电平需要外接上拉电阻。

qq_45583706的博客 1405

AUTOSAE软件配置-项目标准配置 (PSC) 导入达芬奇( DaVinci Configurator)

文件是“可拆分的”,这意味着如果格式符合 AUTOSAR 拆分规则,则可以与其他。)是一个ARXML文件,主要由OEM提供,其中包含。)工件,可以作为输入文件导入到达芬奇配置器专业版。导入的参数将以只读方式处理。不允许在多个 ARXML 文件中导入一个。的已存在数据或从输入文件派生的数据。可以作为输入文件导入到另一个。文件会覆盖用户配置的基本。元素和诊断数据预配置。定义 [数据结构]。中,可以导出一个或多个。

m0_49148264的博客 1010

linux 宏内核 hurd,以GNU/Linux为基础的开源平台_Microsoft SERVER 2012 简体中文标准版_服务器知识学堂-中关村在线...

对于Debian并没有正式的商业支持,但是通过它的咨询页面在世界范围连接Debian顾问,比其它Linux的母版本比如Ubuntu,Linux Mint等而言,Debian则更多地开发子版本。由于Debian项目众多内核分支中以Linux宏内核为主,而且Debian开发者所创建的操作系统中绝大部分基础工具来自于GNU工程,因此“Debian”常指Debian GNU/Linux。Debian Li...

weixin_39626690的博客 324

全局注意机制 GAM | Global Attention Mechanism: Retain Information to Enhance Channel-Spatial Interactions

全局注意机制 GAM

Coding... 2576

关于 Mac OS X 内核技术来源

Mach(Multiple Asynchronously Communication Hosts) 是一个由卡内基梅隆大学开发的操作系统内核,Mach的开发是为了取代BSD的UNIX核心。 Mach 继承卡内基梅隆大学的 Accent kernel,Mach 项目主导人理查德·拉希德曾于微软的研究部门担任高级人员,后成为微软副总裁。另一位 Mach 开发者阿瓦德斯·特凡尼安(Avie Tevanian)曾是 NeXT 首席程序员,之后担任苹果电脑软件技术部门主管直到2006年。 该项目在1985年启动,并且

liaowenxiong的博客 3282

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

GNU Hurd 项目教程

GNU Hurd 项目教程 1. 项目介绍 GNU Hurd 是 GNU 项目的一部分,旨在开发一个自由的操作系统内核,作为 Unix 内核的替代品。GNU Hurd 是一个微内核服务器集合,运行在 GNU Mach 微内核之上,提供文件系统、网络协议、文件访问控制等功能。GNU Hurd 自 1990 年以来一直由 GNU 项目开发,目标是创建一个通用的、自由的操作系统内核。 2. 项目快速启动...

gitblog_00626的博客 779

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

hurd和linux内核区别,Hurd内核取代GNU Guix对Linux的支持

系统520网站最新消息,GNU guix项目官员最近宣布,他们将逐步终止对Linux内核的支持,并用Hurd内核取代它。Guix 是一个事务性包管理器,也是一个尊重用户自由的 GNU 系统高级发行版。除了标准的软件包管理功能外,Guix 支持透明升级和回滚、无特权包管理、针对每个用户的配置文件,以及垃圾回收。去年五月,经过多年开发后,GNU Guix 终于发布了 1.0.0 版本。官方公告表示,这...

weixin_42510962的博客 1459
上一篇: java校验文件魔数
翁翔坚
博客等级 码龄8年 26粉丝 68原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值