《Linux系统编程(第2版)》——第1章 入门和基本概念 1.1 系统编程

  1. 云栖社区>
  2. 博客>
  3. 正文

《Linux系统编程(第2版)》——第1章 入门和基本概念 1.1 系统编程

异步社区 2017-05-02 11:07:00 浏览1038
展开阅读全文

本节书摘来自异步社区《Linux系统编程(第2版)》一书中的第1章,第1.1节,作者:【美】Robert Love著,更多章节内容可以访问云栖社区“异步社区”公众号查看

第1章 入门和基本概念

摆在你面前的是一本关于系统编程的书,你将在本书中学习到编写系统软件的相关技术和技巧。系统软件运行在系统的底层,与内核和系统核心库进行交互。常见的系统软件包括Shell、文本编辑器、编译器、调试器、核心工具(GNU Core Utilities)以及系统守护进程。此外,网络服务、Web服务和数据库也属于系统软件的范畴。这些程序都是基于内核和C库实现的,可以称为“纯”系统软件。相对地,其他软件(如高级GUI应用),很少和底层直接交互。有些程序员一直在编写系统软件,而有些程序员则只投入了很少一部分时间。不管怎样,深入理解系统编程都能让他受益匪浅,不管你是把系统编程作为制胜法宝,还是认为它仅仅作为高层次概念的基础,系统编程是编写所有软件的灵魂。

确切地说,这是一本关于Linux的系统编程的书。Linux是类UNIX的现代操作系统,由Linus Torvalds和全球松散的程序员社区从零开始实现。尽管Linux和UNIX有着共同的目标和理念,但Linux并不是UNIX。Linux遵循自己的原则,关注方方面面的需求,专注于实用功能的开发。总的来说,Linux系统编程的核心和任何其他UNIX系统并没有区别。然而,除了这些基本点,和传统的UNIX系统相比,Linux有其自身的特点Linux支持更多的系统调用,有不同的行为和新的特性。

1.1 系统编程

从传统角度而言,所有的UNIX编程都属于系统级编程的范畴。这是由于UNIX系统并没有提供很多高级抽象,甚至是在如X Windows这样的系统上开发应用,也会涉及大量的UNIX的核心API。因此,可以说本书是通用的Linux编程指南。然而,本书并不涉及Linux编程环境——比如,书中没有任何关于如何使用make的说明。本书涵盖的是现代Linux机器上所使用的系统编程API。

系统编程和应用编程存在一些区别,但也有很多共性。系统编程最突出的特点在于要求系统程序员必须对其工作的硬件和操作系统有深入全面的了解。系统程序主要是与内核和系统库打交道,而应用程序还需要与更高层次的库进行交互,这些库把硬件和操作系统的细节抽象封装起来。这种抽象有以下几种目的:一是增强系统的可移植性,二是便于实现不同系统版本间的兼容,三是可以构建更易于使用、功能更强大或二者兼而有之的高级工具箱。对于一个应用,使用多少系统库和高级库取决于应用的运行层次。即使是开发那些基本不用系统库的应用,也能够通过了解系统编程而受益匪浅。对底层系统的深入理解和良好实践,对任何形式的编程都大有裨益。

1.1.1 为什么要学习系统编程
最近10年,不管是Web开发(如JavaScript)还是托管代码(如Java),应用编程的趋势都是逐渐远离系统级编程向高级开发发展。然而,这种开发趋势并非意味着系统编程的终结。实际上,依然需要有人来开发JavaScript解释器和Java虚拟机,这本身就是系统编程。此外,Python、Ruby或Scala程序员还是可以从系统编程中受益的,因为深入了解计算机灵魂的程序员在任何层次都能够编写出更好的代码。

虽然应用编程的趋势是逐渐远离系统级编程,绝大部分的UNIX和Linux代码还是属于系统级编程范畴,其中大部分是用C和C++实现的,主要是C库和内核的接口。另外,传统的系统编程——如Apache、bash、cp、Emacs、init、gcc、gdb、glibc、ls、mv、vim和X,也都不会很快过时。

系统编程通常包含内核开发,至少包括设备驱动编程。但是,和多数系统编程的书籍一样,本书并不讨论内核开发,而是专注于用户空间的系统级编程——即内核之上的所有内容(尽管了解内核对于理解本书大有裨益)。设备驱动编程是个很宽泛博大的主题,已经有很多书籍对此做了专门而又深入的探讨。

什么是系统级应用接口?在Linux上如何编写系统级应用?内核和C库到底提供了什么?如何优化代码?Linux上编程有什么技巧?和其他的UNIX版本相比,Linux提供了哪些精巧的系统调用?这些系统调用是如何工作的?本书将对这些问题一一进行探讨。

1.1.2 系统编程的基础
Linux系统编程有3大基石:系统调用、C库和C编译器,每个都值得深入探讨。

1.1.3 系统调用
系统编程始于系统调用,也终于系统调用。系统调用(通常简称为syscall)是为了从操作系统请求一些服务或资源,是从用户空间如文本编辑器、游戏等向内核(系统的核心)发起的函数调用。系统调用范围很广,从大家都熟悉的如read()和write(),到罕见的如get_thread_area()和set_tid_address()都在其范畴之内。

Linux实现的系统调用远远少于其他内核。举例来说,微软的Windows,其系统调用号称有几千个,而Linux x86-64体系结构的系统调用大概只有300个。在Linux内核中,每种体系结构(Alpha、x86-64或PowerPC)各自实现了标准系统调用。因此,不同体系结构支持的系统调用可能存在区别。然而,超过90%的系统调用在所有的体系结构上都实现了。本书所探讨的正是这部分共有的内容。

调用系统调用
位于用户空间的应用程序无法直接访问内核空间。从安全和可靠性角度考虑,也需要禁止用户空间的应用程序直接执行内核代码或操纵内核数据。但从另外一个角度看,内核也必须提供这样一种机制,当用户空间的应用希望执行系统调用时,可以通过该机制通知内核。有了这种机制,应用程序就可以“深入”内核,执行内核允许的代码。这种机制在不同的体系结构上又各不相同。举个例子,在i386微处理器上,用户空间的应用需要执行参数值为0x80的软件中断指令int。该指令会把当前运行环境从用户空间切换成内核空间,即内核的保护区域,内核在该区域执行中断处理函数——中断0x80的处理函数是什么呢?只能是系统调用处理函数!

应用程序通过寄存器告诉内核调用哪个系统调用以及传递什么参数。系统调用以数值表示,从0开始。举个例子,在i386微处理器体系结构上,要请求系统调用5(即open()),用户空间在发送int指令前,需要把5写到寄存器eax中。

参数传递也以类似的方式处理。还是以i386为例,为每个可能的参数指定一个寄存器——寄存器ebx、ecx、edx、esi和edi顺序存储前5个参数。对于极少数参数超过5个的系统调用,则使用单个寄存器指向保存所有参数的用户空间缓存。当然,大部分系统调用只包含几个参数。

虽然基本思想是一致的,但不同体系结构处理系统调用的方式不同。作为一名系统程序员,通常不需要了解内核是如何处理系统调用的。系统调用已经集成到各种体系结构的标准调用规范中,并通过编译器和C库自动处理。

1.1.4 C库
C库(libc)是UNIX应用程序的核心。即使你是使用其他语言编程,通常还是会通过高级语言封装的C库来提供核心服务,以方便系统调用。在现代Linux系统中,C库由GNU libc提供,简称glibc,发音是[gee-lib-see],或者有时发作[glib-see]。

GNU C库的功能远远超出了其名字的范畴。glibc中,除了标准C库,还提供了系统调用封装、线程支持和基本应用工具。

1.1.5 C编译器
在Linux中,标准C编译器是由GNU编译器工具集(GNU Compiler Collection,gcc)提供的。最初,gcc是GNU版的C编译器cc,因此,gcc表示GNU C编译器(GNU C Compiler)。随着时间推移,gcc支持越来越多的语言。时至今日,gcc已经成了GNU编译器家族的代名词。此外,gcc还表示C编译器二进制程序。除非特别指明,本书中提到gcc时,都是指gcc应用程序。

因为编译器辅助实现了标准C(参阅1.3.2小节)和系统ABI(参阅1.2.1小节和1.2.2小节),在UNIX系统(包括Linux)中所使用的编译器和系统编程紧密相关。

C++

本章把C语言作为系统编程的通用语言,但是C++语言也功不可没。

今天,C++在系统编程中的地位仅次于C语言。由于历史原因,比起C++,Linux开发人员更倾向于使用C语言:核心库、守护进程、工具箱以及Linux内核都是用C语言实现的。在非Linux环境中,C++语言作为“C语言的升级”,其优势是显而易见的,但是在Linux环境中,C++的地位还是逊于C。

尽管如此,本书给出的大部分相关的C语言代码都可以替换成C++。C++确实可以作为C语言的替代,适合任何系统编程工作:C++代码可以链接C代码,调用Linux系统调用,还可以充分利用glibc。

比起C,C++还为系统编程奠定了另外两块基石:标准的C++库和GNU C++编译器。标准的C++库实现了C++系统接口以及ISO C++11标准,由libstdc++库提供(有时写作libstdcxx)。GNU C++编译器是Linux系统为C++提供的标准编译器,由二进制程序g++提供。

网友评论

登录后评论
0/500
评论
异步社区
+ 关注