惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

www.infosecurity-magazine.com
www.infosecurity-magazine.com
月光博客
月光博客
IT之家
IT之家
博客园 - Franky
大猫的无限游戏
大猫的无限游戏
博客园 - 【当耐特】
Recorded Future
Recorded Future
罗磊的独立博客
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
T
Tailwind CSS Blog
Apple Machine Learning Research
Apple Machine Learning Research
博客园 - 聂微东
Last Week in AI
Last Week in AI
S
Schneier on Security
Google DeepMind News
Google DeepMind News
C
CERT Recently Published Vulnerability Notes
NISL@THU
NISL@THU
AWS News Blog
AWS News Blog
D
Docker
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
酷 壳 – CoolShell
酷 壳 – CoolShell
博客园 - 叶小钗
Spread Privacy
Spread Privacy
The GitHub Blog
The GitHub Blog
MongoDB | Blog
MongoDB | Blog
P
Palo Alto Networks Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
V
V2EX
T
Tenable Blog
A
Arctic Wolf
C
Cisco Blogs
S
SegmentFault 最新的问题
T
The Exploit Database - CXSecurity.com
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
Martin Fowler
Martin Fowler
G
GRAHAM CLULEY
The Register - Security
The Register - Security
Project Zero
Project Zero
H
Hackread – Cybersecurity News, Data Breaches, AI and More
Latest news
Latest news
J
Java Code Geeks
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
T
Threatpost
L
LangChain Blog
D
Darknet – Hacking Tools, Hacker News & Cyber Security
腾讯CDC
I
Intezer
Schneier on Security
Schneier on Security
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org

Posts on WKLKEN THINKING

apisix 中的 lrucache apisix 中的服务发现机制 apisix 中的负载均衡 apisix etcd机制 聊聊框架 关于 k8s 的 zero downtime deployment 一些建议 apisix 遇到的一些问题 关于在除夕前一天换了一个洗衣机的故事 Django DRF 性能优化 DRF 的一些实践 Part1: Serializer DRF继承关系图 Better Code: 关于接口的灵活性 新的仓库: wklken/naming 缓存使用的一些经验 Better Code: 抽象: 可扩展性与可维护性的抉择 Better Code: 异常时, 该提示用户哪些信息? Better Code: 更好的异常日志打印 Go: some libs Go: go-redis/cache升级的坑 Go: logrus性能提升 Go: gin validation 远程办公的一点总结 Go: 开发过程中的一些bug 项目管理实践: 风险驱动开发 Go: 一种error wrap调用链处理方式 漫谈技术选型 Go: 基于 apitest 做handler层单元测试 Go: go-sql-driver interpolateparams参数优化 [分享]深度工作 你需要更多的思考时间 Django项目重构小结 工作七年小结: 学习,生活及其他 [分享]bash日常: bash-utils 极客时间推广海报 2017总结: 予时光以意义 k8s APIServer源码: api注册详细细节 k8s APIServer源码: api注册主体流程 k8s APIServer源码: 服务启动 k8s APIServer源码: go-restful框架 重构 - 读书笔记(Python示例) 写给新人的沟通建议 vim 杂谈 - 关于快速编辑 vim 杂谈 - 关于移动 读书笔记-重构: 章11 处理概括关系 读书笔记-重构: 章10 简化函数调用 读书笔记-重构: 章9 简化表达式 读书笔记-重构: 章8 重新组织数据 读书笔记-重构: 章7 在对象之间搬移特性 读书笔记-重构: 章6 重新组织函数 Python 代码规范小结 [分享]关于vim ElasticSearch集群部署文档 Logstash+ElasticSearch处理mysql慢查询日志 [分享]关于代码调试DE那些事 Logstash+ElasticSearch+Kibana- 实现相对通用的数据收集分析 ELK维护的一些点(二) [分享]Python源码剖析-数据结构 一些Centos Python生产环境的部署命令 摘录<<6个月学会任何一种外语>> ELK 维护的一些点 也许是一个新的开始 一些vim的个性化配置 读书笔记-调试九法 这段时间的一些想法 Python 源码阅读 - 垃圾回收机制 我为什么要写博客 Python源码阅读-闭包的实现 Python源码阅读-内存管理机制(二) Python源码阅读-内存管理机制(一) Python-基础-数据结构小结 '活动'设计的一些trick 一些简单的Python测试题 我的tmux配置及说明【k-tmux】 Review and Restart 工作四周年小结 vim插件: surround & repeat[成对符号编辑] vim插件: gundo[时光机] vim插件: expand-region[区域选中] vim插件: quickrun[快速执行] vim插件: trailing-whitespace[行尾空格处理] vim插件: closetag[成对标签补全] vim插件: ctrlp[文件搜索] vim插件: airline[状态栏增强] vim插件: theme[主题] vim插件: tagbar[大纲式导航] vim插件: nerdcommenter[快速注释] vim插件: rainbow_parentheses[括号高亮] vim插件: syntastic[语法检查] vim插件: delimitmate[符号自动补全] vim插件: matchit[成对标签跳转] vim插件: easy-align[快速对齐] vim插件: multiple-cursors[多光标操作] vim插件: vim-signature[快速标记跳转] vim插件: easymotion[快速跳转] vim插件: vundle[管理插件] Elasticsearch几个问题的解决 分享一份 Vim 简介PPT k-vim 更新9.0版本 关于知识管理工具的思考 Logstash+ElasticSearch+Kibana处理nginx访问日志
APUE笔记-第一章 UNIX基础知识
2015-09-23 · via Posts on WKLKEN THINKING

最近在找工作, 额, 从七月份一直休息到九月初, 开始找, 结果发现快到十月了, 节点不是很好, 要过两个节, 所以估计入职什么的要到节后了>_<#

这几个月也在思考一些东西, 顺手也解决掉了Python源码剖析, 目前在逐步梳理笔记

发现自己首次做笔记还是太杂太乱, 堆在wiki里面一大坨的感觉, 还是要梳理画图, 思路更清晰些, 静候吧, 还是十篇左右的样子

读APUE, 做法比较残忍, 把书切开, 拆成一章一章地装订, 方便携带和阅读, 最后发现画满了一堆东西

发现还是不方便自己查阅, 所以还是决定重读, 转成笔记, 放到博客上方便搜索/查阅

对了, 代码之前只是下了看到的时候run下, 这次重读写写注释, 放到github了, 链接

在此感谢作者W.Richard Stevens :) 这本书五星好评, 建议如果搞linux相关后端, 可以读下


Unix体系结构: 内核与系统调用

内核(kernel): 严格意义上, 将操作系统定义为一种软件, 它控制计算机硬件资源, 提供程序运行环境(相对较小, 位于环境的中心)

系统调用(system call): 内核的接口

关系: 公用函数库构建在系统调用接口之上, 应用软件既可以使用公用函数库, 也可以使用系统调用

登陆

口令文件/etc/passwd, 七个字段,冒号分隔

登录名:加密口令:数值用户 ID: 数值组 ID: 注释字段:起始目录:shell

加密口令已经转移到另一个文件

shell: 一个命令行解释器, 它读取用户输入, 然后执行命令

Linux 默认shell是Bourne-again shell

文件和目录

文件系统: 目录和文件组成的一种层次接口, 目录的起点称为根(root), 其名字是\\

目录(directory)是一个包含许多目录项的文件

文件名(filename): 不能出现斜线/和空操作符null(好的习惯只使用印刷字符的一个子集作为文件名字符)

创建目录时, 会自动创建两个文件名, 当前目录. 以及父目录..

路径名(pathname): 一个或多个以斜线分割的文件名序列. 以斜线开头的是绝对路径(absolute pathname), 否则是相对路径(relative pathname)

ls.c源代码

#include "apue.h"
#include <dirent.h>

// 可编译执行
// apue.h, 包含某些标准头文件, 定义了很多常量及库函数

int
main(int argc, char *argv[])
{
    DIR                *dp;
    // 结构体
    struct dirent    *dirp;

    // 需要至少一个参数
    if (argc != 2)
        // apue.h自定义函数
        err_quit("usage: ls directory_name");

    // 赋值后判断, opendir返回指向 DIR 结构体的指针
    if ((dp = opendir(argv[1])) == NULL)
        // apue.h自定义函数err_sys
        err_sys("can't open %s", argv[1]);

    // 赋值后判断, 读每一项, 返回指向readdir结构的指针或null(没有目录项可读时)
    while ((dirp = readdir(dp)) != NULL)
        // 取出每个目录的名字
        printf("%s\n", dirp->d_name);

    closedir(dp);

    // 终止程序, 0正常结束, 1-255出错
    exit(0);
}

工作目录(working directory), 每个进程都有一个, 优势成为当前工作目录, 进程可以通过chdir函数更改其工作目录.

输入和输出

文件描述符(file descriptor), 一个小的负整数, 内核使用它标识一个特定进程正在访问的文件. 当内核打开或创建一个新文件时, 返回一个文件描述符, 在读写的时候使用

每当运行一个新程序时, 所有的shell都为其打开三个文件描述符: 标准输入(standard input), 标准输出(standard output)以及标准错误(standard error)

不用缓冲的I/O 函数open/read/write/lseek/close

mycat.c源码

#include "apue.h"

/* 缓冲区大小, 常量 */
#define    BUFFSIZE    4096

int
main(void)
{
    int     n;
    char    buf[BUFFSIZE];

    /* STDIN_FILENO/STDOUT_FILENO -> apue.h -> unisted.h, 标准输入文件描述符0/标准输出文件描述符1 */

    /* 从标准输入读, read返回读得的字节数, 读到末端返回0, 发生错误返回-1 */
    while ((n = 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 函数的带缓冲接口, 可以无需担心如何选取最佳的缓冲区大小

getputc.c源码

#include "apue.h"


/* stdin/stdout -> apue.h -> stdio.h 标准输入文件/标准输出文件 */
/* EOF为stdio.h中定义的常量 */

int
main(void)
{
    int c;

    /* 从标准输入中读入一个字符 */
    while ((c = getc(stdin)) != EOF)
        /* 输出到标准输出 */
        if (putc(c, stdout) == EOF)
            err_sys("output error");

    if (ferror(stdin))
        err_sys("input error");

    exit(0);
}

程序和进程

程序(program): 存放在磁盘上, 处于某个目录中的一个可执行文件.(使用6个exec函数中的一个有内核将程序读入存储器, 并使其执行)

进程(process): 程序的执行实例

进程ID(process ID), 每个进程都有一个唯一的数字标识符, 总是一非负整数

hello.c 源码

#include "apue.h"

int
main(void)
{
    /* getpid得到进程pid */
    printf("hello world from process ID %ld\n", (long)getpid());
    exit(0);
}

进程控制: 三个主要函数, fork/exec(六种变体)/waitpid

shell1.c 源码

#include "apue.h"
#include <sys/wait.h>


/* fork创建一个新进程, 它被调用一次(由父进程调用), 返回两次(在父进程中返回子进程的进程ID, 在子进程中返回0) */

int
main(void)
{
    char    buf[MAXLINE];    /* from apue.h */
    pid_t    pid;
    int        status;

    printf("%% ");    /* print prompt (printf requires %% to print %) */

    /* 读入一行, 每一行命令会产生一个子进程用于执行 */
    while (fgets(buf, MAXLINE, stdin) != NULL) {
        /* 去掉换行符 */
        if (buf[strlen(buf) - 1] == '\n')
            buf[strlen(buf) - 1] = 0; /* replace newline with null */

        /* 执行读入的命令 */
        /* fork创建一个子进程, 返回<0则表示fork发生了错误 */
        if ((pid = fork()) < 0) {
            err_sys("fork error");

        /* 对于子进程, fork返回的pid=0(父进程fork返回的pid>0) */
        } else if (pid == 0) {        /* child */

            /* 调用execlp以执行从标准输入读入的命令 */
            /* fork+exec组合, 是某些操作系统所称的产生(spawn)一个新的进程 */
            execlp(buf, buf, (char *)0);
            err_ret("couldn't execute: %s", buf);
            /* 退出 */
            exit(127);
        }

        /* 父进程, 等待子进程终止 */
        /* pid为子进程id, status为子进程终止状态(用于判断其实如何终止的) */
        /* parent */
        if ((pid = waitpid(pid, &status, 0)) < 0)
            err_sys("waitpid error");
        printf("%% ");
    }
    exit(0);

通常, 一个进程只有一个控制线程(thread), 同一时刻只执行一组机器指令.(对于某些问题, 如果不同部分各使用一个控制线程, 那么整个问题解决相对容易, 多个控制线程也能充分利用多处理器系统的并行性)

在一个进程内的所有线程共享同一地址空间/文件描述符/栈以及与进程相关的属性(所以各线程在访问共享数据时需要采取同步措施以避免不一致性)

线程也用 ID 标识, 但只在其所属进程内起作用

出错处理

UNIX 函数出错的时候, 常常返回一个负值, 而整型变量errno通常被设置为含有附加信息的一个值

errno.h中, 定义了符号errno以及可以赋予它的各种常量.(errno(3)手册中)

对于errno两条规则

1. 如果没有出错, 其值则不会被一个例程清楚, 因此, 仅当函数的返回值指明出错时, 才检验其值
2. 任一函数都不会将errno值设置为0, 在errnoh中定义的所有常量都不为0

testerror.c 源码

#include "apue.h"
#include <errno.h>


/* strerror, 将errnum映射为一个出错信息字符串, 并且返回此字符串的指针 */
/* perror, 基于errno的当前值, 在标准出错上产生一条出错信息, 然后返回 */

int
main(int argc, char *argv[])
{
    /* 常量 EACCES / ENOENT */
    fprintf(stderr, "EACCES: %s\n", strerror(EACCES));
    errno = ENOENT;
    perror(argv[0]);
    exit(0);
}

出错恢复: errno.h中定义的各种出错分为致命性和非致命性两类.

致命性出错: 无法执行恢复动作, 最多只能在屏幕上打印一条出错信息, 或写入日志, 然后终止

非致命性出错: 可以较为妥善地处理

用户标识

用户ID(user ID), 数值, 系统中标识各个不同的用户, 每个用户唯一(用户不能更改其用户 ID)

用户 ID 为0的用户为根( root) 或超级用户(superuser)

组ID(group ID), 一个数值, 指定用户登陆名时同时指定的. 允许同组各个成员之间共享资源

组文件将组名映射为数字 ID, /etc/group

口令文件包含: 登录名 = 用户 ID 的映射

组文件包含: 组名 = 组ID 的映射

打印用户ID和组ID

uidgid.c 源码

#include "apue.h"

int
main(void)
{
    /* getuid / getgid */
    printf("uid = %d, gid = %d\n", getuid(), getgid());
    exit(0);
}

附加组ID

允许一个用户属于多个组, 最多16个.

信号

信号(signal): 通知进程已发生某种情况的一种技术.

e.g. 一个进程执行除法操作, 其除数为0, 则将名为SIGFPE的信号发给该进程

进程如何处理信号?

1. 忽略该信号
2. 按系统默认方式处理.
3. 提供一个函数, 信号发生时则调用该函数(捕捉信号)

信号捕捉

shell2.c 源码

#include "apue.h"
#include <sys/wait.h>

/* 声明信号处理函数 */
static void    sig_int(int);        /* our signal-catching function */

int
main(void)
{
    char    buf[MAXLINE];    /* from apue.h */
    pid_t    pid;
    int        status;


    /* signal函数, 指定SIGINT 到处理函数 sig_int */
    /* 机制, 类似于直接注册到了进程, 观察是否异常发生后捕获处理 */
    if (signal(SIGINT, sig_int) == SIG_ERR)
        err_sys("signal error");

    printf("%% ");    /* print prompt (printf requires %% to print %) */
    while (fgets(buf, MAXLINE, stdin) != NULL) {
        if (buf[strlen(buf) - 1] == '\n')
            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);
        }

        /* parent */
        if ((pid = waitpid(pid, &status, 0)) < 0)
            err_sys("waitpid error");
        printf("%% ");
    }
    exit(0);
}


/* 处理函数, 打印 */
void
sig_int(int signo)
{
    printf("interrupt\n%% ");
}

时间值

UNIX系统两种不同的时间值

日历时间, time_t, 从1970年1月1日00:00:00以来的国际标准时间UTC锁经过的秒数

进程时间, clock_t, CPU时间, 度量进程使用的中央处理器资源, 以始终滴答计算

Unix系统使用三个进程时间

1. 时钟时间, 总时间, real
2. 用户cpu时间, 执行用户指令耗时, user
3. 系统cpu时间, 执行内核程序耗时, sys

系统调用和库函数

所有操作系统都提供多种服务的入口点(系统调用), 程序由此想内核请求服务

UNIX所使用的技术是为每个系统调用在标准 C 库中设置一个具有同样名字的函数. 用户进程用标准 C 调用序列来调用这些函数, 然后函数又用系统所要求的技术调用相应的内核服务

系统调用通常提供一种最小接口,而库函数通常提供比较复杂的功能。

系统调用: 最小接口, 单一职责, 不可替换

C库函数: 复杂功能, 可替换, 可自行定义