第一章 UNIX基础知识
1.2 UNIX体系结构
可将操作系统定义为一种软件-内核(kernel)。 内核的接口称为系统调用(system call)。 公用函数库建立在系统调用接口之上,应用软件可以使用公用函数库,也可以使用系统调用。 shell是一种特殊的应用程序,它为运行其他应用程序提供了一个接口。
1.3 登录
1 登录名 系统口令文件通常是:/etc/passwd 文件。 口令文件中的登录项由7个冒号分割的字段组成,它们是: 登录名,加密口令,数值用户ID,数值组ID,注释字段,起始目录,shell程序。 如: sar:x:205:105:Stephen Rago:/home/sar:/bin/ksh 目前所有系统已将加密口令移到另一个文件中。 2 shell shell是一个命令行解释器,它读取用户输入,然后执行命令。 Linux默认shell是Bourne-again shell。
1.4 文件和目录
1 文件系统 UNIX文件系统是目录和文件组成的一种层次结构,目录的起点称为根root,其名字是一个字符/。 2 文件名 目录中的各个名字称为文件名。 创建新目录的同时自动创建俩个文件名:.(当前目录),..(父目录),根目录中点与点点相同。 3 路径名 一个或多个以斜线分割的文件名序列构成路径名。 4 工作目录 每个进程都有一个工作目录,也叫当前工作目录。 5 起始目录 登录时,工作目录设置为起始目录(home directory),即为口令文件中设置值。
[程序清单1-1]
- #include "stdio.h"
- #include "dirent.h"
- int
- main(int argc, char *argv[])
- {
- DIR *dp;
- struct dirent *dirp;
- if(argc != 2) {
- printf("no input a dirname!\n");
- exit(0);
- }
- if((dp = opendir(argv[1])) == NULL) {
- printf("can't open %s\n", argv[1]);
- exit(0);
- }
- while((dirp = readdir(dp)) != NULL) {
- printf("%s\n", dirp->d_name);
- }
- closedir(dp);
- exit(0);
- }
1.5 输入和输出
1 文件描述符 文件描述符通常是一个小的非负整数,内核用它标识一个特定进程正在访问的文件。读写文件时需要使用它。 2 标准输入,标准输出,标准出错 每当运行一个新程序时,所有的shell都为其打开三个文件描述符:标准输入,标准输出,标准出错。 3 不用缓冲的I/O 函数open,read,write,lseek,close提供了不用缓冲的I/O的输入输出。这些函数都使用文件描述符。 键入文件结束字符通常为:Ctrl+D。
[程序清单1-2]
- #include "unistd.h"
- #include "stdio.h"
- #define BUFFSIZE 4096
- int
- main(void)
- {
- int n;
- char buf[BUFFSIZE];
- while((n = read(STDIN_FILENO, buf, BUFFSIZE)) > 0) {
- if(write(STDOUT_FILENO, buf, n) != n) {
- printf("write error.\n");
- exit(0);
- }
- }
- if(n < 0) {
- printf("read error.\n");
- }
- exit(0);
- }
4 标准I/O 标准I/O函数提供一组对不用缓冲I/O函数的带缓冲的接口。
[程序清单1-3]
- #include "stdio.h"
- int
- main(void)
- {
- int c;
- while ((c = getc(stdin)) != EOF) {
- if(putc(c, stdout) == EOF) {
- printf("output error\n");
- exit(0);
- }
- }
- if (ferror(stdin)) {
- printf("input error\n");
- }
- exit(0);
- }
1.6 程序和进程
1 程序 程序是存放在磁盘上的一个可执行文件。 2 进程和进程ID 程序的执行实例被称为进程(process)。 UNIX确保每个进程都有一个唯一的非负整数标识符,称为进程ID。
[程序清单1-4]
- #include "stdio.h"
- #include "unistd.h"
- int
- main(void)
- {
- printf("hello world from process ID %d\n", getpid());
- exit(0);
- }
3 进程控制 三个进程控制的主要函数:fork,exec,waitpid。
[程序清单1-5] - #include
- #include
- #include "stdio.h"
- #define MAXLINE 1024
- int
- main(void)
- {
- char buf[MAXLINE];
- pid_t pid;
- int status;
- printf("%%");
- while (fgets(buf, MAXLINE, stdin) != NULL) {
- if (buf[strlen(buf) - 1] == '\n') {
- buf[strlen(buf) - 1] = 0;
- }
- if ((pid = fork()) < 0) {
- printf("fork error\n");
- }
- else if (pid == 0) {
- execlp(buf, buf, (char*)0);
- printf("couldn't execute: %s\n", buf);
- exit(127);
- }
- if ((pid = waitpid(pid, &status, 0)) < 0) {
- printf("waitpid error\n");
- }
- printf("%%");
- }
- exit(0);
- }
4 线程和线程ID 一个进程中可以包含多个线程。 每个线程也都有一个非负整数标识符,称为线程ID。
1.7 出错处理
UNIX函数出错时,常常返回一个负值,而且全局整型变量errno通常被设置为含有附加信息的一个值。 C标准定义了俩个函数来打印出错信息: #include
char *strerror(int errnum);
此函数返回errnum(通常就是errno)代表的出错信息字符串指针。 #include
void perror(const char *msg);
该函数首先输出msg指向的字符串,然后是一个冒号,一个空格,接着是对应与errno值的出错信息,然后是一个换行符。
[程序清单1-6] - #include "stdio.h"
- #include "string.h"
- #include
- int
- main(int argc, char *argv[])
- {
- fprintf(stderr, "EACCES: %s\n", strerror(EACCES));
- errno = ENOENT;
- perror(argv[0]);
- exit(0);
- }
1.8用户标识
1 用户ID 口令文件登录项中包括用户ID,用户ID为0的用户为根root或超级用户superuer。 2 组ID 口令文件登录项中包括用户的组ID,组被用于将若干用户分配到不同的项目组或部门中去,这种机制允许同组的各个成员共享资源。 组文件将组名映射为数字组ID,它通常是/etc/group.
[程序清单1-7] - #include "stdio.h"
- int
- main(void)
- {
- printf("uid = %d, gid = %d\n", getuid(), getgid());
- exit(0);
- }
3 附加组ID 除了口令文件中对一个用户指定一个组ID外,还允许一个用户属于额外的组,登录时,读文件/etc/group,寻找该用户作为其成员的16个记录项就可得到该用户的附加组ID。 大多数系统最少支持16个附加组。
1.9 信号
信号(signal)是通知进程已发生某种情况的一种技术。 进程处理信号的三种方式: 1 忽略该信号。 2 按系统默认方式处理。 3 提供一个函数,信号发生时则调用该函数。
[程序清单1-8] - #include
- #include
- #include "stdio.h"
- #define MAXLINE 1024
- static void sig_int(int);
- int
- main(void)
- {
- char buf[MAXLINE];
- pid_t pid;
- int status;
- if(signal(SIGINT, sig_int) == SIG_ERR) {
- printf("signal error\n");
- }
- printf("%%");
- while(fgets(buf, MAXLINE, stdin) != NULL) {
- if(buf[strlen(buf) - 1] == '\n')
- {
- buf[strlen(buf) - 1] = 0;
- }
- if((pid = fork()) < 0) {
- printf("fork error\n");
- }
- else if(pid == 0) {
- execlp(buf, buf, (char*)0);
- printf("couldn't execute: %s\n", buf);
- exit(127);
- }
- if((pid = waitpid(pid, &status, 0)) < 0) {
- printf("waitpid error\n");
- }
- printf("%%");
- }
- exit(0);
- }
- void
- sig_int(int signo)
- {
- printf("interrupt\n%%");
- }
1.10 时间值
长期以来,UNIX系统一直使用俩中不同的时间值: 1 日历时间 该值是自1970-01-01 00:00:00以来国际标准时间(UTC)所经过的秒数累计值。 系统基本数据类型time_t用于保持这种时间值。 2 进程时间 也被称为CPU时间,用以度量进程使用的中央处理器资源。进程时间以时钟滴答计算,历史上曾经取每秒50,60,100个滴答。 系统基本数据类型clock_t用于保存这种时间值。可以用sysconf函数得到每秒时钟滴答数。 当度量一个进程执行时间时,UNIX系统使用三个进程时间值: 时钟时间:进程运行的时间总量,其值与系统中同时运行的进程数有关。 用户CPU时间:执行用户指令所用的时间。 系统CPU时间:执行内核程序所经历的时间。
|