1. umask
豆瓣网友指出,微信读书有个好处,就是能看到别人的标注和笔记,别人也能看到自己的。kindle也能共享,但是远没有微信方便和活跃。我就偷偷看别人的笔记,比如大家读古龙时候的感叹。有时猜测,这位读者是个孩子,那位读者觉得自己不是孩子了。有时自己也感叹,哇,当年没有读懂,或者当年肯定读不懂。有时还参与两句。
也看娄嘉鹏老师的技术笔记,他的很多阅读不是为了学习,而是考虑在教学中如何清晰讲解,比如下面这条就挺有意思。
娄老师标注的是文字原文是这样的,"特别强调一下,网络上有很多关于计算umask遮罩后权限值的讲解,比较主流但是错误的讲解方式是使用“同位相减”的做法来计算遮罩后的值"。
"特别强调一下",WJY同学表达过,这是相当有脾气的说法。作者的意思是"就这你还不明白?我还得再说一遍。"对这句话,教师当慎言。
(1)
后面作者提到了错误的看法,还举了例子。用 权限666 减去 掩码011,同位相减得到权限值 655。这个结果是错的,正确的权限值是 666。
为啥正确呢,不知道。作者没有给出正确的算法。教学中教师通常容易犯的错误是只把正确的说了,但是(既不说原理,也)不说哪些是错的。比如深蹲时双脚就像旋入地面,原理是绷紧下肢提高对膝盖的控制,尤其是最低端,对应的错误是大腿 (内侧?) 放松。教练就说,旋进地面啊,拳要打进去,站直,你怎么连什么是站直也不会,不是超伸。好,终于说到了什么是不对的。
正确的权限值是 666,这可以通过实验验证,但是原理还是不清楚。
作者说,权限666 和 掩码011 间的运算,当作按位减是不对的,那么什么是对的呢?对这个算法的描述,作者用的动词是"遮罩掉"。遮罩就是掩码mask。但是,"遮罩掉"是个什么操作,难度不 (显然) 是按位减么?作者定义了个名词,但是没有解释。
上网搜索一下,比如[https://www.cnblogs.com/wish123/p/7073114.html]在《Linux中设置UMASK值》这篇博客用的动词是,从最大权限777中"去掉"umask值。
遮罩掉、去掉,有同学说,不是挺明白的么?
(2)
什么是明白?就是能编程序实现。C语言里有定义遮罩掉、去掉这两个函数或操作符么。你是不是又想做按位减法了。
对了,娄老师标记这段话时,是怎么评论的呢?他说,"一般与掩码与,umask取反与"。我最初没看明白,但是娄老师这句话显然比作者更靠谱,至少 与、取反这样的术语,比"遮罩掉、去掉"更接近机器的语言。在靠谱这个意义上,不说遮罩掉、去掉这样的人话,更像是做人事。
我没问娄老师到底啥意思,又不急着知道,觉得自己想想挺好玩的。
事实上,我能体会到 遮罩掉、去掉 的含义,也就是说,我可以枚举/穷举出所有的结果,而且这本书里有例子可以验证。于是我打了个表格,以下第一列p代表 privilege权限,第二列是umask,第三列是求出的结果。
|P | umask | r ||0 | 0 | 0 ||0 | 1 | 0 ||1 | 0 | 1 ||1 | 1 | 0 |
看第三010行,确实不是按位减。这一位,也就是作者举的 666 去掉 011 以后 得 666,而不是 655,6或5转换成二进制为110和101的最后一bit。
有个这个表格,我们可以写一大堆if-else……了。
(3)
不优雅啊,我估计RH和GYB同学会这么说。
离散数学课和数字电路课上,我们学过把真值表转换成逻辑表达式。过程略过,结果是
r = p & !umask
这说我想起了娄老师说的话,"umask取反与",umask取反,然后与p。
2. 补码
什么时候用补码?负整数在计算机中用补码表示。
为什么需要补码?通过补码,CPU可以使用加法器做减法计算。
怎么算补码?反码加1。
为什么?
那些天同时在看这本书,[https://book.douban.com/subject/25882201/],From Mathematics to Generic Programming。看到补码运算的时候写了一段笔记,简单解释补码计算的原理。
(1)补码
补码是什么?
补码 = 模 - 数 ..................公式1
模是什么?模是进制的大小。钟表12进制,模就是12。人类用10进制,模是10。
模12的例子:
(-3) = 12 - 3 = 9 ;9点是差3个小时12点(-2) = 12 - 2 = 10 ;10点是差2个小时12点
模10的例子:
-2 = 10 - 2 = 8
模8的例子:
-2 = 8 - 2 = 6
对上述例子中的最后一个,模8中的-2,我们验证一下。
二进制下2取反+1,
(010)-> 取反 -> (101) -> +1 -> (110)-2 ->->->->->->->->->->->->-> 6
(2)取反
取反操作 <=> (模-1)-数 ............公式2
这是因为 数+反 = 模-1
看两个例子
001+ 110------ 111 010+ 101------ 111
(3)推导
取反 <=> (模-1) - 数 ..........根据公式2取反 + 1 <=> (模-1) - 数 + 1 ..........左右都加1取反 + 1 <=> 模 - 数 .......... -1+1 => 0取反 + 1 <=> 补码 .......... 公式1
所以"取反+1"就是补码。
(4)原理
基于的原理是 : 模 就是 0。
12点就是0点,毕十10就是零蛋。限制位数,0x100,就是 0x00。
更深层的原理是因为 : 求余,求模。
这就是为什么求余运算的名字是求模的原因吧。
(5)编码举例
除去符号位 (假设模8) ,以下每行的两个数字编码相同。
| +0 | -0 || 1 | -7 || 2 | -6 || 3 | -5 || 4 | -4 || 5 | -3 || 6 | -2 || 7 | -1 |
每行的两个数字,它们绝对值的和是8,即模。
举例,-7的补码是
原码 111, 按位取反得到 000, 加1得到 001.
刚好与同一行的 1 的原码 001 相同。
我们还能注意,负数里面还有个 -0。
3. ASCII
这段非常短,就是想回答下不少同学可能有过的疑惑,ASCII里为什么 A是65,a是97,这么有零有整的。不怎么好记啊。
因为,如果你不看两列的ASCII码表,而是找一下16行16列的ASCII码表就会发现,或者按十六进制就会发现,就像1024是整数,'A'和'a'都是整数。
A 是 0x41,即4*16+1 = 65;
a 是 0x61,即6*16+1 = 97。
还有'0',是0x30,即 48。
为什么ASCII要在大写、小写、数字之间加些乱七八糟的字符,逗号、冒号、方括号,还不把这些符号连续地放在一起。正是为了填充,让A、a、0在整数开头吧。