应老大要求,从今天开始APUE的读书笔记。其实就是一些比较需要注意的点
拿到一台Linux主机,最新开始的是登陆。
Linux 登陆的验证是靠 /etc/passwd 这个文件的。
$ grep kiosk /etc/passwd
kiosk:x:1000:1000:kiosk:/home/kiosk:/bin/bash
这些子段分别是用户名,加密后的密码,用户id,用户组id,描述,家目录,使用的shell。
整个登陆过程如下:
read /etc/inittab get username in tty get password
init ---------------------> mingetty ---------------------------> login ------------------/etc/pam.d-----------> call PAM modules -------------> act/rej
Linux PAM是一个通用的认证机制,是以库的模式。
文件系统:
目录(directory)是一个包含目录项的文件,在逻辑上,可以认为每个目录项都包含一个文件名,同时还包含说明该文件属性的信息。文件属性是:文件类型,文件长度,文件所有者,文件的许可权(例如,其他用户能否能访问该文件),文件最后的修改时间等,可以使用stat和fstat去查看详细信息。
使用stat命令去查看文件的详细信息。
$ stat google-chrome.desktop
File: ‘google-chrome.desktop’
Size: 8392 Blocks: 24 IO Block: 4096 regular file
Device: fd02h/64770d Inode: 134751621 Links: 1
Access: (0755/-rwxr-xr-x) Uid: ( 0/ root) Gid: ( 0/ root)
Context: unconfined_u:object_r:user_home_t:s0
Access: 2018-03-19 15:06:39.280035865 +0800
Modify: 2018-03-08 17:50:18.060889472 +0800
Change: 2018-03-08 17:50:26.282852535 +0800
Birth: -
解释一下:
- File:显示文件名
- Size:显示文件大小
- Blocks:文件使用的数据块总数
- IO Block:IO块大小
- regular file:文件类型(常规文件)
- Device:设备编号
- Inode:Inode号
- Links:链接数
- Access:文件的权限
- Gid、Uid:文件所有权的Gid和Uid。
首先清楚文字描述符的概念。
文字描述符是一个小的非负整数,内核用以标识一个特定进程正在存访的文件。当内核打开一个现存文件或创建一个新文件时,它就返回一个文件描述符。当读、写文件时,就可使用它。
按惯例,每当运行一个新程序时,所有的 s h e l l都为其打开三个文件描述符:标准输入、标准输出以及标准出错。如果像简单命令 l s那样没有做什么特殊处理,则这三个描述符都连向终端。
文件描述符的本质是 数组元素的下标。
右侧的表称为i节点表,在整个系统中只有1张。该表可以视为结构体数组,该数组的一个元素对应于一个物理文件。
中间的表称为文件表,在整个系统中只有1张。该表可以视为结构体数组,一个结构体中有很多字段,其中有3个字段比较重要:
左侧的表称为文件描述符表,每个进程有且仅有1张。该表可以视为指针数组,数组的元素指向文件表的一个元素。最重要的是:数组元素的下标就是大名鼎鼎的文件描述符。
open系统调用执行的过程:新建一个i节点表元素,打开的物理文件(如果对应于该物理文件的i节点元素已经建立,就不做任何操作);新建一个文件表的元素,根据open的第2个参数设置file status flags字段,将current file offset字段置0,将v-node ptr指向刚建立的i节点表元素;在文件描述符表中,寻找1个尚未使用的元素,在该元素中填入一个指针值,让其指向刚建立的文件表元素。最重要的是:将该元素的下标作为open的返回值返回。
不带缓存的IO和标准IO
不带缓存的IO是指 函数open、read、write、lseek以及close提供了不用缓存的 I / O。这些函数都用文件描述符进行工作。 如read和write函数都有一个参数--- ‘buf’。这个参数用来指向读取或者写入的地方。通常我们都是自己新建一个数组,用来读取或者写入,这就是不带缓存的IO,需要自己考虑读取内容的存放位置(或者写入内容的存放位置),同时还得考虑读取或者写入的长度。
与之相对的就是标准IO。最常见的是printf()。
有三个用于进程控制的主要函数: fork、exec和waitpid(exec函数有六种变体,但经常把它们统称为exec函数)。
首先看下面的代码
#include
#include
#include "ourhdr.h"
#include "myerror.h"
int main(void){
char buf[MAXLINE];
pid_t pid;
int status;
printf("%% ");
while(fgets(buf,MAXLINE,stdin) != NULL) {
buf[strlen(buf) - 1] = 0; /*replace newline with null*/
if ((pid=fork()) < 0)
err_sys("fork error");
else if (pid == 0) { /*child*/
execlp(buf,buf,(char *) 0);
err_ret("couldn't execute: %s",buf);
exit(127);
}
if ((pid = waitpid(pid,&status,0)) < 0) /*parent*/
err_sys("waitpid error");
printf("%% ");
}
exit(0);
}
调用fork创建一个新进程。新进程是调用进程的复制品,故称调用进程为父进程,新创建的进程为子进程。 fork对父进程返回新子进程的非负进程ID,对子进程则返回0。因为fork创建一新进程,所以说它被调用一次 (由父进程),但返回两次(在父进程中和在子进程中 )。
在子进程中,调用 execlp以执行从标准输入读入的命令。这就用新的程序文件替换了子进程。fork和跟随其后的exec的组合是某些操作系统所称的产生一个新进程。
子进程调用 execlp执行新程序文件,而父进程希望等待子进程终止,这一要求由调用waitpid实现,其参数指定要等待的进程 (在这里, pid参数是子进程 I D )。waitpid函数也返回子进程的终止状态( status变量)。在此简单程序中,没有使用该值。如果需要,可以用此值精确地确定子进程是如何终止的。
$ ./a.out
% date
2018年 03月 19日 星期一 17:24:24 CST
% pwd
/home/kiosk/C/apue
%
UNIX中有三个时间值。
时钟时间,用户cpu时间,系统cpu时间。
时钟时间(墙上时钟时间wall clock time):从进程从开始运行到结束,时钟走过的时间,这其中包含了进程在阻塞和等待状态的时间。
用户CPU时间:就是用户的进程获得了CPU资源以后,在用户态执行的时间。
系统CPU时间:用户进程获得了CPU资源以后,在内核态的执行时间。
进程的三种状态为阻塞、就绪、运行。
时钟时间 = 阻塞时间 + 就绪时间 +运行时间
用户CPU时间 = 运行状态下用户空间的时间
系统CPU时间 = 运行状态下系统空间的时间。
$ time grep "_POSIX_SOURCE" /usr/lib64/*.h &> /dev/null
real 0m0.004s
user 0m0.003s
sys 0m0.001s