一般情况下,Linux内核可执行文件采用/boot/vmlinuz或与之类似的路径名。早期的UNIX实现称其内核为UNIX。在后续实现了虚拟内存机制的UNIX系统中,其内核名称变更为vmunix。对Linux来说,文件名称中的系统名需要调整,而以“z”替换“linux”末尾的“x”,意在表明内核是经过压缩的可执行文件。
在文件系统内,会对文件类型进行标记,以表明其种类。其中一种用来表示普通数据文件,常称之为“普通文件”或“纯文本文件”。其他文件类型包括设备、管道、套接字、目录以及符号链接。
目录是一种特殊类型的文件,内容采用表格形式,数据项包括文件名以及对相应文件的引用。这一“文件名+引用”的组合被称为链接。每个文件都可以有多条链接,因而也可以有多个名称,在相同或不同的目录中出现。
进程的当前工作目录继承自其父进程。
每个文件都有一个与之相关的用户ID和组ID,分别定义文件的属主和属组。系统根据文件的所有权来判定用户对文件的访问权限。
对于目录而言,权限设置的意义与文件不同: 读权限允许列出目录内容(即该目录下的文件名),写权限允许对目录内容进行更改(比如,添加、修改或删除文件名),执行(有时也称为搜索)权限允许对目录中的文件进行访问(但需受文件自身访问的权限的约束) 。
进程的内存布局 - 逻辑上将一个进程划分为以下几部分(也称为段):
内核通过对父进程的复制来创建子进程。子进程从父进程处继承数据段、栈段以及堆段的副本后,可以修改这些内容,不会影响父进程的“原版”内容。(在内存中被标记为只读的程序文本段则由父、子进程共享。)
然后,子进程要么去执行与父进程共享代码段中的另一组不同函数,或者,更为常见的情况是使用系统调用execve()去加载并执行一个全新程序。execve()会销毁现有的文本段、数据段、栈段及堆段,并根据新程序的代码,创建新段来替换它们。
可使用以下两种方式之一来终止一个进程:其一,进程可使用 _exit()
系统调用(或相关的 exit()
库函数),请求退出;其二,向进程传递信号,将其“杀死”。无论以何种方式退出,进程都会生成“终止状态”,一个非负小整数,可供父进程的 wait()
系统调用检测。
进程的用户和组标识符(凭证)
每个进程都有一组与之相关的用户ID(UID)和组ID(GID):
sudo
的作用?)每个进程都会消耗诸如打开文件、内存以及CPU时间之类的资源。使用系统调用 setrlimit()
,进程可为自己消耗的各类资源限定一个上限。此类资源限制的每一项均有两个相关值:软限制(soft limit)限制了进程可以消耗的资源总量,硬限制(hard limit)限制软限制的调整上限。非特权进程在针对特定资源调整软限制值时,可将其设置为0到相应硬限制值之间的任意值,但硬限制值则只能调低,不能调高。
由 fork()
创建的新进程,会继承其父进程对资源限制的设置。
使用ulimit命令可调整shell的资源限制。shell为执行命令所创建的子进程会继承上述资源设置。
进程间通信(IPC)机制:
系统调用处理流程:
system_call()
例程(位于汇编文件 arch/i386/entry.S
中)来处理这次中断,具体如下:sys_call_table
)进行索引,发现并调用相应的系统调用服务例程。如果系统调用服务例程带有参数,那么将首先检查参数的有效性。随后,该服务例程会执行必要的任务,这可能涉及对特定参数中指定地址处的值进行修改,以及在用户内存和内核内存间传递数据。最后,该服务例程会将结果状态返回给 system_call()
例程。所有系统调用都是以原子操作方式执行的。之所以这么说,是指内核保证了某系统调用中的所有步骤会作为独立操作而一次性加以执行,其间不会为其他进程或线程所中断。
竞争状态(race conditions)或竞争冒险,是这样一种情形:操作共享资源的两个进程(或线程),其结果取决于一个无法预期的顺序,即这些进程/线程获得CPU使用权的先后相对顺序。
内核为文件操作维护着三个数据结构:
通常情况下,当增大已分配内存时,realloc()
会试图去合并在空闲列表中紧随其后且大小满足要求的内存块。若原内存块位于堆的顶部,那么 realloc()
将对堆空间进行扩展。如果这块内存位于堆的中部,且紧邻其后的空闲内存空间大小不足, realloc()
会分配一块新内存,并将原有数据复制到新内存块中。最后这种情况最为常见,还会占用大量CPU资源。一般情况下,应尽量避免调用 realloc()
。
实际用户ID和实际组ID确定了进程所属的用户和组。
在大多数UNIX实现中,当进程尝试执行各种操作(即系统调用)时,将结合有效用户ID、有效组ID,连同辅助组ID一起来确定授予进程的权限 。
有效用户ID为0(root的用户ID)的进程拥有超级用户的所有权限。这样的进程又称为特权级进程(privileged process)。而某些系统调用只能由特权级进程执行。
通常,有效用户ID及组ID与其相应的实际ID相等,但有两种方式能够致使二者不同。其一是使用9.7节中所讨论的系统调用,其二是执行set-user-ID和set-group-ID程序。
实际ID定义了进程所属。在大多数的UNIX实现中,进程对诸如文件之类资源的访问,其许可权限由有效ID决定。然而,Linux会使用文件系统ID来决定对文件的访问权限,而将有效ID用于检查其他权限。(因为文件系统ID一般等同于相应的有效ID,所以Linux对文件权限的检查方式与其他UNIX实现相同。)
SUSv3规定,调用ctime()
、gmtime()
、localTime()
或asctime()
中的任一函数,都可能会覆盖由其他函数返回,且经静态分配的数据结构。换言之,这些函数可以共享返回的字符数据和tm结构体。
系统的本地时区由时区文件/etc/localtime定义,通常链接到/usr/share/zoneinfo下的一个文件。
为运行中的程序指定一个时区,需要将TZ环境变量设置为由一冒号(:)和时区名称组成的字符串,其中时区名称定义于/usr/share/zoneinfo中。设置时区会自动影响到函数ctime()
、localtime()
、mktime()
和strftime()
。为了获取当前的时区设置,上述函数都会调用tzset(3)。函数tzset()
会首先检查环境变量TZ。如果尚未设置该变量,那么就采用/etc/localtime中定义的默认时区来初始化时区。如果TZ环境变量的值为空,或无法与时区文件名相匹配,那么就使用UTC。