所有操作系统都为它们所运行的程序提供服务。典型的服务包括:执行新程序、打开文件、读文件、分配存储区以及获得当前时间等。
- UNIX体系结构
(1)从严格意义上说,可将操作系统定义为一种软件它控制计算机硬件资源,提供程序运行环境。我们通常将这种软件称为内核(kernel),因为它相对较小,而且位于环境的核心。
(2)内核的接口被称为系统调用(system call)。
(3)shell是一个特殊的应用程序,为运行其他应用程序提供了一个接口:
1)shell是一个命令解释器,它读取用户输入,然后执行。
2)shell的用户输入通常来自于终端(交互式shell),有时来自于文件(称为shell脚本)。 - 文件和目录
(1)文件系统
UNIX文件系统是目录和文件的一种层次结构,所有东西的起点是称为根(root)的目录,这个目录的名称是一个字符“/”。
(2)文件名
1)目录终端各个名字称为文件名。只有斜线(/)和空字符这两个字符不能出现在文件名中。
2)创建新目录时会自动创建两个文件名:.(点)和..(点点)。分别指向当前目录和父目录。
(3)路径名
由斜线分隔的一个或多个文件名组成的序列(也可以斜线开头)构成路径名(pathname)。
(4)实例
1234567891011121314151617181920//myls.c 实现ls(l)命令的简单实现#include "apue.h"#include <dirent.h>int main(int argc, char *argv[] ){DIR *dp;struct dirent *dirp;if ( argc != 2)err_quit(" usage: ls directory_name");\if (( dp = opendir(argv[1])) == NULL)err_sys("can not open %s, argv[1]");while ((dirp = readdir(dp)) != NULL)printf("%s\n", dirp->d_name);closedir(dp);exit(0);} - 输入和输出
(1)文件描述符
文件描述符(file description)通常是一个小的非负整数,内核用以标识一个特定进行正在访问的文件。当内核打开一个现有文件或创建一个新文件时,它都返回一个文件描述符。在读、写文件时,可以使用这个文件描述符。(2)标准输入、标准输出和标准错误
1)按惯例,每当运行一个新程序时,所有的shell都为其打开3个文件描述符:标准输入、标准输出、标准错误。
2)大多数shell都提供一种方法,使其中任何一个或所有这三个描述符都能重新定向到某个文件,例如:
ls > file.list #执行ls,其标准输出重新定向到名为file.list的文件。(3)不带缓冲的I/O
函数open, read, write, lseek以及close提供了不带缓冲的I/O。这些函数都使用文件描述符。
(4)标准I/O
1)标准I/O函数为那些不带缓冲的I/O函数提供了一个带缓冲的接口。使用标准I/O函数无需担心如何选取最佳的缓冲区大小。
2)标准I/O函数还简化了对输入行的处理。例如,fgets函数读取一个完整的行,而read函数读取指定字节数。
实例:用标准I/O将标准输入复制到标准输出
123456789101112131415#include "apue.h"int main(void){int c;while ((c = getc(stdin)) != EOF) #读到输入的最后一个字节时,getc返回常量EOFif (putc(c, stdout) == EOF)err_sys("output error");if (ferror(stdin))err_sys("input error");exit(0);}
其中,getc函数一次读取一个字符,然后函数putc将此字符写到标准输出。 - 程序和进程
(1)程序
程序(program)是一个存储在磁盘上某个目录中的可执行文件。内核使用exec函数(7个exec函数之一),将程序读入内存,并执行程序。
(2)进程和进程ID
程序的执行实例被称为进程(process)。UNIX系统确保每个进程都有一个唯一的数字标识符,称为进程ID(process ID),进程ID总是一个非负整数。
实例:打印进程ID
1234567#include "apue.h"int main(void){printf("hello world from process ID %ld\n", (long)getpid());exit(0);}
其中,getpid函数得到进程ID,返回一个pid_t数据类型。(3)进程控制
有3个用于进程控制的主要函数:fork, exec, waitpid。(exec函数有7种变体,但经常把它们统称为exec函数)(4)线程和线程ID
1)通常,一个进程只有一个控制线程(thread)——某一时刻执行的一组机器指令。对于某些问题,如果有多个控制线程分别作用于它的不同部分,那么解决起来就容易的多。另外,多个控制线程也可以充分利用多处理器系统的并行能力。
2)一个进程内的所有线程共享同一地址空间、文件描述符、栈以及与进程相关的属性。
3)与进程相同,线程也用ID标识。但是,线程ID只在它所属的进程内起作用。
4)控制线程的函数与控制进程的函数类似,但另有一套。 - 出错处理
(1)当UNIX系统函数出错时,通常会返回一个负值,而且整型变量errno通常被设置为具有特定信息的值。
(2)文件<errno.h>中定义了errno以及可以赋予它的各种常量,这些常量都以字符E开头。 - 信号
信号(signal)用于通知进程发生了某种情况。例如,当某一进程执行除法操作,其除数为0,则将名为SIGFPR(浮点异常)的信号发送给该进程。进程有以下3种处理信号的方式:
(1)忽略信号
(2)按系统默认方式处理。对于除数为0,系统默认方式是终止该进程。
(3)提供一个函数,信号发生时调用该函数,这种称为捕捉该信号。通过提供自编的函数,我们就能知道什么时候产生了信号,并按期望的方式处理它。很多情况下都会产生信号,终端键盘上有两种产生信号的方法,分别称为中断键(interrupt key,通常是Delete键或Ctrl+C)和退出键(quit key, 通常是Ctrl+\)。
另一种产生信号的方法是调用kill函数。在一个进程中调用此函数就可向另一个进程发送一个信号。当然这样做也有些限制:当向一个进程发送信号时,我们必须是那个进程的所有者或者是超级用户。