目录
- 1. 内核、内核态和用户态
- 2. 用户和组
- 3. 文件和文件系统
- 4. I/O模型
- 5. 程序、进程、线程和协程
- 6. shell、终端和会话
1. 内核、内核态和用户态
? 内核是指管理和分配计算机资源的核心层软件。其中计算机资源包括CPU、RAM(随机存取存储器)和设备。内核有以下职责:
- 进程调度
- 内存管理
- 提供文件系统
- 创建和终止进程
- 设备访问
- 联网
- 提供系统调用应用编程接口(API)
? 由于需要限制不同的程序之间的访问能力, 防止他们获取别的程序的内存数据, 或者获取外围设备的数据,并发送到网络, CPU划分出两个权限等级——用户态和内核态。内核态可以访问所有数据,包括所有程序的内存、联机的所有设备等。用户态只能访问当前程序的内存,也不能访问任何外围设备,如果要访问就需要调用内核提供的API。
2. 用户和组
? 系统的每个用户都拥有唯一的登录名,一个用户可以隶属于多个组。用户和组分别记录在/etc/passwd 和/etc/group文件中。为了保证用户的密码安全,经过加密的密码存在/etc/shadow中。
? etc/passwd文件可以使用getpwnam()和getpwuid()来读取。
/* 由:分隔,分别对应结构中对应的字段
* root:x:0:0:root:/root:/usr/bin/zsh
*/
struct passwd {
char *pw_name; //Login name
char *pw_passwd; //Encrypted password
uid_t pw_uid; //User Id
gid_t pw_gid; //Group Id
char *pw_gecos; //@Deprecated
char *pw_dir; //Initial working directory
char *pw_shell; //Login shell
};
#include <pwd.h>
struct passwd *getpwnam(const char *name);
struct passwd *getpwid(uid_t uid);
/* 扫描/etc/passwd文件 */
struct passwd *getpwent(void);
void setpwent(void); //设置指针到开头
void endpwent(void); //扫描完成关闭文件
? etc/group文件可以使用getpwnam()和getpwuid()来读取。
/* 由:分隔,分别对应结构中对应的字段
* adm:x:4:syslog
*/
struct group {
char *gr_name; //Group name
char *gr_passwd; //Encrypted password
gid_t gr_gid; //Group Id
char **gr_men; //Members list
};
#include <grp.h>
struct group *getgrnam(const char *name);
struct group *getgrgid(gid_t gid);
/* 扫描/etc/group文件 */
struct group *getgrent(void);
void setgrent(void); //设置指针到开头
void endgrent(void); //扫描完成关闭文件
? etc/shadow文件可以使用getspnam()来读取。
/* 由:分隔,分别对应结构中对应的字段
* root:*:17737:0:99999:7:::
*/
struct spwd {
char *sp_name; //Login name
char *sp_pwdp; //Encrypted password
...
};
#include <shadow.h>
struct spwd *getspnam(const char *name);
/* 扫描/etc/shadow文件 */
struct group *getspent(void);
void setspent(void); //设置指针到开头
void endspent(void); //扫描完成关闭文件
? 我们可以使用标准密码认证,我们可以对输入的密码进行加密与/etc/shadow中存储的sp_pwdp进行比较。加密算法封装于crypt()函数中。
#define _XOPEN_SOURCE
#include <unistd.h>
char *crypt(const char *key, const char *salt);
? 认证示例:
...
struct *wpd = getpwnam(username);
struct *spwd = getspnam(username);
if (spwd != NULL)
wpd->pw_passwd = spwd->sp_pwdp;
char *password = getpass("Password:");
char *encrypted = crypt(password, pwd->pw_passwd);
... //clear password and validity check
Boolean authOk = strcmp(encrypted, pwd->pw_passwd) == 0;
if (authOk)
//success
3. 文件和文件系统
? 在Linux下,一切皆文件。包括设备、管道、套接字、目录及符号链接等。文件都可以使用同一套系统调用执行I/O操作。内核维护着一套单根目录结构,系统的所有文件都放到根目录“/”下。
? 文件的系统是操作系统用于明确磁盘或分区上的文件的方法和数据结构;即在磁盘上组织文件的方法。磁盘驱动器是一种机械装置,由一个或多个高速旋转的盘片组成。通过在磁盘上快速移动的读写磁头,便可获取/修改磁盘表面的磁盘编码信息。磁盘表面信息物理上存储于磁道的一组同心圆上。磁道自身又被划分为若干扇区,每个扇区则包含一系列物理块。物理块的容量一般为512字节(或512的倍数),代表了驱动器可读写的最小信息单元。文件系统中用来分配空间的基本单位是逻辑块,即文件系统所在磁盘设备上若干连续的物理块。每块磁盘划分为一个或多个(不重叠)分区,每个分区视为/dev路径下的单独设备。分区可以容纳文件系统、数据区域或交换区域。文件系统由引导块、超级块、i-node表和数据块组成。
? 引导块总是文件系统的首块,是在计算机启动后初始自举,找到操作系统内核所在磁盘分区,并把内核加载到内存中。超级块记录文件系统有关的参数信息,包括i-node表容量,文件系统的逻辑块大小和文件系统的逻辑块数量。i-node表和数据块类似链表结构,i-node记录索引和指针,指针指向数据块地址。文件名存在数据块中。 在目录文件中,文件数据块中存入i-node索引,这个映射称为硬链接,在数据块中存入另一个数据块的文件名称为软链接。
? 文件属性可以使用stat()、lstat()和fstat()获取文件属性。
struct stat {
dev_t st_dev;
ino_t st_ino;
mode_t st_mode; //File type and permissions
nlink_t st_nlinke;
uid_t st_uid; //User ID of file owner
gid_t st_gid; //Group ID of file owner
dev_t st_rdev;
off_t st_size; //Total file size (bytes)
...
};
? st_mode字段内含有位掩码。可拆分为4部分,d rwx rwx rwx,分别表示:文件类型、属主权限(文件拥有者)、属组权限(与文件拥有者同用户组的其他用户)、其他用户组用户权限。
4. I/O模型
? UNIX I/O模型的显著特点之一就是其输入/输出的通用性概念。也就是说,同一套系统调用(open()、read()、write()、close()等)所执行的I/O操作,可施之于所有文件类型。所有执行I/O操作的系统调用都以文件描述符来指代打开的文件。其中0、1、2分别指代标准输入(stdin)、标准输出(stdout)、标准错误(stderr),这3个文件描述符始终是打开的。
- 打开一个文件:open()
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int flags, .../* mode_t mode */);
? flags参数是文件访问模式的位掩码
标志 | 用途 |
---|---|
O_RDONLY | 以只读方式打开 |
O_WRONLY | 以只写方式打开 |
O_RDWR | 以读写方式打开 |
O_CREAT | 若文件不存在则创建之 |
O_DIRECTORY | 若pathname不是目录,则失败 |
O_EXCL | 与O_CREAT一起,如果文件已存在就不打开文件,返回错误EEXIST |
O_LARGEFILE | 在32位系统中使用此标志打开大文件 |
O_APPEND | 总在文件尾部追加数据 |
O_TRUNC | 如果文件已经存在且为普通文件,那么清空文件内容,长度置为0 |
... ... |
- 创建一个文件:creat()
#include <fcntl.h> int creat(const char *pathname, mode_t mode);
- 数据写入文件:write()
#include <unistd.h> ssize_t write(int fd, void *buffer, size_t count);
- 关闭文件:write()
#include <unistd.h> int close(int fd);
- 改变文件偏移量
#include <unistd.h> off_t lseek(int fd, off_t offset, int whence);
? whence参数表明应参照哪个基点来解释offset参数
标志 | 用途 |
---|---|
SEEK_SET | 从文件头部起始点开始偏移 |
SEEK_CUR | 从当前文件偏移量开始偏移 |
SEEK_END | 从文件尾部开始偏移 |
- I/O控制方法:ioctl()
#include <sys/ioctl.h> int ioctl(int fd, int request, .../* argp */);
5. 程序、进程、线程和协程
? 计算机程序是一组计算机能识别和执行的指令。通常以两种面目示人:源码形式和二进制形式。源码程序指人类可以阅读的文本文件,二进制指源码经过编译和链接处理后形成的二进制机器码。进程是正在执行的程序实例。每个进程可执行多个线程,线程为共享同一虚拟内存及一干其他属性的进程。进程和线程都是由内核进行调度,利用CPU时间片的概念进行抢占式调度,协程是用户级概念,完全由用户态程序自己调度。也就是说,内核并不知道协程的存在。
? 进程在内存上分布几个段:
- 文本:程序的指令
- 数据:程序使用的静态变量
- 堆:程序可从该区域动态分配额外内存
- 栈:随函数调用、返回而增减的一片内存,用于局部变量和函数调用链接信息分配存储空间
? 所有用户进程都由父进程创建,都有一个进程ID和父进程ID,所有进程之父为内核创建的PID为1的init进程。init进程和守护进程随系统开启而运行,系统关闭而终止。
? 我们可以使用getpid()和getppid()来获取进程ID和父进程ID
#include <sys/ioctl.h>
pid_t getpid(void);
pid_t getppid(void);
? 内核为每个进程都提供了相应的目录,命名为/proc/PID,该路径下包含但不限于以下文件:
文件 | 描述 |
---|---|
cmdline | 以\0分隔的命令行参数 |
cwd | 指向当前工作目录的符号链接 |
Environ | 环境信息,以\0分隔 |
exe | 指向正在执行文件的符号链接 |
fd | 指向由进程打开的文件的符号链接 |
maps | 内存映射 |
men | 进程虚拟内存 |
root | 指向根目录的符号链接 |
status | 进程状态信息(进程ID、凭证、内存使用量、信号等) |
task | 为进程中的每个线程均包含一个子目录 |
6. shell、终端和会话
原文地址:https://www.cnblogs.com/edwardorchis/p/11020364.html