0%

what is Bayes' theorem#

贝叶斯推断(Bayesian inference)是一种统计学方法,用来估计统计量的某种性质。

它是 贝叶斯定理(Bayes' theorem的应用。英国数学家托马斯·贝叶斯(Thomas Bayes)在1763年发表的一篇论文中,首先提出了这个定理。

Bayesian inference 与其他统计学推断方法截然不同。它建立在主观判断的基础上,也就是说,你可以不需要客观证据,先估计一个值,然后根据实际结果不断修正。正是因为它的主观性太强,曾经遭到许多统计学家的诟病。

贝叶斯推断需要 大量的计算,因此历史上很长一段时间,无法得到广泛应用。只有计算机诞生以后,它才获得真正的重视。人们发现,许多统计量是无法事先进行客观判断的,而互联网时代出现的大型数据集,再加上高速运算能力,为验证这些统计量提供了方便,也为应用贝叶斯推断创造了条件,它的威力正在日益显现。

贝叶斯定理#

贝叶斯定理, 实际上就是计算 条件概率的公式。

所谓"条件概率"(Conditional probability),就是指在事件B发生的情况下,事件A发生的概率,用\(P(A|B)\)来表示。

根据文氏图,可以很清楚地看到 \(P(A|B)\) 就是 \(P(A \cap B)\)除以\(P(B)\)\[P(A|B) = \frac {P(A \cap B)} {P(B)}\] 即: \[P(A \cap B) = P(A|B) P(B)\] 同理: \[P(A \cap B) = P(B|A) P(A)\] 因此: \[P(A|B) = \frac {P(B|A) P(A)} {P(B)}\]

我们把 \(P(A)\)称为先验概率(Prior probability),即在B事件发生之前,我们对A事件概率的一个判断\(P(A|B)\)称为后验概率(Posterior probability),即在B事件发生之后,对A事件概率的重新评估\(\frac{P(B|A)}{P(B)}\) 称为似然函数Likelyhood),这是一个调整因子,使得预估概率更接近真实概率

所以,条件概率可以理解成下面的式子:

1
后验概率 = 先验概率 x 调整因子
我们先预估一个先验概率,然后加入实验结果,看这个实验到底是增强还是削弱了"先验概率"(取决于Likelyhood),由此得到更接近事实的 "后验概率"。

when to use Bayes' theorem#

我们先看看简单的例子: - 事实 Evidence: Robin个性安静,喜欢阅读。

需要猜测 Robin是图书管理员 还是 农民。大多数人也许会认为Robin是图书管理员。 - 假设 hypothesis: Robin是图书管理员

接下里我们验证一下这个概率,其中目前数据显示: - 人群中: 图书管理员/农民 = \(\frac{1}{20}\) - 图书管理员符合Evidence的概率为 \(\frac{4}{10}\) - 农民符合Evidence的概率为 \(\frac{1}{20}\)

我们需要求出: P(H|E) - P(H|E) --> P(hypothesis given evidence):后验概率,指Evidence条件下,hypothesis成立的概率。通俗的讲,\(P(H|E) = \frac{H中符合E的概率}{符合E的总概率} = \frac{P(E|H) P(H)}{P(E)}\)

  • \(P(E|H) = \frac{4}{10}\) :P(evidence given hypothesis) Likehood 似然,这里指 图书管理员 中 符合Evidence 的概率。

  • \(P(H) = \frac{1}{1+20} = \frac{1}{21}\)\(P(H)\)也叫 先验,这里指图书管理员的概率。

  • \(P(E) = \frac{4}{10} + \frac{1}{20} = \frac{9}{20}\)

所以 $P(H|E) = {P(E)} = {} $

reference#

1.Bayesian inference 2.Bayes' theorem 3.贝叶斯推断及其互联网应用(一):定理简介

1.what can Binary Indexed Tree do?#

树状数组,也称作“二叉索引树”(Binary Indexed Trees)或 Fenwick Tree。 它可以高效地实现如下两个操作: 1. 数组前缀和(prefix sum)的查询 2. 单点更新(update)

2.why choose Binary Indexed Tree?#

假设有个n维数组: [2,3,5,-1,6] query(2,4) 求从第二个元素到第四个元素的和: 3 + 5 - 1 = 7 update(4, 2): 更新第四个元素加2

  • 普通遍历法: query: O(n) update: O(1)

  • dp 方法 建立prefix sums数组(前i个元素的和) 耗时: O(n); query: O(1) 如果有个元素需要更新,则需要更新所有涉及的 prefix sums数组元素:O(n)

  • 树状数组 每个结点仅 存储部分元素的和 query: O(log(n)) update: O(log(n))

3.Basic idea of Binary Indexed Tree#

Binary Indexed Tree的 每个结点仅 存储部分区间元素的和. 我们以长度为16的数组为例

-- 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
A数组 1 0 2 1 1 3 0 4 2 5 2 2 3 1 0 2
C数组 1 1 3 4 5 8 8 12 14 19 21 23 26 27 27 29
tree node include range 1 1~2 3 1~4 5 5~6 7 1~8 9 9~10 11 9~12 13 13~14 15 1~16
  • A数组是 原值, C数组是 根据某一规则存的是A数组若干项的和
  • 从上图来看,C数组似乎是呈对称的形态,比如C[8]表示A[1] ~ A[8]的和,而C[4]表示A[1] ~ A[4]的和,所以C[8]又可以表示C[4] + [6] + C[7]。一个C数组的元素只有一个父结点,但却有好多子节点,可以形象地理解为一个C数组元素管着一片区域,怎么去知道一个元素到底在管着哪些A数组的元素呢? 下面是C[1] ~ c[8]值计算方式:
    1
    2
    3
    4
    5
    6
    7
    8
    C[1] = A[1];
    C[2] = A[1] + A[2];
    C[3] = A[3];
    C[4] = A[1] + A[2] + A[3] + A[4];
    C[5] = A[5];
    C[6] = A[5] + A[6];
    C[7] = A[7];
    C[8] = A[1] + A[2] + A[3] + A[4] + A[5] + A[6] + A[7] + A[8];
    我们把上面索引的十进制,全部换成二进制,如下:
    1
    2
    3
    4
    5
    6
    7
    8
    C[1] = C[0001] = A[0001];
    C[2] = C[0010] = A[0001]+A[0010];
    C[3] = C[0011] = A[0011];
    C[4] = C[0100] = A[0001]+A[0010]+A[0011]+A[0100];
    C[5] = C[0101] = A[0101];
    C[6] = C[0110] = A[0101]+A[0110];
    C[7] = C[0111] = A[0111];
    C[8] = C[1000] = A[0001]+A[0010]+A[0011]+A[0100]+A[0101]+A[0110]+A[0111]+A[1000];
    仔细观察上面的二进制表示,我们可以发现,\(C[i]\)管的范围就是 \(i\) 的二进制表示数 从低位到高位第一个为1的位置,(高位保持不变)和其所有低位二进制元素之和

3.1 lowbit#

那么怎么知道从低位起第一个为1的数怎么表示?这个操作有个名字叫做lowbit,计算方式为:

1
lowbit(i) = i & (-i) 
计算该数的二进制从右往左第一个非0位所表示的10进制数,如下所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* (二进制)保留最低位的1及其后面的0,高位的1全部变为0,
* 即得到该数的二进制从右往左第一个非0位所表示的10进制数。
* 例如:
* <pre>
* 原值 0000 0110
* 取反 1111 1001
* +1 1111 1010
* & 0000 0010
* </pre>
*
* @param k 待处理的十进制数
* @return 处理后的十进制数
*/
private static int lowBit(int k) {
return k & -k;
}
再从图片宏观来看,我们发现整个结构似乎有点 二分的味道,具有某种对称性。 - 如果数组位置是奇数,那么\(C[i] = A[i]\); - 如果是偶数,可以形象地看作二分:\(C[i] = C[i / 2] + A[i - (i / 2) + 1] + .... + A[i]\)。 而\(C[i / 2]\)又可以利用上面的二分继续继续进行分割,直至不可分割。上面可以推导出如下公式:\(C[i]=A[i−2k+1]+A[i−2k+2]+…+A[i]\) ,其中k为i的二进制中从最低位到高位连续零的长度。其中这个2k就是上面提到的\(lowbit(i)\)。所以我们就知道即C[i]数组中保存的就是数组A的区间\([i-lowbit(i)+1, i]\)中所有数的和,公式如下:

\[ C[i] = \sum_{n=i-lowbit(i)+1}^i A[i]\]

除此之外,还有一些规律:

  • \(C[i]\)保存的是以它为根的子树中所有叶节点的和。
  • \(C[i]\)的子节点数量\(lowbit(i)\)
  • \(C[i]\)的父节点为\(C[i+lowbit(i)]\)

4.update 单点更新#

在实际编码中,是没有\(A\)数组的,只有\(C\)数组,数据是保存在\(C\)数组的,但逻辑上的操作是针对A数组,比如获取和更新某个索引位置的元素。

我们知道,\(C\)数组中,父节点是所有子节点的和。当子节点更新时,需要从下至上更新所有关联结点。在更新过程中,也就是需要找到所有父节点。已知 \(C[i]\)的父节点为\(C[i+lowbit(i)]\),所以每次向上寻找,只需在索引 \(i\) 加上 \(lowbit(i)\)

1
2
3
4
while (i <= length) {
tree[i] += value;
i += lowBit(i);
}

5.prefix sum 数组前缀和#

查找和的路径有点像单点更新的 逆过程。前i个数的和 \(Sum(i)\)\(C\)数组可以表示为:

\[ Sum(i) = Sum(i-lowbit(i)) + C[i] \]

代码可以写为:

1
2
3
4
5
int sum = 0;
while (i > 0) {
sum += tree[i];
i -= lowBit(i);
}

reference#

  1. Fenwick Tree
  2. Binary Indexed Trees
  3. 树状数组BinaryIndexedTree
  4. 树状数组学习笔记
  5. 树状数组

nohup#

nohup在 忽略hangup signals的情况下运行给定命令, 可以实现 退出帐户/关闭终端之后继续运行相应的进程

1
nohup command [arg]…
  • 在缺省情况下该作业的所有输出都被 重定向到一个名为nohup.out的文件中。

  • 如果当前目录的 nohup.out 文件不可写,输出重定向到 $HOME/nohup.out 文件中。

若要输出到 nohup.out以外的文件中,可以对其进行重定向。例如

1
nohup make > make.log

  • nohup 不会自动将它运行的命令放在后台;必须通过在命令行末尾 加上“&”

nohup和&的区别#

  • & : 指在后台运行

  • nohup : 不挂断的运行,注意 并没有后台运行的功能。用nohup运行命令可以使命令永久的执行下去,和用户终端没有关系,例如我们断开SSH连接都不会影响他的运行,注意了nohup没有后台运行的意思;&才是后台运行

  • &是指在后台运行,但当用户退出(挂起)的时候,命令自动也跟着退出

example 1#

1
sh test.sh & 

将sh test.sh任务放到后台 ,关闭xshell,对应的任务也跟着停止

example 2#

1
nohup sh test.sh  

将sh test.sh任务放到后台关闭标准输入,终端不再能够接收任何输入(标准输入),重定向标准输出和标准错误到当前目录下的nohup.out文件,即使关闭xshell退出当前session依然继续运行

example 3#

1
nohup sh test.sh  & 

将sh test.sh任务放到后台,但是依然可以使用标准输入终端能够接收任何输入,重定向标准输出和标准错误到当前目录下的nohup.out文件,即使关闭xshell退出当前session依然继续运行

example 4#

1
nohup command > myout.file 2>&1 &   

在上面的例子中,0 – stdin (standard input),1 – stdout (standard output),2 – stderr (standard error);2>&1是将标准错误(2)重定向到标准输出(&1),标准输出(&1)再被重定向输入到myout.file文件中。

reference#

  1. nohup: Run a command immune to hangups
  2. nohup 详解

.py文件是如何转换为机器指令被CPU执行呢?.pyc文件作用是什么?

执行原理#

C/C++之类的编译性语言编写的程序,是需要从源文件转换成计算机使用的机器语言,经过 链接器链接之后形成了二进制的可执行文件。运行该程序的时候,就可以把 二进制程序从硬盘载入到内存中并运行。

但是对于Python而言,python源码 不需要编译成二进制代码,它可以直接从源代码运行程序

从程序执行时的基本表示是 实际计算机上的机器语言 还是 虚拟机的机器语言维度,可以将程序设计语言划分为两大类:编译型语言和解释型语言

  • 编译实现的语言,如:C、C++、Fortran、Pascal、Ada。由编译型语言编写的源程序需要经过 编译,汇编和链接才能输出目标代码,然后 由机器执行目标代码。目标代码是由 机器指令组成,不能独立运行,因为源程序中可能使用了一些汇编程序不能解释引用的库函数,而库函数又不在源程序中,此时还需要链接程序完成外部引用和目标模板调用的链接任务,最后才能输出可执行代码。

  • 解释型语言,解释器 不产生目标机器代码,而是 产生中间代码,这种中间代码与机器代码不同,中间代码的解释是 由软件支持的不能直接使用在硬件上。该软件解释器通常会 导致执行效率较低,用解释型语言编写的程序是由另一个可以理解中间代码的解释程序执行的。和编译的程序不同的是, 解释程序的任务是 逐一将源代码的语句解释成可执行的机器指令,不需要将源程序翻译成目标代码再执行。对于解释型语言,需要一个专门的解释器 来执行该程序,每条语句只有在执行是才能被翻译,这种解释型语言每执行一次就翻译一次,因而效率低下

  • Java解释器,java很特殊,java是需要编译的,但是没有直接编译成机器语言,而是编译成字节码,然后在Java虚拟机上用解释的方式执行字节码。Python也使用了类似的方式,先将python编译成 python字节码,然后由一个专门的python字节码解释器负责解释执行字节码。

  • python是一门解释语言,但是出于效率的考虑,提供了一种编译的方法。编译之后就得到pyc文件,存储了字节码。python这点和java很类似,但是java与python不同的是,python是一个解释型的语言,所以编译字节码不是一个强制的操作,事实上,编译是一个自动的过程,一般不会在意它的存在。编译成字节码可以节省加载模块的时间,提高效率。除了效率之外,字节码的形式也增加了反向工程的难度,可以保护源代码。这个只是一定程度上的保护,反编译还是可以的。

执行过程#

Python 更像 Shell 脚本这样的解释性语言,实际上执行原理本质同Java一样,都可以归纳为 虚拟机字节码

虽然 python 命令也叫做 Python 解释器 ( Interpreter ),但跟其他脚本语言解释器有本质区别。实际上, Python 解释器包含 编译器 以及 虚拟机 两部分。当 Python 解释器启动后,主要执行以下两个步骤:

  1. 编译器 将 .py 文件中的 Python 源码 编译成 字节码
  2. 虚拟机 逐行执行编译器生成的 字节码

因此, .py 文件中的 Python 语句 并没有直接转换成机器指令,而是转换成 Python 字节码 。

字节码#

Python中有一个内置函数 compile(),可以将源文件 编译成codeobject,首先看这个函数的说明:

1
compile(source, filename, mode[, flags[, dont_inherit]]) -> code object
- source ,源文件的内容字符串 - filename ,源文件名称 - mode 编译模式 :exec-编译module,single-编译一个声明,eval-编译一个表达式

PyCodeObject#

定义 test.py文件

1
2
3
4
5
6
7
8
9
10
11
12
PI = 3.14

def circle_area(r):
return PI * r ** 2

class Dog(object):

def __init__(self, name):
self.name = name

def yelp(self):
print('woof, i am', self.name)
调用 compile 函数编译源码
1
2
3
4
5
>>> result = compile(open('C:\\Users\\liuwen03\\Desktop\\test.py').read(), 'test.py', 'exec')
>>> result
<code object <module> at 00000000036E27B0, file "test.py", line 1>
>>> result.__class__
<type 'code'>
看上去我们得到了一个 code对象 。 在 Include/code.h 中,可以找到代表代码对象的 C 结构体 PyCodeObject 。 PyCodeObject 定义如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
/* Bytecode object */
struct PyCodeObject {
PyObject_HEAD
int co_argcount; /* #arguments, except *args 参数数量*/
int co_posonlyargcount; /* #positional only arguments */
int co_kwonlyargcount; /* #keyword only arguments 关键字参数个数*/
int co_nlocals; /* #local variables 局部变量个数*/
int co_stacksize; /* #entries needed for evaluation stack 执行代码所需栈数量*/
int co_flags; /* CO_..., see below 标识*/
int co_firstlineno; /* first source line number 代码块首行行号*/
PyObject *co_code; /* instruction opcodes 指令操作码,也就是字节码*/
PyObject *co_consts; /* list (constants used) 常量列表*/
PyObject *co_names; /* list of strings (names used) 全局变量名列表*/
PyObject *co_varnames; /* tuple of strings (local variable names) 局部变量名列表*/
PyObject *co_freevars; /* tuple of strings (free variable names) 闭包名字列表*/
PyObject *co_cellvars; /* tuple of strings (cell variable names) 被嵌套函数使用的名字列表*/
/* The rest aren't used in either hash or comparisons, except for co_name,
used in both. This is done to preserve the name and line number
for tracebacks and debuggers; otherwise, constant de-duplication
would collapse identical functions/lambdas defined on different lines.
*/
Py_ssize_t *co_cell2arg; /* Maps cell vars which are arguments. */
PyObject *co_filename; /* unicode (where it was loaded from) 文件名*/
PyObject *co_name; /* unicode (name, for reference) 函数名*/
PyObject *co_linetable; /* string (encoding addr<->lineno mapping) See
Objects/lnotab_notes.txt for details. */
void *co_zombieframe; /* for optimization only (see frameobject.c) */
PyObject *co_weakreflist; /* to support weakrefs to code objects */
/* Scratch space for extra data relating to the code object.
Type is a void* to keep the format private in codeobject.c to force
people to go through the proper APIs. */
void *co_extra;

/* Per opcodes just-in-time cache
*
* To reduce cache size, we use indirect mapping from opcode index to
* cache object:
* cache = co_opcache[co_opcache_map[next_instr - first_instr] - 1]
*/

// co_opcache_map is indexed by (next_instr - first_instr).
// * 0 means there is no cache for this opcode.
// * n > 0 means there is cache in co_opcache[n-1].
unsigned char *co_opcache_map;
_PyOpcache *co_opcache;
int co_opcache_flag; // used to determine when create a cache.
unsigned char co_opcache_size; // length of co_opcache.
};
从源码可以看出, 代码对象 PyCodeObject 用于存储编译结果,包括 字节码 以及代码涉及的 常量 名字 等等。

1
2
>>> result.co_code
'd\x00\x00Z\x00\x00d\x01\x00\x84\x00\x00Z\x01\x00d\x02\x00e\x02\x00f\x01\x00d\x03\x00\x84\x00\x00\x83\x00\x00YZ\x03\x00d\x04\x00S'

字节码现在看上去如同天书一般。看看代码对象涉及的所有名字 和 常量列表:

1
2
3
4
5
>>> result.co_names
('PI', 'circle_area', 'object', 'Dog')

>>> result.co_consts
(3.14, <code object circle_area at 00000000036DBEB0, file "test.py", line 3>, 'Dog', <code object Dog at 00000000036E2A30, file "test.py", line 6>, None)

常量列表里 还有两个代码对象!其中一个是 circle_area 函数体,另一个是 Dog 类定义体。回忆一下 Python 作用域 的划分方式: 每个作用域对应着一个代码对象 !若假设成立, Dog 代码对象的常量列表应该还藏着两个代码对象,分别代表 init 方法和 yelp 方法的函数体:

事实确实如此:

1
2
3
4
>>> dog_code = result.co_consts[3]
>>> dog_code.co_consts
(<code object __init__ at 00000000036D48B0, file "test.py", line 8>, <code object yelp at 00000000036D49B0, file "test.py", line 11>)
>>>
因此,我们得到以下结论: Python 源码编译后,每个作用域都对应着一个代码对象子作用域代码对象位于父作用域代码对象常量列表里,层级一一对应。

反编译#

从上面我们可以看到,字节码是一堆长得跟天书一样的不可读的字节序列,跟二进制机器码一样。 用dis反编译字节码,让它变得可读

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
>>> import dis
>>> dis.dis(result.co_code)
0 LOAD_CONST 0 (0)
3 STORE_NAME 0 (0)
6 LOAD_CONST 1 (1)
9 MAKE_FUNCTION 0
12 STORE_NAME 1 (1)
15 LOAD_CONST 2 (2)
18 LOAD_NAME 2 (2)
21 BUILD_TUPLE 1
24 LOAD_CONST 3 (3)
27 MAKE_FUNCTION 0
30 CALL_FUNCTION 0
33 BUILD_CLASS
34 STORE_NAME 3 (3)
37 LOAD_CONST 4 (4)
40 RETURN_VALUE
第一列是字节码偏移量 ,第二列是 指令 ,第三列是 操作数

以第一条字节码为例, LOAD_CONST 指令将常量加载进栈,常量下标由操作数给出。而下标为 0 的常量是:

1
2
>>> result.co_consts[0]
3.14
因此,第一条字节码就是将常量 3.14 加载到栈。

由于代码对象 保存了 常量、名字等上下文信息,因此直接对代码对象进行反编译可以得到更为清晰的结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
>>> dis.dis(result)
1 0 LOAD_CONST 0 (3.14)
3 STORE_NAME 0 (PI)

3 6 LOAD_CONST 1 (<code object circle_area at 00000000036DBEB0, file "test.py", line 3>)
9 MAKE_FUNCTION 0
12 STORE_NAME 1 (circle_area)

6 15 LOAD_CONST 2 ('Dog')
18 LOAD_NAME 2 (object)
21 BUILD_TUPLE 1
24 LOAD_CONST 3 (<code object Dog at 00000000036E2A30, file "test.py", line 6>)
27 MAKE_FUNCTION 0
30 CALL_FUNCTION 0
33 BUILD_CLASS
34 STORE_NAME 3 (Dog)
37 LOAD_CONST 4 (None)
注意到,操作数 指定的 常量或名字的 实际值在旁边的括号内列出。另外,字节码以语句为单位进行分组,中间以空行隔开语句行号在字节码前面给出。 PI = 3.14 这个语句编译成以下两条字节码:
1
2
1           0 LOAD_CONST               0 (3.14)
3 STORE_NAME 0 (PI)

pyc#

如果将 test 作为模块导入, Python 将在 test.py 文件所在目录下生成 .pyc 文件:

import test pyc 文件保存 经过序列化处理的代码对象 PyCodeObject 。这样一来, Python 后续导入 test 模块时,直接读取 pyc 文件并反序列化即可得到代码对象 ,避免了重复编译导致的开销。只有 test.py 有新修改(时间戳比 pyc 文件新), Python 才会重新编译。

reference#

  1. python编译过程和执行原理
  2. Python 源码深度剖析/18 Python 程执行过程与字节码

What is Soft Link And Hard Link?#

  • 硬链接(Hard Link) 充当所选文件的副本(镜像)。它访问原始文件中的可用数据。如果删除了先前选择的文件,则指向该文件的硬链接仍将包含该文件的数据
  • 软链接(Soft Link) 充当文件名的 指针或引用。它不会访问原始文件中的可用数据。如果删除了先前的文件,则软链接将 指向不再存在的文件

图解Soft Link 和 Hard Link#

Difference#

参数 Hard link Soft link
Inode number* 相同 不同
Directories 目录 不能链接目录(超级用户可以链接目录) 可以链接目录
File system 文件系统 不能跨文件系统 可以跨文件系统
Data 数据 不能跨文件系统 可以跨文件系统
File system 文件系统 保留原始数据文件 仅指向文件名,不保留文件的数据
permissions文件权限 与原始文件 始终保持相同权限 权限不同
Original file’s deletion 原始文件的删除 该链接仍可访问原始文件的数据 该链接无法使用,因为它无法访问原始文件的数据
Speed 访问速度 相对快 相对慢

reference#

  1. Difference between Hard link and Soft link
  2. Explaining Soft Link And Hard Link In Linux With Examples

inode是一个重要概念,是理解Unix/Linux文件系统和硬盘储存的基础

1. what is inode?#

linux文件相关 size 属性
扇区(Sector) 512bytes(0.5KB) 硬盘的最小存储单位
块(block) 通常4KB 文件存取的最小单位(操作系统读取硬盘,一次性读取一个块,提高效率)
inode 一般是128字节或256字节 储存文件的元信息(如创建者、创建日期、大小等)

inode, 中文译名为"索引节点",包含了与该 文件有关的一些信息

2. the content of inode#

use stat to check the content of inode:

1
2
3
4
5
6
7
8
9
$ stat mssh_ip.sh

File: ‘mssh_ip.sh’
Size: 183 Blocks: 8 IO Block: 4096 regular file
Device: fe21h/65057d Inode: 7120498 Links: 1
Access: (0644/-rw-r--r--) Uid: ( 7857/liuwen03) Gid: ( 7857/liuwen03)
Access: 2021-01-28 14:13:04.213991000 +0800
Modify: 2021-01-28 14:13:04.213991000 +0800
Change: 2021-02-04 11:37:19.457991000 +0800

由此可以看出, inode包含文件的元信息,具体来说有以下内容:

1
2
3
4
5
6
7
  * 文件的字节数
  * 文件拥有者的User ID
  * 文件的Group ID
  * 文件的读、写、执行权限
  * 文件的时间戳,共有三个:ctime指inode上一次变动的时间,mtime指文件内容上一次变动的时间,atime指文件上一次打开的时间。
  * 链接数,即有多少文件名指向这个inode
  * 文件数据block的位置

3. what is inode number?#

inode是inode table中的一个item。Linux扩展文件系统 (如ext2/ext3) 维护了一个inode的数组:inode table。inode table包含该文件系统中所有文件的列表。inode table中的各个inode项具有 唯一的编号 (该文件系统唯一),即inode number

使用ls -i命令,可以看到文件名对应的inode number:

1
2
$ ls -li mssh_ip.sh
7120498 -rw-r--r-- 1 liuwen03 liuwen03 183 Jan 28 14:13 mssh_ip.sh
## 4. the size and the number of inode inode也会消耗硬盘空间,所以硬盘格式化的时候,操作系统自动将硬盘分成两个区域。一个是 数据区,存放文件数据;另一个是inode区(inode table),存放inode所包含的信息。 每个inode节点的size,一般是 128或256字节。inode节点的总数,在格式化时就给定,一般是每1KB或每2KB就设置一个inode。假定在一块1GB的硬盘中,每个inode节点的大小为128字节,每1KB就设置一个inode,那么inode table的大小就会达到128MB,占整块硬盘的12.8%。

df -i显示文件系统inode的使用信息

1
2
3
4
5
6
7
8
9
10
$ df -i
Filesystem Inodes IUsed IFree IUse% Mounted on
/dev/dm-0 8134656 127239 8007417 2% /
udev 2056585 353 2056232 1% /dev
tmpfs 2058885 487 2058398 1% /run
tmpfs 2058885 4 2058881 1% /dev/shm
tmpfs 2058885 4 2058881 1% /run/lock
tmpfs 2058885 13 2058872 1% /sys/fs/cgroup
/dev/vdc1 209713152 344897 209368255 1% /home
/dev/vda1 62248 328 61920 1% /boot

5. 目录的inode结构#

Linux中的 目录 也被视为文件。目录是将文件名映射到其inode number的特殊文件 (此映射称为dentry)。因此,当我们说某个目录包含文件和其他目录时,我们的意思是该目录将这些文件和目录映射到它们的inode number。这就是目录无法容纳两个具有相同名称的文件的原因,因为它无法使用两个不同的inode number映射一个名称

每个目录项,由两部分组成:所包含文件的文件名,以及该文件名对应的inode号码

理解了上面这些知识,就能理解目录的权限。目录文件的读权限(r)和写权限(w),都是针对目录文件本身。由于目录文件内只有文件名和inode号码,所以 如果只有读权限,只能获取文件名,无法获取其他信息,因为其他信息都储存在inode节点中,而 读取inode节点内的信息需要目录文件的执行权限(x)

6、inode的特殊作用#

由于inode号码与文件名分离,这种机制导致了一些Unix/Linux系统特有的现象。

  • 有时,文件名包含特殊字符,无法正常删除。这时,直接删除inode节点,就能起到删除文件的作用

  • 移动文件或重命名文件,只是改变文件名,不影响inode号码

  • 打开一个文件以后,系统就 以inode号码来识别这个文件,不再考虑文件名。因此,通常来说,系统无法从inode号码得知文件名

reference#

1.理解inode 2.详解Linux Inode

Linux ln命令是一个非常重要而且常用命令,它用于 为文件或者目录创建链接

Links types#

soft link/hard link

使用方法#

-P (默认也是)创建 Hard links#

1
2
3
$ ln -P my_file.txt my_link.txt

$ ln my_file.txt my_link.txt

-s 创建 Symlinks(Soft links)#

1
$ ln -s [OPTIONS] FILE LINK
    1. 创建一个软链接 my_link.txt, 指向my_file.txt
      1
      $ ln -s my_file.txt my_link.txt
    1. 给文件创建 Symlinks
      1
      $ ln -s /mnt/my_drive/movies ~/my_movies

-f (--force) 强制覆盖#

如果创建一个已经存在的 Symlinks, 会报错

1
2
3
$ ln -s my_file.txt my_link.txt

ln: failed to create symbolic link 'my_link.txt': File exists

使用 -f 强制覆盖

1
$ ln -sf my_file.txt my_link.txt

移除Symlinks#

  • unlink
    1
    $ unlink symlink_to_remove
  • rm
    1
    $ symlink_to_remove

注: 不要在文件末尾加上 "/"

reference#

  1. Ln Command in Linux
  2. Linux软链接和硬链接的区别之ln命令详解

江湖传言linux有三剑客, 而awk 是三剑客的老大。利剑出鞘,潜龙升天。#

introduction#

awk属于 一种编程语言,可以通过编程实现各种需要的文本处理需求。awk在其对 数据分析并生成报告时,显得尤为强大。简单来说awk就是把文件 逐行的读入,以 空格为默认分隔符将每行切片,切开的部分再进行各种分析处理。

awk有3个不同版本: awk、nawk和gawk,未作特别说明,一般指gawk,gawk 是 AWK 的 GNU 版本。

awk工作流程:#

读入有 ''换行符分割的一条记录,然后将记录按指定的域分隔符划分域,填充域,$0则表示所有域(原记录),\(1**表示**第一个域**,\)n表示第n个域。默认域分隔符是 空白键 或 tab键**。

基本用法:#

1
awk [选项参数] 'BEGIN{命令 } pattern{ 命令 } END{ 命令 }'  文件名    # 行匹配语句 awk '' 只能用单引号

pattern模块#

打印某几列#

1
2
$ echo 'i am so busy' | awk '{print $1, $2 $3}'
i amso

选项参数#

-F参数:指定分隔符

1
2
3
4
5
$ echo '192.168.1.1' | awk -F "." '{print $2}'
168

$ echo 'I am leo。my qq is xxxxxx' |awk -F '[ ,]+' '{print $3" "$7}' # 多个分隔符
leo xxxxxx
- 我们将字符串 i am so busy 通过管道传递给awk命令,相当于awk处理一个文件,该文件的内容就是i am so busy,默认通过空格作为分隔符(不管列之间有多少个空格都将当作一个空格处理)i am so busy就分割成四列了。 - 另外,我们发现pattern{ 命令 }中","输出时为空格" "。

BEGIN 定义表头#

1
2
3
4
5
6
7
8
9
10
11
$ cat score.txt
tom 60 60 60
kitty 90 95 87
jack 72 84 99

$ awk 'BEGIN{print "姓名 语文 数学 英语"}{printf "%-8s%-5d%-5d%-5d\n",$1,$2,$3,$4}' score.txt # 左对齐的操作(%-8s左对齐,宽8位)

姓名 语文数学英语
tom 60 60 60
kitty 90 95 87
jack 72 84 99

END#

1
2
3
4
5
6
7
8
9
10
11
12
$ awk 'BEGIN{print "姓名 语文 数学 英语 总成绩"; \
sum1=0;sum2=0;sum3=0;sumall=0} \ # 这里定义了初始值
{printf "%5s%5d%5d%5d%5d\n",$1,$2,$3,$4,$2+$3+$4;\
sum1+=$2;sum2+=$3;sum3+=$4;sumall+=$2+$3+$4}\
END{printf "%5s%5d%5d%5d%5d\n","总成绩",sum1,sum2,sum3,sumall}'\
score.txt

姓名 语文 数学 英语 总成绩
tom 60 60 60 180
kitty 90 95 87 272
jack 72 84 99 255
总成绩 222 239 246 707

运算符#

运算符 作用
= += -= *= /= %= ^= **= 赋值
?: C条件表达式
&& 逻辑与
~ 和 !~ 匹配正则表达式和不匹配正则表达式
< <= > >= != == 关系运算符
空格 连接
+ - 加,减
* / % 乘,除与求余
+ - ! 一元加,减和逻辑非
^ *** 求幂
++ -- 增加或减少,作为前缀或后缀
$ 字段引用
in 数组成员
example1:过滤第一列大于2的行#
1
2
3
4
5
$ awk '$1>2' log.txt    #命令
#输出
3 Are you like awk
This's a test
10 There are orange,apple,mongo

exmple2:输出奇数行#

1
2
3
4
5
6
7
8
9
10
11
$ vim demo.txt
root:x:0:0:root:/root:/usr/bin/zsh
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync

$ awk -F ':' 'NR % 2 == 1 {print $1}' demo.txt
root
bin
sync

exmple3:输出第一个字段等于指定值的行#

1
2
3
4
5
6
$ awk -F ':' '$1 == "root" {print $1}' demo.txt
root

$ awk -F ':' '$1 == "root" || $1 == "bin" {print $1}' demo.txt
root
bin

if语句#

example1:善用if else判断语句#
1
2
3
4
5
6
$ awk '{if($2>=90 )print $0}' score.txt
kitty 90 95 87
$ awk '{if($2>=90 )print $1,"优秀"; else print $1,"良好"}' score.txt
tom 良好
kitty 优秀
jack 良好

内置变量#

常用内置变量:

  • FILENAME: 用于保存输入文件名称,当前文件名
  • NF: 一条记录的 字段的数目
  • NR:已经读出的记录数,就是 行号从1开始
  • OFS:用于设置输出分隔字符字符,默认空格。
  • FS:字段分隔符(默认是任何空格)。
  • ORS:用于设置输出记录分隔符,默认为新的一行。
  • RS:记录分隔符(默认是一个换行符)。
  • OFMT:数字的输出格式(默认值是%.6g)。
  • ENVIRON:读取环境变量。
example1:只查看test.txt文件第20到第30行#
1
$ awk '{if(NR>=20 && NR<=30) print $0}' test.txt 

example2:保留小数点后两位#

1
2
$ awk 'BEGIN{OFMT="%.2f";print 1.2567,12E-2}'
1.26 0.12

输出倒数第二个字段#

变量 NF表示当前行有多少个字段,因此 \(NF**就代表 **最后一个字段**。 **\)(NF-1)代表 倒数第二个字段

1
2
3
4
5
6
$ awk -F ':' '{print $1, $(NF-1)}' demo.txt
root /root
daemon /usr/sbin
bin /bin
sys /dev
sync /bin

reference#

1.awk 详解 2.ILinux三剑客传 | 老大:AWK 3.awk 入门教程

what is MongoDB#

MongoDB 是一个 基于分布式文件存储的开源数据库系统。由 C++ 语言编写。旨在为 WEB 应用提供可扩展的高性能数据存储解决方案。

MongoDB 是一个 介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。

在高负载的情况下,添加更多的节点,可以保证服务器性能。

MongoDB 将数据存储为一个 文档,数据结构由 键值(key=>value)对组成。MongoDB 文档类似于 JSON 对象。字段值 可以包含其他文档,数组及文档数组

the feature of MongoDB#

  1. MongoDB 是一个面向文档存储的数据库,操作起来比较简单和容易。
  2. 你可以在MongoDB记录中设置任何属性的索引 (如:FirstName="Sameer",Address="8 Gandhi Road")来实现更快的排序
  3. 你可以通过本地或者网络创建数据镜像,这使得MongoDB有更强的扩展性
  4. 如果负载的增加(需要更多的存储空间和更强的处理能力) ,它可以分布在计算机网络中的其他节点上这就是所谓的分片
  5. Mongo支持丰富的查询表达式。查询指令使用JSON形式的标记,可轻易查询文档中内嵌的对象及数组。
  6. MongoDb 使用update()命令可以实现替换完成的文档(数据)或者一些指定的数据字段 。
  7. Mongodb中的Map/reduce主要是用来对数据进行批量处理和聚合操作
  8. Map和Reduce。Map函数调用emit(key,value)遍历集合中所有的记录,将key与value传给Reduce函数进行处理。
  9. Map函数和Reduce函数是使用Javascript编写的,并可以通过db.runCommand或mapreduce命令来执行MapReduce操作
  10. GridFS是MongoDB中的一个内置功能,可以用于存放大量小文件
  11. 允许在服务端执行脚本,可以用Javascript编写某个函数,直接在服务端执行,也可以把函数的定义存储在服务端,下次直接调用即可。
  12. 支持 各种编程语言:RUBY,PYTHON,JAVA,C++,PHP,C#等多种语言。

reference#

1.MongoDB 教程 2.Introduction to MongoDB