操做系统的任务是为其上运行的程序服务:包括运行、打开文件、读文件、分配内存以及时间获取。shell
In a strict sense, an operating system can be defined as the software that controls the hardware resources of the computer and provides an environment under which programs can run.安全
由内而外:
- 内核 其接口被称为系统调用(System call)。
- shell 特殊的应用程序,为其余应用程序提供接口。
- 库函数 在系统调用接口基础上的函数库。
- 应用 就是应用。。架构
系统根据输入的用户名去/etc/passwd
目录下检索用户名并找到对应的密码文件进行密码匹配。
密码文件长这样:ide
sar:x:205:105:Stephen Rago:/home/sar:/bin/ksh
上述对应各字段值类型:
登陆名:加密的密码:用户ID:组ID:注释域:起始:shell程序函数
shell是一个命令解释器,与用户进行交互。ui
.
和父目录..
。栗子:ls简要实现加密
#include "apue.h" #include <dirent.h> int main (int argc, char *argv[]) { DIR *dp; struct dirent *dirp; // 输入参数不为2, 暗示使用方法不对 if (argc != 2) { err_quit("usage: ls directory_name"); } // 打开指定目录失败 if ((dp = opendir(argv[1])) == NULL) { err_sys("can't not open %s", argv[1]); } // 遍历dp文件夹 打印内部文件的文件名 while ((dirp = readdir(dp)) != NULL) { printf("%s\n", dirp->d_name); } // 关闭文件夹句柄 && 退出程序 closedir(dp); exit(0); }
这里可能遇到一个apue.3e的编译问题,解决方案戳这里。spa
命令行编译执行结果以下:操作系统
一般是一个小的非负整数,内核用它标识一个特定进程访问的文件。命令行
通常来讲,全部派系的shell都会为程序打开标准输入、标准输出、标准错误三个文件描述符。而且这三个文件描述符都能重定向到某个文件。
ls > file.list
非缓冲函数如:open, read, write, lseek, close
。这些函数都与文件描述符协做。
举个栗子:从标准输入读写入标准输出
#include <stdio.h> #include <apue.h> #define BUFFSIZE 4096 int main(void) { int n; char buf[BUFFSIZE]; // 头文件<unistd.h>中包含了`STDIN_FILENO`与`STDOUT_FILENO`两个常量 while ((n = (int)read(STDIN_FILENO, buf, BUFFSIZE)) > 0) { if (write(STDOUT_FILENO, buf, n) != n) { err_sys("write error"); } if (n < 0) { err_sys("read error"); } } exit(0); }
命令运行结果截图:
标准I/O函数提供一种对不用缓冲I/O函数的带缓冲接口。 无脑说就是不用设置缓冲区大小。好比fgets函数读完一行的长度而read函数须要制定读入的字节数。
咱们最熟悉得标准I/O是printf
。。。
举个栗子: 用标准I/O将标准输入复制到标准输出
#include <stdio.h> #include <apue.h> int main(void) { int c; // 头文件<unistd.h>中包含了`STDIN_FILENO`与`STDOUT_FILENO`两个常量 while ((c = getc(stdin)) != EOF) { if (putc(c, stdout) == EOF) { err_sys("output error"); } if (ferror(stdin)) { err_sys("input error"); } } exit(0); }
运行结果略。。
程序是放在磁盘上的。
程序执行的实例叫作进程。每一个进程都有本身的惟一标识符PID。
栗子:打印进程ID
int main(void) { printf("hello world from process ID %d\n", getpid()); exit(0); }
运行结果:
进程控制主要函数有:fork、exec、waitpid
举个栗子:
#include <stdio.h> #include <sys/wait.h> #include <apue.h> 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) { err_sys("fork error"); } else if (pid == 0) { execlp(buf, buf, (char *)0); err_ret("couldn't execute: %s", buf); exit(127); } if ((pid = waitpid(pid, &status, 0)) < 0) { err_sys("watipid error"); } printf("%% "); } exit(0); }
运行结果:
一般一个进程只有一个控制线程,同一时刻只能执行一组机器指令。
在同一个进程内得线程共享同一个地址空间、文件描述符、栈以及进程相关属性。
由于共享资源的关系,因此这里有个线程安全的概念。
与进程相同,线程也用ID标识。线程ID只在进程中起做用。出了进程就没有意义了。
UNIX函数出错的时候一般返回一个负值,用errno
表示错误得种类。某些函数不返回负值而是使用另一种约定,好比返回一个只想对象的指针的大多数函数,出错时候返回null指针。
使用errno的两条规则:
errno
值设置为0,在<errno.h>中定义的全部常量都不为0。标准C钟定义了下面两个方程来实现打印错误消息。
char *strerror(int errnum); void perror(const char *msg);
第一个函数代表根据errno找到错误信息字符串指针,第二个函数代表根据指针,打印出错误信息。
举个栗子:
#include <string.h> #include <errno.h> int main(int argc, char *argv[]) { fprintf(stderr, "EACCES: %s\n", strerror(EACCES)); errno = ENOENT; perror(argv[0]); exit(0); }
经过将./a.out传入perror
函数,这样能够在管道中知道是哪一个程序出错了。
在<errno.h>
的错误定义划分为两种:致命与非致命。致命错误没有恢复操做。而非致命错误相反,遇到此类错误时候能够更为妥善的处理。
资源相关的非致命错误包含EAGAIN, ENFILE, ENOBUFS, ENOLCK, ENOSPC, EWOULDBLOCK
,有些时候还有ENOMEM
。EBUSY
问题出在共享资源被占用的时候也能够视为非致命错误。
资源相关非致命错误的恢复策略通常是延迟重试。
适时的采用恢复策略能够增长应用的健壮性从而避免异常退出。
用户ID用来让系统区分不一样的用户。
用户ID为0得用户为超级用户。操做系统的不少权限只向超级用户提供。
组ID是系统管理员在指定用户名提供的。这种机制容许组内成员共享资源。
组文件将组名映射为数字组ID,它一般是/etc/group.
举个栗子:
int main(int argc, char *argv[]) { printf("uid = %d, gid = %d\n", getuid(), getgid()); exit(0); }
大多数UNIX版本还容许一个用户属于另一个用户组。
信号是通知进程已经发生某种状况的一种技术。举个栗子,一个进程在执行除法运算操做,除数为0,则将名为SIGFPE的信号发给该进程。
栗子:
为了让程序能捕捉到信号,须要让其调用signal
函数来指定对应信号应当执行的动做(函数)。下栗是在捕获信号时候进行打印输出:
// 声明信号handler static void sig_int(int); // 简单handler实体 - 打印 void sig_int(int signo) { printf("interrupt\n%% "); }
UNIX系统有两类时间值:
日历时间:广义时间,用time_t
修饰。
进程时间:CPU时间,用来度量进程使用的CPU资源,用clock_t
修饰。进程时间又分为:
用户CPU时间与系统CPU时间统称CPU时间。能够经过time
函数来获取时间值组成。
从实现者角度看,系统调用和库函数之间有重大区别,但从用户角度,区别并非很重要。
应用程序能够调用系统函数或者库函数,而不少库函数会调用系统调用。
系统调用一般提供最小接口,而库函数提供比较复杂的功能。
UNIX概述