操作系统:一种软件,控制计算机硬件资源,提供程序运行环境。操作系统包含了内核和一些其他软件(如shell、公用函数库、应用程序等)。例如Linux就是GNU操作系统的内核,因此也称为GNU/Linux操作系统。
内核的接口称为系统调用,公用函数库构建在系统调用之上。应用程序既可以使用共用函数库,也可以使用系统调用。shell是一个特殊的应用程序,为运行其他应用程序提供了一个接口。
2. 登录UNIX操作系统用户在登录UNIX时,要输入登录名与密码(口令)。UNIX在口令文件/etc/passwd中查看登录名。
口令文件中的每一个登录项的组成:
$ cat /etc/passwd > root:x:0:0:root:/root:/bin/bash > # 分别是 登录名:加密口令:数字用户ID:数字组ID:注释字段:起始目录:shell程序位置3. 文件和目录
根目录:所有东西的起点是根(root)目录,该目录的名称是一个字符/
目录:目录是一个包含目录项的文件。每个目录项包含了文件名等一些属性信息。创建新目录时都会创建两个文件(两个目录项):.和..,点指向当前目录,点点指向父目录。
$ ls -la > total 16 > drwxr-xr-x 4 root root 4096 Oct 19 21:36 . > drwxr-xr-x 6 root root 4096 Oct 18 11:39 .. > drwxr-xr-x 2 root root 4096 Oct 19 21:36 section_1 > drwxr-xr-x 2 root root 4096 Oct 19 21:36 section_3
路径名:
以/开头的路径名为绝对路径,以根目录为起始。其他的路径名为相对路径,以当前目录作为起始。
工作目录:
每个进程都有一个工作目录,有时称为当前工作目录。所有相对路径都从工作目录开始解释,进程可用chdir系统调用更改其工作目录,通过getcwd系统调用获取当前工作目录。
注意:假如在目录A中运行了目录B中的程序,那么进程B的工作目录是目录A
起始目录:
登录时,工作目录设置为起始目录,一登录就位于的目录即为起始目录,该目录从口令文件中对应登录项中获得。
4. 输入和输出文件描述符:
通常是一个非负整数,内核用它以标识一个特定进程正在访问的文件。当内核打开一个现有文件或创建一个新文件时,都返回一个文件描述符
标准输入 标准输出 标准错误:
每当运行一个新程序时,所有的shell都为其打开3个文件描述符,即标准输入、标准输出和标准错误。这三个描述符都链接至终端。
在头文件#include
#define STDIN_FILENO 0 #define STDOUT_FILENO 1 #define STDERR_FILENO 2
ls > file.list # 将标准输出重定向到文件file.list
不带缓冲的I/O:
函数open、read、write、lseek、close等系统调用提供了不带缓冲的I/O。
ssize_t read(int fd, void *buf, size_t count);
例如上面的read系统调用函数声明,由于不带缓冲区,因此不同的buf大小(count字节数)就会影响程序的效率。
**文件结束符:**Ctrl+D,可以用来结束终端输入
带缓冲的I/O:
标准I/O库函数是带缓冲的函数,无需担心如何选取缓冲区的大小。
5. 程序和进程 5.1 程序程序是一个存储在磁盘上某个目录的可执行文件。内核使用exec函数将程序读入内存并执行程序
5.2 进程和进程ID程序的一次执行实例称为进程。UNIX系统为每个进程都有一个唯一的数字标识符,即进程ID(PID),一个非负整数。在进程中可以通过getpid()系统调用获取自身PID。
5.3 线程和线程ID一个进程内的所有线程共享同一地址空间、文件描述符、栈以及与进程相关的属性。
线程也用ID标识,但是线程ID只在它所属的进程内起作用。一个进程中的线程ID在另一个进程中没有意义。
6. 出错处理当UNIX系统调用出错时,通常会返回负值(有些返回的是空指针等),并且变量errno被设置为具有特定信息的值。
errno以前的定义是:
extern int errno;
但是在支持线程的环境中,多个线程共享进程地址空间,每个线程都有属于它自己的局部errno,以避免一个线程干扰另一个线程。因此errno定义为
extern int *__errno_location (void); # define errno (*__errno_location ())
对于errno需要注意两点:
- 如果没有出错,errno值不会被例程清除。因此仅当函数返回值出错时,再检验errno值
- 任何函数都不会将errno设置为0
C标准提供两个函数以打印出错信息
-
perror函数:
#include
void perror (const char *msg); 先在标准错误上输出错误信息msg,然后是一个冒号一个空格,接着是errno对应的错误信息,然后换行符
-
strerror函数:
#include
char *strerror (int errnum); 将errnum(通常就是errno)对应的出错信息字符串返回。
在口令文件的登录项中用户ID是一个数值,用来向系统标识不同的用户。
用户ID为0的用户为根用户(root)或超级用户(superuser),可以在口令文件中看到一个登录项的用户名是root。
root:x:0:0:root:/root:/bin/bash
这种用户的特权为超级用户特权,如果一个进程有超级用户特权,那么大多数文件权限检查都不再进行。
7.2 组ID口令文件的登录项也包含组ID,也是一个数值。在口令文件中,有多个登录项拥有相同的组ID。
组ID用于将若干用户分配到不同的项目和部门中去,允许同组的各个成员之间共享资源(如文件)。比如可以设置文件权限为组内所有用户可以访问,组外用户不能访问。
组文件:将组名映射为数值的组ID,通常是/etc/group
$ cat /etc/group > root:x:0: > daemon:x:1:
对于磁盘上的每个文件,文件系统通常存储该文件所有者的用户ID和组ID,而不是用户名和组名。这是因为存储这两个ID只需要4个字节(假定每个ID以双字节整形值存放),而如果使用完整用户名和组名,需要更多的字节(需要更多磁盘空间)。另外在检验权限时,字符串比较与整数比较相比更慢。
但对于用户而言使用用户名和组名更方便,因此使用口令文件包含用户名和用户ID的映射,组文件包含组名和组ID的映射。比如ls -l命令就使用口令文件将用户ID映射为用户名从而打印出文件所有者的用户名。
进程可以使用getuid()和getgid()获取用户ID与组ID。
7.3 附属组ID大多数UNIX系统还允许一个用户属于另外一些组。允许一个用户属于最多16个其他的组。
可以通过etc/group中列有指定用户作为其成员的前16个记录项即为该用户的附属组。
adm:x:4:syslog # ID为4的组是syslog用户的附属组
/etc/group文件的每个记录项字段意义:
组名:加密口令:组ID:组中的附加用户
最后一个字段,组中的附加用户:如果该用户组是这个用户的初始组,则该用户不会写入这个字段
8. 信号用于通知进程发生了某种情况。如执行除0操作,则将SIGFPE(浮点异常)信号发送给该进程
进程有三种处理信号方式:
- 忽略信号(不推荐)
- 按系统默认方式执行,比如对于除0操作,系统默认方式是终止进程
- 提供信号处理函数,信号发生时调用该函数,称之为捕捉该信号。
UNIX使用两种不同的时间值
9.1 日历时间自1970年1月1日00:00:00起到某时刻的秒数,通过time_t类型保存这种时间值。
9.2 进程时间用于表示进程使用CPU时间资源。通过clock_t类型保存这种时间值
UNIX系统为每个系统维护了三个进程时间值
-
时钟时间:
进程运行的总时间。时钟时间 = 阻塞时间 + 就绪时间 + 运行时间。
-
用户CPU时间:
进程获得了CPU资源以后,在用户态执行的时间。
-
系统CPU时间:
进程获得了CPU资源以后,在内核态的执行时间
CPU时间 = 用户CPU时间 + 系统CPU时间
10. 系统调用和库函数为了程序能向内核请求服务,UNIX提供了一些能够直接进入内核的入口点,称为系统调用。
通用库函数可能会调用一个或多个系统调用,但他们不是内核入口点。通用库函数也可能不调用系统调用。



