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

推荐订阅源

Cyberwarzone
Cyberwarzone
V
Vulnerabilities – Threatpost
T
Tenable Blog
Forbes - Security
Forbes - Security
Simon Willison's Weblog
Simon Willison's Weblog
AWS News Blog
AWS News Blog
G
GRAHAM CLULEY
Know Your Adversary
Know Your Adversary
S
Securelist
C
Cybersecurity and Infrastructure Security Agency CISA
Project Zero
Project Zero
C
CXSECURITY Database RSS Feed - CXSecurity.com
V
Visual Studio Blog
WordPress大学
WordPress大学
Latest news
Latest news
K
Kaspersky official blog
T
Tailwind CSS Blog
T
Threat Research - Cisco Blogs
B
Blog RSS Feed
C
Cisco Blogs
博客园 - 聂微东
Martin Fowler
Martin Fowler
T
The Blog of Author Tim Ferriss
小众软件
小众软件
L
LangChain Blog
阮一峰的网络日志
阮一峰的网络日志
L
LINUX DO - 热门话题
Stack Overflow Blog
Stack Overflow Blog
罗磊的独立博客
P
Proofpoint News Feed
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
P
Privacy International News Feed
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
C
CERT Recently Published Vulnerability Notes
Cisco Talos Blog
Cisco Talos Blog
S
SegmentFault 最新的问题
Security Latest
Security Latest
Y
Y Combinator Blog
爱范儿
爱范儿
aimingoo的专栏
aimingoo的专栏
P
Privacy & Cybersecurity Law Blog
L
LINUX DO - 最新话题
月光博客
月光博客
The GitHub Blog
The GitHub Blog
博客园 - 三生石上(FineUI控件)
S
Security Affairs
P
Proofpoint News Feed
D
DataBreaches.Net
有赞技术团队
有赞技术团队
云风的 BLOG
云风的 BLOG

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库函数: 复杂功能, 可替换, 可自行定义