Never too late to learn.

0%

Linux/UNIX系统编程手册-基本概念

Linux/UNIX系统编程手册

[德] Michael Kerrisk

第2章 基本概念
第3章 系统编程概念

基本概念

内核

内核的职责:(Tasks performed by the kernel)

  • 进程调度(Process scheduling)
  • 内存管理(Memory scheduling)
  • 提供了文件系统(Provision of a file system)
  • 创建和终止进程(Creation and termination of process)
  • 对设备的访问(Access to devices)
  • 联网(Networking)
  • 提供系统调用应用编程接口(Provision of a system call API)

内核态和用户态(Kernel mode and user mode)

现代处理器架构一般允许CPU至少在两种不同状态下运行,即:用户态和内核态(或supervisor mode)。执行硬件指令可使CPU在两种状态间来回切换。与之对应,可将虚拟内存区域划分为用户空间和内核空间。在用户态下运行时,CPU只能访问被标记为用户空间的内存;当运行于内核态时,CPU既可以访问用户空间内存,也能访问内核空间内存。

shell

  • Bourne again shell(bash): GUN项目对Bourne shell(sh)的重新实现。

单根目录层级、目录、链接及文件

每个目录至少包含两条记录:...,前者时指向目录本身的链接,后者是指向其上级目录的链接。

文件I/O模型

UNIX系统I/O模型最为显著的特性之一是其I/O通用性概念。也就是说,同一套系统调用(open(), read(), write(), close()等)所执行的I/O操作,可用于所有文件类型,包括设备。

One of the distinguishing features of the I/O model on UNIX systems is the concept
of universality of I/O. This means that the same system calls (open(), read(),
30 Chapter 2
write(), close(), and so on) are used to perform I/O on all types of files, including
devices.

I/O系统调用使用文件描述符(file descriptor)–数值很小的非负整数–来指代打开的文件。获取文件描述符的常用手法是调用open(),在参数中指定I/O操作目标文件的路径名。

进程(Process)

进程是正在执行的程序实例。执行程序时,内核会将程序代码载入虚拟内存,为程序变量分配空间,建立bookkeeping数据结构,以记录与进程有关的各种信息(进程ID,用户ID,组ID以及终止状态)。
内核为各个进程分配计算机资源,在其生命周期内对进程的资源做统筹和调整,进程结束时,释放资源,供其他进程使用。

进程的内存布局(Process memory layout)

  • 文本:程序的指令
  • 数据:程序使用的静态变量
  • 堆: 程序可从该区域动态分配额外内存
  • 栈:随函数调用、返回而增减的一片内存,用于为局部变量和函数调用链接信息分配存储空间

创建进程和执行进程

进程使用系统调用fork()来创建一个新进程。调用fork()的进程被称为父进程,新创建的进程被称为子进程。内核通过对父进程的复制来创建子进程。

子进程可以使用系统调用execve()去加载并执行一个全新程序。execve()会销毁现有的文本段、数据段、栈、堆,并根据新程序的代码,创建新segments来替换他们。

每一个进程都有一个唯一的整型进程标识符(PID), 以及一个父进程标识符(PPID).

可使用两种方式来终止一个进程:

  • 其一,进程可使用_exit()系统调用,请求退出;
  • 其二,向进程传递信号,将其“杀死”;
    无论以何种方式推出,进程都会生成“终止状态”,一个非负小整数,可供父进程的wait()系统调用检测。一般“终止状态”为0表示正常退出,非0表示有错误发生。

The init process

1
2
3
4
5
6
7
8
9
10
11
12
13
> $ man init
SYSTEMD(1)
NAME
systemd, init - systemd system and service manager

SYNOPSIS
systemd [OPTIONS...]

init [OPTIONS...] {COMMAND}

DESCRIPTION
systemd is a system and service manager for Linux operating systems. When run as first process on boot (as PID 1), it acts as init system that brings up and maintains userspace
services.

守护进程(Daemon processes)

Examples of daemon processes include syslogd, which records messages in the system
log, and httpd, which serves web pages via the Hypertext Transfer Protocol
(HTTP).

环境列表(Environment list)

每一个进程都有一份环境列表,即在进程用户空间内存中维护的一组环境变量。可使用exportprintenv命令来创建和查看当前进程的环境变量。

$ export [MYVAR=’Hello world’]

$ printenv
使用unset,命令撤销一个环境变量

进程间通讯及同步(Interprocess Communication and Synchronization)

Linux提供了丰富的进程间通讯(IPC)机制:

  • 信号(signal), 表示事件的发生;
  • 管道(pipes)和FIFO, 用于在进程间传递数据;
  • 套接字(sockets), 供同一主机或联网的不同主机所运行的进程之间传递数据;
  • 文件锁定(file locking), 为防止其他进程读取或更新文件内容,允许某进程对文件的部分区域加以锁定;
  • 消息队列(message queues), 用于进程间交换信息(数据报);
  • 信号量(semaphore), 用来同步进程动作;
  • 共享内存(shared memory), 允许两个或以上进程共享一块内存;

信号(Signals)

信号也常被称为“软件中断”。进程收到信号,就意味着某个事件或者异常情况发生。信号有很多类型,分别表示不同的事件和情况。每种信号类型通过不同的整数加上SIGxxx来标识自己。
内核、其他进程或者进程自身均可向进程发送信号。
内核向进程发送信号:

  • 用户键入中断字符(ctrl + c)
  • 进程的子进程之一终止
  • 进程设定的定时器通知
  • 进程尝试访问无效内存地址
    shell中,可使用kill命令向进程发送信号。在程序内部,系统调用kill()可提供相同的功能。
    信号从产生到送达进程期间,一直处于挂起(pending)状态。通常,系统会在接收进程下次获得调度时,将处于挂起状态的信号同时送达,如果进程正在运行,则立即送达。

线程(Threads)

每一个进程都可以执行多个线程。可将线程想象为共享同一虚拟内存及一些其他属性的进程。每个线程都会执行相同的程序代码,共享同一数据区域和堆。可是每个线程都有自己的栈,用来装载本地变量和函数调用链接信息。
线程之间可以通过共享的全局变量进行通讯。
多线程应用能从多处理器硬件的并行处理中获益匪浅。

进程组和shell任务控制(Process Group and Shell Job Control)

shell执行的每个程序都创建一个新进程。例如,shell创建了3个进程来执行一下管道命令

& ls -l | sort -k5n | less

主流的shell都提供了一种交互式特性,名为任务控制。shell会将管道内的所有进程置于一个新进程组或任务中,进程组都具有相同的进程组标识符(某个进程的进程ID)。

/proc文件系统

/proc文件系统时一种虚拟文件系统,以文件系统目录和文件形式,提供一个指向内核数据结构的接口。这位查看和改变各种系统属性开启了方便之门。此外,还能通过一组以/proc/PID形式命名的目录查看系统中运行各行程的相关信息。

系统编程概念

系统调用(system call)

系统调用允许进程向内和请求服务。与用户空间的函数调用相比,哪怕是最简单的系统调用都会产生显著的开销,其原因时为了执行系统调用,系统需要临时性的切换到内核态。此外,内核还需验证系统调用的参数、用户内存和内核内存之间也有数据需要传递。

在Linux上,一般情况下,使用glibc作为C语言标准库的实现。

Coffee? ☕