1.6 系统调用和库函数

所有的操作系统都提供服务访问点,程序可以通过它们请求内核中的服务。各种 Unix 都提供精心定义的有限个内核入口点,即系统调用。我们不能改变系统调用,除非我们有内核的源代码。Unix 第7版提供大约50个系统调用,4.4BSD 提供大约135个,而 SVR4 大约有120个。在《Unix程序员手册》第2节中有系统调用接口的文档。它是以 C 语言定义的,在任何给定的系统中无需考虑系统调用是如何被调用的。在各种 Unix 系统中,每个系统调用在标准 C 函数库中都有一个相同名字的函数。一个应用程序用标准 C 的调用序列来调用此函数。这个函数再调用相应的内核服务,所使用的技术依赖于所在系统。函数可能把一个或多个 C 参数放到通用寄存器中,并执行几条机器指令产生一个软件中断进入内核。对于我们来说,可以把系统调用看成 C 函数。

在《Unix程序员手册》的第3节中为程序员定义了一般用途的函数。虽然这些函数可能调用一个或多个内核系统调用但没有进入内核的入口点。如函数 printf 可能调用了系统调用 write 去执行输出,而函数 strcpy(复制一个串)和 atoi(将 ASCII 码转换成整数)完全不涉及操作系统。从实现者的角度来看,一个系统调用和库函数有着根本的区别。但在用户看来区别并不严重。在 4.4BSD 中我们运行图1-2中的程序。程序调用了三个函数:socket、sendto 和 recvfrom,每个函数最终调用了一个内核中同样名称的函数。在本书的后面我们可以看到这三个系统调用的 BSD 内核实现。如果我们在 SVR4 中运行这个程序,在那里,用户库中的插口函数调用“流”子系统,那么三个函数同内核的相互作用是完全不同的。在 SVR4 中对 socket 的调用最终调用内核 open 系统调用,操作文件 /dev/udp 并将流模块 sockmod 放置到结果流。调用 sendto 导致一个 putmsg 系统调用,而调用 recvfrom 导致一个 getmsg 系统调用。这些 SVR4 的细节在本书中并不重要,我们仅仅想指出的是:实现可能不同但都提供相同的 API 给应用程序。从一个版本到下一个版本的实现技术可能会改变。在 Net/1 中,send 和 sendto 是分别用内核系统调用实现的。但在 Net/3 中,send 是一个调用系统调用 sendto 的库函数:


send(int s, char *msg, int len, int flags) {

    return(sendto(s, msg, len, flags, (struct sockaddr *) NULL, 0));

}

用库函数实现 send 的好处是仅调用 sendto,减少了系统调用的个数和内核代码的长度。缺点是由于多调用了一个函数,增加了进程调用 send 的开销。因为本书是说明 TCP/IP 的伯克利实现的,大多数进程调用的函数(socket、bind、connect 等)是直接由内核系统调用来实现。

想了解更多关于 C 语言库函数和 Unix 常用系统调用的信息,可以参考这篇文章。如果你对 BSD Unix 工具箱感兴趣,推荐阅读这本书。还有更多关于 Unix 系统调用的详细说明,请查看这个文档

1.7 网络实现概述

Net/3 通过同时对多种通信协议的支持来提供通用的底层基础服务。的确,4.4BSD 支持四种不同的通信协议族:

1) TCP/IP(互联网协议族),本书的主题。

2) XNS(Xerox 网络系统),一个与 TCP/IP 相似的协议族;在80年代中期它被广泛应用于连6计计。

想深入研究 Unix 的 C 语言库函数和网络实现,请看这里。更多关于 4.4BSD 操作系统设计与实现的内容,可以访问这个链接。如果你需要 Unix 系统网络编程的教材,点击这里查看详细信息。