找回密码
 立即注册
首页 业界区 业界 在GNU Hurd中感受Mach微内核的进程通信(IPC) ...

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

育局糊 2025-6-4 08:24:40
什么是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。
  1. wget https://cdimage.debian.org/cdimage/ports/latest/hurd-i386/current/debian-hurd.img.gz
复制代码
然后把文件解压:
  1. gzip -d debian-hurd.img.gz
复制代码
笔者使用的是qemu,所以用这个指令来运行:
  1. qemu-system-i386 -enable-kvm -m 2G -drive \
  2.     format=raw,cache=writeback,file=debian-hurd.img \
  3.     -nic user,hostfwd=tcp::2222-:22 -display curses -vga std
复制代码
启动完成后会看到
  1. Debian GNU/Hurd 12 debian tty3
  2. login:
复制代码
直接输入root然后Enter就能进入bash了。使用passwd给root加个密码。用exit退出去然后再用demo登录,然后再用passwd改个密码[7]。用vim或者个人喜欢的编辑器打开/etc/ssh/ssh_config把PasswordAuthentication yes那行的#去掉。接下来我们就可以直接再开一个新的terminal然后用ssh来在GNU Hurd上操作了:
  1. ssh -p 2222 demo@localhost
复制代码
然后准备工作就都完成了,接下来就可以开始研究练习了。
Mach IPC without MIG

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

我们就先写出两个线程吧。
  1. #include <threads.h>
  2. #include <stdio.h>
  3. int
  4. server (void *arg)
  5. {
  6.     printf ("I'm server\n");
  7. }
  8. int
  9. client (void *arg)
  10. {
  11.     printf ("I'm client\n");
  12. }
  13. int
  14. main (void)
  15. {
  16.     thrd_t server_thrd, client_thrd;
  17.     thrd_create (&server_thrd, server, NULL);
  18.     thrd_create (&client_thrd, client, NULL);
  19.     thrd_join (server_thrd, NULL);
  20.     thrd_join (client_thrd, NULL);
  21.     return 0;
  22. }
复制代码
编译时要用这条指令:
  1. 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:
  1. diff --git a/main.c b/main.c
  2. index f73724a..2eb9f38 100644
  3. --- a/main.c
  4. +++ b/main.c
  5. @@ -1,5 +1,8 @@
  6. #include <threads.h>
  7. #include <stdio.h>
  8. +#include <mach.h>
  9. +
  10. +static mach_port_t port;
  11. int
  12. server (void *arg)
  13. @@ -17,6 +20,7 @@ int
  14. main (void)
  15. {
  16.      thrd_t server_thrd, client_thrd;
  17. +    mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE, &port);
  18.      thrd_create (&server_thrd, server, NULL);
  19.      thrd_create (&client_thrd, client, NULL);
复制代码
这样子我们就获得了一个在自己的task中的port,而我们对这个port则拥有接收权利。读者可能会想问为什么要把client和server放在同一个task中而不是分开两个放。正如练习中描述的那样[8],放在同一个task中是为了简化实现,不然就会涉及到另一个关于central name server的课题了。
Message

练习描述中有提到信息的结构[8]:
  1. Inline messages have the following general structure:
  2. [ [mach_msg_header_t] [[mach_msg_type_t][data]] [[mach_msg_type_t][data]]... ]
  3. ^ ^                                      ^^                               ^             ^
  4. | |                                       ||                              |                \- Second set of arguments
  5. | |                                       ||                              \- First set of arguments
  6. | \- Message header        |\- Header for first set of arguments
  7. \- Message                         \- Pay load
  8. When data is marked out of line, the data section is detached.
复制代码
我们打算让Client发送:"Alice\0"给Server并且让Server回复"Hello Alice\0",我们可以创建一个这样的结构:
git diff
  1. diff --git a/main.c b/main.c
  2. index 2eb9f38..2307de1 100644
  3. --- a/main.c
  4. +++ b/main.c
  5. @@ -2,6 +2,13 @@
  6. #include <stdio.h>
  7. #include <mach.h>
  8. +struct message
  9. +{
  10. +    mach_msg_header_t msg_header;
  11. +    mach_msg_type_t first_header;
  12. +    char string[100];
  13. +};
  14. +
  15. static mach_port_t port;
  16. int
  17. @@ -13,6 +20,7 @@ server (void *arg)
  18. int
  19. client (void *arg)
  20. {
  21. +    struct message m;
  22.      printf ("I'm client\n");
  23. }
复制代码
Client

在Mach kernel interface中能看到mach_msg就将是我们用来发送和接收message的接口函数了。头文件声明则在这里:
  1. extern mach_msg_return_t
  2. mach_msg
  3.    (mach_msg_header_t *msg,
  4.     mach_msg_option_t option,
  5.     mach_msg_size_t send_size,
  6.     mach_msg_size_t rcv_size,
  7.     mach_port_name_t rcv_name,
  8.     mach_msg_timeout_t timeout,
  9.     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就会如下所示:
  1. 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:
  1. typedef struct mach_msg_header {
  2.     mach_msg_bits_t       msgh_bits;
  3.     mach_msg_size_t      msgh_size;
  4.     mach_port_t               msgh_remote_port;
  5.     mach_port_t               msgh_local_port;
  6.     mach_port_seqno_t  msgh_seqno;
  7.     mach_msg_id_t          msgh_id;
  8. } 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. ......
我们用不到所以不管。
因此结果会是如下:
  1. m.msg_header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0);
  2. m.msg_header.msgh_size = sizeof (struct message);
  3. m.msg_header.msgh_remote_port = port;
  4. m.msg_header.msgh_local_port = MACH_PORT_NULL;
  5. m.msg_header.msgh_seqno = 0;
复制代码
再接下来就是m.first_header,描述在Mach 3 Kernel Interface 330页。
  1. typedef struct {
  2.     unsigned int        msgt_name : 8,
  3.                                    msgt_size : 8,
  4.                                    msgt_number : 12,
  5.                                    msgt_inline : 1,
  6.                                    msgt_longform : 1,
  7.                                    msgt_deallocate : 1,
  8.                                    msgt_unused : 1;
  9. } 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。
所以结果如下:
  1. m.first_header.msgt_name = MACH_MSG_TYPE_STRING_C;
  2. m.first_header.msgt_size = 8;
  3. m.first_header.msgt_number = 100;
  4. m.first_header.msgt_inline = true;
  5. m.first_header.msgt_longform = false;
  6. m.first_header.msgt_unused = 0;
复制代码
最后再整合一下:
git diff
  1. diff --git a/main.c b/main.c
  2. index 2307de1..fcea63d 100644
  3. --- a/main.c
  4. +++ b/main.c
  5. @@ -1,6 +1,8 @@
  6. #include <threads.h>
  7. #include <stdio.h>
  8. #include <mach.h>
  9. +#include <string.h>
  10. +#include <stdbool.h>
  11. struct message
  12. {
  13. @@ -21,7 +23,28 @@ int
  14. client (void *arg)
  15. {
  16.      struct message m;
  17. -    printf ("I'm client\n");
  18. +    mach_msg_return_t err;
  19. +
  20. +    m.msg_header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0);
  21. +    m.msg_header.msgh_size = sizeof (struct message);
  22. +    m.msg_header.msgh_remote_port = port;
  23. +    m.msg_header.msgh_local_port = MACH_PORT_NULL;
  24. +    m.msg_header.msgh_seqno = 0;
  25. +
  26. +    m.first_header.msgt_name = MACH_MSG_TYPE_STRING_C;
  27. +    m.first_header.msgt_size = 8;
  28. +    m.first_header.msgt_number = 100;
  29. +    m.first_header.msgt_inline = true;
  30. +    m.first_header.msgt_longform = false;
  31. +    m.first_header.msgt_unused = 0;
  32. +
  33. +    strcpy (m.string, "Alice\0");
  34. +
  35. +    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);
  36. +
  37. +    printf ("Client error code: %d\n", err);
  38. +
  39. +    printf ("From Server: %s\n", m.string);
  40. }
  41. int
复制代码
笔者增加了一个err变量来检查mach_msg是否成功,现在编译后运行的话应该会看到如下的输出:
  1. I'm server
  2. Client error code: 0
  3. 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:
  1. diff --git a/main.c b/main.c
  2. index fcea63d..8200286 100644
  3. --- a/main.c
  4. +++ b/main.c
  5. @@ -16,7 +16,13 @@ static mach_port_t port;
  6. int
  7. server (void *arg)
  8. {
  9. -    printf ("I'm server\n");
  10. +    struct message m;
  11. +    mach_msg_return_t err;
  12. +
  13. +    err = mach_msg (&(m.msg_header), MACH_RCV_MSG, sizeof (struct message), sizeof (struct message), port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
  14. +
  15. +    printf ("Server receive error code: %d\n", err);
  16. +    printf ("Server: from client: %s\n", m.string);
  17. }
  18. int
复制代码
现在再编译运行的话我们就会看到:
  1. Server receive error code: 0
  2. Server: from client: Alice
  3. <卡在这>
复制代码
先庆祝一下!这意味着Server成功从Client接收了一个信息,而卡着说明Client正在等待着来自Server的回复。当然Client这辈子都等不到它的回复了,因为我们还没写。那就Ctrl-C结束进程然后把最后一步给写上去吧。
其实剩下的只要复制Client的发送方式就行啦:
git diff
  1. diff --git a/main.c b/main.c
  2. index 8200286..03ed4e2 100644
  3. --- a/main.c
  4. +++ b/main.c
  5. @@ -18,11 +18,32 @@ server (void *arg)
  6. {
  7.      struct message m;
  8.      mach_msg_return_t err;
  9. +    char s[100];
  10.      err = mach_msg (&(m.msg_header), MACH_RCV_MSG, sizeof (struct message), sizeof (struct message), port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
  11.      printf ("Server receive error code: %d\n", err);
  12.      printf ("Server: from client: %s\n", m.string);
  13. +
  14. +    m.msg_header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0);
  15. +    m.msg_header.msgh_size = sizeof (struct message);
  16. +    m.msg_header.msgh_remote_port = port;
  17. +    m.msg_header.msgh_local_port = MACH_PORT_NULL;
  18. +    m.msg_header.msgh_seqno = 0;
  19. +
  20. +    m.first_header.msgt_name = MACH_MSG_TYPE_STRING_C;
  21. +    m.first_header.msgt_size = 8;
  22. +    m.first_header.msgt_number = 100;
  23. +    m.first_header.msgt_inline = true;
  24. +    m.first_header.msgt_longform = false;
  25. +    m.first_header.msgt_unused = 0;
  26. +
  27. +    sprintf (s, "Hello %s\n\0", m.string);
  28. +    strcpy (m.string, s);
  29. +
  30. +    err = mach_msg (&(m.msg_header), MACH_SEND_MSG, sizeof (struct message), sizeof (struct message), port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
  31. +
  32. +    printf ("Server send error code: %d\n", err);
  33. }
  34. int
复制代码
最后编译运行后的输出:
  1. Server receive error code: 0
  2. Server: from client: Alice
  3. <卡在这>Server send error code: 0Server send error code: 0Client error code: 0From Server: Hello Alice
复制代码
Client成功接收到了Server的回复!成功了!笔者就是不太清楚为什么会出现两次Server send error code: 0。不过至少是成功了。
最后再放个完整的代码:
  1. #include <threads.h>
  2. #include <stdio.h>
  3. #include <mach.h>
  4. #include <string.h>
  5. #include <stdbool.h>
  6. struct message
  7. {
  8.     mach_msg_header_t msg_header;
  9.     mach_msg_type_t first_header;
  10.     char string[100];
  11. };
  12. static mach_port_t port;
  13. int
  14. server (void *arg)
  15. {
  16.     struct message m;
  17.     mach_msg_return_t err;
  18.     char s[100];
  19.     err = mach_msg (&(m.msg_header), MACH_RCV_MSG, sizeof (struct message), sizeof (struct message), port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
  20.     printf ("Server receive error code: %d\n", err);
  21.     printf ("Server: from client: %s\n", m.string);
  22.     m.msg_header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0);
  23.     m.msg_header.msgh_size = sizeof (struct message);
  24.     m.msg_header.msgh_remote_port = port;
  25.     m.msg_header.msgh_local_port = MACH_PORT_NULL;
  26.     m.msg_header.msgh_seqno = 0;
  27.     m.first_header.msgt_name = MACH_MSG_TYPE_STRING_C;
  28.     m.first_header.msgt_size = 8;
  29.     m.first_header.msgt_number = 100;
  30.     m.first_header.msgt_inline = true;
  31.     m.first_header.msgt_longform = false;
  32.     m.first_header.msgt_unused = 0;
  33.     sprintf (s, "Hello %s\n\0", m.string);
  34.     strcpy (m.string, s);
  35.     err = mach_msg (&(m.msg_header), MACH_SEND_MSG, sizeof (struct message), sizeof (struct message), port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
  36.     printf ("Server send error code: %d\n", err);
  37. }
  38. int
  39. client (void *arg)
  40. {
  41.     struct message m;
  42.     mach_msg_return_t err;
  43.     m.msg_header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0);
  44.     m.msg_header.msgh_size = sizeof (struct message);
  45.     m.msg_header.msgh_remote_port = port;
  46.     m.msg_header.msgh_local_port = MACH_PORT_NULL;
  47.     m.msg_header.msgh_seqno = 0;
  48.     m.first_header.msgt_name = MACH_MSG_TYPE_STRING_C;
  49.     m.first_header.msgt_size = 8;
  50.     m.first_header.msgt_number = 100;
  51.     m.first_header.msgt_inline = true;
  52.     m.first_header.msgt_longform = false;
  53.     m.first_header.msgt_unused = 0;
  54.     strcpy (m.string, "Alice\0");
  55.     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);
  56.     printf ("Client error code: %d\n", err);
  57.     printf ("From Server: %s\n", m.string);
  58. }
  59. int
  60. main (void)
  61. {
  62.     thrd_t server_thrd, client_thrd;
  63.     mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE, &port);
  64.     thrd_create (&server_thrd, server, NULL);
  65.     thrd_create (&client_thrd, client, NULL);
  66.     thrd_join (server_thrd, NULL);
  67.     thrd_join (client_thrd, NULL);
  68.     return 0;
  69. }
复制代码
总结

可以看到MIG还是帮开发者减少了很多烦恼的。在练习中也能更加清晰的感受到微内核的IPC是怎么一回事。在这里也感谢Hurd的开发者们和练习的作者Neal H Walfield的付出。有任何不正确的地方欢迎指出。
作者:chenw1
链接:https://www.cnblogs.com/chenw1/p/18768256
本文来自博客园,欢迎转载,但请注明原文链接,并保留此段声明,否则保留追究法律责任的权利。
All right reserved.

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册