分类目录归档:Others

关于 Linux 用户界面字体

Noto Sans 是个好东西,但是要选好才好看。

Noto Sans 是个不错的字体,中日英混排也算做得还不错。由于其无衬线 / 黑体设计,注定很适合作为一个屏幕字体而使用。但是在操作系统 UI 这个显示场景下,部分字体显的过宽,看起来就让人很烦躁。在经过一番调试以后,总结出了一些比较好的搭配方式。

Noto Sans 在不考虑地区版本(如 CJK)和等宽版本的情况下,主要有非 UI 和 UI 两个后缀。也就是说曾存在有 Noto Sans Arabic 和 Noto Sans Arabic UI 这样两个版本,其主要区别在与 UI 版本的纵向宽度更窄,根据 Google 的设计指引,带有 UI 后缀的字体应该被使用在 UI 设计的场景下,虽然原文中说的是阿拉伯语,但是从我的经验看来,在 UI 场景下使用纵向较窄的一些的特化版本无论在什么语言下都是一个好的选择。

接下来就是很吊诡的一件事了,Ubuntu 的 fonts-noto 包在 16.04 到 18.04 版本中做了较大的改动,原来的版本中包含 Noto Sans 和 Noto Sans UI 这两个字体,因此对于英文场景,直接照着选就完事了。然而在 18.04 中,不知是不是上游的改动,删除了 Noto Sans UI 这个字体。由于一直没找到比较好的纵向较窄的替代品,因此那个较宽的版本让我眼镜不舒服了很长一段时间。

后来发现,Ubuntu 自带的版本中,有一个叫 Noto Sans Display 的版本,这个相比 Noto Sans,仅仅是修改了字符纵向的宽度,符合 UI 的设计特点。在经过一番调试以后,发现确实是一个很不错的选择。

经过测试的字体配置如下,桌面环境为 Xfce:

  • 窗口内文(Appearance 设置项中)Noto Sans Display Regular 9pt
  • 窗口标题(Window Manager 设置项中)Noto Sans Display Bold 9pt
  • Slight Hinting
  • 96/120 dpi(这个一定要设置)

对于 Seedbox 的 VNC 的场景,如果觉得字体略小阅读吃力,可以使用更大一些配置:

  • 窗口内文 Noto Sans Display Regular 10pt
  • 窗口标题 Noto Sans Bold 9pt(注意这里,使用 NSD 会导致窗口标题显得太窄而和内容不协调)
  • Slight Hinting
  • 96 dpi

其实这个配置在我曾经的某个盒子上被配出来过,但是一直没有将其标准化,所以也一直没有记得……

 

关于 OPAL Drive 和 BitLocker 关系的理解

起因是这样的,美帝良心想这个 X1 Carbon 的官网上在调整 SSD 配置的时候,从 256G 加到 1T 需要增加 591USD,即使算上 45%off 的折扣也要 200 多刀。然而一个 SN750 1T 在美亚不过也才 200 刀,在官网加 SSD 就相当不合算了。

但是我注意到了一点,联想官网上对 SSD 的描述是 OPAL,也就是符合 OPAL 标准的自加密 SSD (Self-Encrypting Drive,SED),机器到手以后也默认开启了 BitLocker。在我的记忆中,BitLocker 是一个纯软件的实现,那么这里 SSD 的自加密和 BitLocker 的关系是什么呢?

查了一圈资料以后发现,BitLocker 存在硬件和软件两种实现方式。在检测到硬盘控制器支持硬件加密的时候,Windows 会直接使用该功能,且不对数据进行任何处理的方式就写入磁盘,由主控完成加密。如果主控不支持加密,则使用软件实现。具体的实现方式可以通过 manage-bde 这个命令查看:

如果是软件实现,那么在 Encryption Method 下就会显示类似于 XTS-AES 128 这样的字眼。在一些比较老的版本上可能只是 AES 128。

如果是启用了硬件加密特性,那么应该是这样:

显示为 Hardware Encryption。

如果是官网购买的 OPAL Drive,那么应该是可以显示为 Hardware Encryption 的,如果是自行购买的 SN700(官网是 SN720,也就是前者的 OEM 版本),那么就是软件加密。

如果没有关闭 BitLocker 就取下了硬盘,在数据可以抹掉的情况下,理论上有两种方法可以解开。一个是使用 BitLocker 恢复密钥,这个会自动同步到自己的 Microsoft Account 上,可以看这里。或者也可以考虑直接使用命令抹掉数据并重新生成密钥,这个可以看 ArchWiki 的详细讲解。

Reference:
https://wiki.archlinux.org/index.php/Self-Encrypting_Drives

How to Enable BitLocker Hardware Encryption with SSDs


https://support.microsoft.com/en-us/help/4026181/windows-10-find-my-bitlocker-recovery-key
https://howtogeek.com/fyi/you-cant-trust-bitlocker-to-encrypt-your-ssd-on-windows-10/
https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/ADV180028

如何压缩 VirtualBox 的 vdi 虚拟磁盘

VMware 的虚拟磁盘管理界面上有两个功能,一个是整理磁盘碎片,另一个是回收未使用的空间。这两个功能配合使用,就可以清理掉虚拟磁盘映像文件里面已经删除了的文件依然占用的空间。然而,辣鸡如 VirtualBox 并没有提供这两个功能,需要手动进行未使用空间填零和压缩磁盘映像文件两个步骤。

首先需要确保使用的映像文件格式是动态分配,这样才有压缩的可能性。

如果是 Windows 客户机,那么首先进行磁盘碎片整理,完成之后,下载 sdelete 工具,使用例如:

sdelete.exe c: -z

这样的命令对 C 盘完成填 0 操作。

如果是 Linux 客户机,那么需要使用 zerofree 命令完成这个操作。由于该命令不能在盘符分区已经挂载的情况下工作,且最近版本的 Ubuntu 在 Recovery 模式下也会挂载所有的盘符,因此如果要进行这个操作,需要使用一个 Live CD 进入系统。安装 zerofree 之后,运行:

# zerofree -v /dev/sda1

这样的命令对 /dev/sda1 进行填 0 操作,之后就可以进行压缩了。

压缩的操作我们需要使用 VBoxManage 命令:

VBoxManage modifymedium disk "/path/to/disk.vdi" --compact

在 Windows 下可能需要手动在 VirtualBox 安装路径下找到 VBoxManage.exe 进行操作。之后就压缩成功了。

红米 5A 刷欧洲版 MIUI 记录

老年人的第一次刷机。

最近把红米备机拿出来用了,升到了 ADUI 10,感觉还不错,但是广告实在太多。想起来当年被卡 15 天解 BL 锁的限制早过了很久了,就想着把这破系统刷了吧。

查了一圈发现 MIUI EU 版是相对干净的系统,可以在这里取得。

准备工作

解锁 Bootloader
MIUI EU 版刷机包
Android Debug Bridge
第三方 Recovery TWRP
Root 包 Magisk

刷机步骤

  1. 解锁 Bootloader
    1. 进入“设置 -> 开发者选项 -> 设备解锁状态”中绑定账号和设备;
    2. 手动进入 Fastboot 模式(关机后,同时按住开机键和音量下键);
    3. 通过USB连接手机,点击 “解锁”按钮;
  2. 刷入 TWRP
    1. 在 Bootloader 模式下,运行 fastboot flash recovery <twrp_image>刷入 TWRP
    2. fastboot reboot 重启设备,注意此时需要立即按下音量上 + 电源以进入 Recovery 模式,否则 MIUI 会覆盖刷入的自定义 Revocery
  3. Recovery 成功刷入设备后,将手机关机,在关机状态下按住电源键和音量上键,进入 Recovery 模式,滑动 Swipe to Allow Modifications 进入主菜单。点击 Wipe,点击 Format Data,输入 yes 完成格式化,返回上一菜单,点击 Advanced Wipe,选择 Dalvik,Cache,System,Data 和 Internal Storage 分区,并滑动 Swipe to Wipe 进行擦除,然后返回 Recovery 主菜单
    1. 注意可能在 Wipe /data 分区时可能会出现错误:TWRP Unable to mount '/data',如果出现该错误,在 Fastboot 模式下运行 fastboot format userdata清理 /data 分区之后再进行操作
  4. 在电脑磁盘列表中找到手机,复制 ROM 至手机,复制完成后在 Recovery主菜单中,点击 Install,点击 ROM 包,滑动 Swipe to confirm Flash 进行刷入,重启设备
    1. 任意方法刷入 ROM 后,命令窗口中结束 adb 服务adb kill-server
  5. 重启进入 Recovery 模式,按照刷 ROM 的方式刷入 root 包

然后大功告成

参考资料

https://www.zhihu.com/question/50231539/answer/530627637
https://www.reddit.com/r/Nexus6P/comments/3qnzz0/twrp_unable_to_mount_data/

 

YACC 基本用法

YACC 文件格式

yacc 文件分为三部分:
… definitions …(%{}%)
%%
… rules …
%%
… subroutines …

定义部分

第一部分包括标志(token)定义和 C 代码(用“%{”和“%}”括起来)。
如在定义部分定义标志:
%token INTEGER
当运行 yacc 后,会产生头文件,里面包含该标志的预定义,如:
#ifndef YYSTYPE 
#define YYSTYPE int 
#endif 
#define INTEGER 258 
extern YYSTYPE yylval;
lex 使用该头文件中的标志定义。Yacc 调用 lex 的 yylex() 来获得标志(token),与标志对应的值由lex 放在变量 yylval 中。yylval 的类型由 YYSTYPE 决定,YYSTYPE缺省类型是int。如:
[0-9]+ { 
yylval = atoi(yytext); 
return INTEGER; 
}
标志0-255被保留作为字符值,一般产生的token标志从258开始。如:
[-+] return *yytext; /* return operator */
返回加号或减号。注意要把减号放在前面,避免被认作是范围符号。
对于操作符,可以定义 %left 和 %right:%left表示左相关(left-associative),%right 表示右相关(right-associative)。可以定义多组 %lef t或 %right,在后面定义的组有更高的优先级。如:
%left ‘+’ ‘-‘
%left ‘*’ ‘/’
上面定义的乘法和除法比加法和减法有更高的优先级。
改变 YYSTYPE 的类型。如这样定义 TTSTYPE:
%union
{ 
     int iValue; /* integer value */ 
     char sIndex; /* symbol table index */ 
     nodeType *nPtr; /* node pointer */ 
};

则生成的头文件中的内容是:

typedef union
{ 
     int iValue;      /* integer value */ 
     char sIndex;    /* symbol table index */ 
     nodeType *nPtr; /* node pointer */ 
} YYSTYPE; 
extern YYSTYPE yylval;

可以把标志(token)绑定到 YYSTYPE 的某个域。如:

%token <iValue> INTEGER
%type <nPtr> expr
把 expr 绑定到 nPtr,把INTEGER绑定到 iValue。yacc 处理时会做转换。如:
expr: INTEGER { $$ = con($1); }
转换结果为:
yylval.nPtr = con(yyvsp[0].iValue);
其中 yyvsp[0] 是值栈(value stack)当前的头部。
定义一元减号符有更高的优先级的方法:
%left GE LE EQ NE ‘>’ ‘<‘
%left ‘+’ ‘-‘
%left ‘*’
%nonassoc UMINUS
%nonassoc 的含义是没有结合性。它一般与 %prec 结合使用表示该操作有同样的优先级。如:
expr: ‘-‘ expr %prec UMINUS { $$ = node(UMINUS, 1, $2); }
表示该操作的优先级与 UMINUS 相同,在上面的定义中,UMINUS 的优先级高于其他操作符,所以该操作的优先级也高于其他操作符计算。

规则部分

规则部分很象 BNF 语法。
规则中目标或非终端符放在左边,后跟一个冒号(:),然后是产生式的右边,之后是对应的动作(用{}包含)。如:
%token INTEGER
%%
program: program expr '\n' { printf("%d\n", $2); } 

;
expr: INTEGER { $$ = $1; }  
     | expr '+' expr { $$ = $1 + $3; } 
     | expr '-' expr { $$ = $1 - $3; } 
;
%%
int yyerror(char *s) 
{ 
     fprintf(stderr, "%s\n", s); 
     return 0; 
}

其中,$1 表示右边的第一个标记的值,$2 表示右边的第二个标记的值,依次类推。$$ 表示规约后的值。

第三部分

该部分是函数部分。当 yacc 解析出错时,会调用函数 yyerror(),用户可自定义函数的实现。

main 函数是调用 yacc 解析入口函数 yyparse()。如:

int main(void) 
{ 
     yyparse(); 
     return 0; 
}

递归的处理

递归处理有左递归和右递归。
左递归形式:
list: item
| list ‘,’ item;
右递归形式:
list: item
| item ‘,’ list
使用右递归时,所有的项都压入堆栈里,才开始规约;而使用左递归的话,同一时刻不会有超过三个项在堆栈里。
If-Else的冲突
当有两个 IF 一个 ELSE 时,该 ELSE 和哪个 IF 匹配是一个问题。有两种匹配方法:与第一个匹配和与第二匹配。现代程序语言都让 ELSE 与最近的 IF 匹配,这也是 yacc 的缺省行为。
虽然 yacc 行为正确,但为避免警告,可以给 IF-ELSE 语句比 IF 语句更高的优先级:
%nonassoc IFX
%nonassoc ELSE
stmt: IF expr stmt %prec IFX
| IF expr stmt ELSE stmt
出错处理
当 yacc 解析出错时,缺省的行为是调用函数 yyerror(),然后从 yylex 返回一个值。一个更友好的方法是忽略一段错误输入流,继续开始扫描。这里要涉及到 YACC 中错误保留字 error 的应用。
Yacc源程序的风格
建议按照如下风格来写:
(1)终端符名全部用大写字母,非终端符全部用小写字母;
(2)把语法规则和语义动作放在不同的行;
(3)把左部相同的规则写在一起,左部只写一次,而后面所有规则都写在竖线“|”之后;
(4)把分号“;”放在规则最后,独占一行;
(5)用制表符来对齐规则和动作。
语法分析中的错误处理
当进行语法分析时发现输入串有语法错误,最好能在报告出错信息以后继续进行语法分析,以便发现更多的错误。
Yacc 处理错误的方法是:当发现语法错误时,yacc 丢掉那些导致错误的符号适当调整状态栈。然后从出错处的后一个符号处或跳过若干符号直到遇到用户指定的某个符号时开始继续分析。Yacc 内部有一个保留的终结符 error,把它写在某个产生式的右部,则Yacc就认为这个地方可能发生错误,当语法分析的确在这里发生错误时,Yacc 就用上面介绍的方法处理,如果没有用到 error的产生式,则 Yacc打印出 “Syntax error”,就终止语法分析。
下面看两个使用 error 的简单例子:
1.下面的产生式
stat: error
;
使 yacc 在分析 stat 推导出的句型时,遇到语法错误时跳过出错的部分,继续分析(也会打印语法错误信息)
2.下面的产生式
stat: error ‘;’
;
使 yacc 碰到语法错时,跳过输入串直到碰到下一个分号才继续开始语法分析。
嵌入式动作
对于语法分析程序中的每一个语法规则,都有相应的 C/C++ 语句来做一些额外的处理,这个额外的处理就是语法动作。不过语法动作和词法动作的不同之处在于,语法动作允许嵌入式的语法动作,而词法动作不行。
尽管 yacc 的语法分析技术只允许动作在规则的末端,但 yacc 可以自动模拟嵌入在规则内部的动作。如果在规则内部写入一个动作,yacc 就会创造一个右侧为空并且左边是自动生成的名字规则,使得嵌入的动作进高规则的动作里去,用自动成成的名字代替最初的规则内的动作。
例如: 下面的句子是等价的
thing : A {printf(“I am A”) ;} B
thing : A fakename B;
fakename : {printf(“I am A”);}
这种方式将A值作为 $1,  规则末端的动作可将嵌入式动作的值作为 $2 ,B 的值为 $3.
Example:
//L文件:
%{
#include "FIRST_TA.H"
#include <stdio.h>
#include <stdlib.h>
%}
%%
a    {return A_STATE;}
b    {return B_STATE;}
c     {return C_STATE;}
not   {return NOT;}
%%

//Y文件:
%{
#include <stdio.h>
#include <stdlib.h>
%}
%token    A_STATE B_STATE C_STATE NOT
%%

program :    
    A_STATE B_STATE {
        int c, d;
        c = 20;
        d = 25;
    }
      c_state_not  {
            int e,f;
            e = 30;
            f = 35;
        }
    |
    A_STATE B_STATE  {
            int a, b;
            a = 10;
            b = 15;
    }
    c_state_not : C_STATE NOT{}
%%

输入文件的字符:a, b, c, f, c, not

NumPy 部分操作的理解

  • 矩阵操作中 axis = -1,相当与对矩阵最后一维进行操作
  • NumPy 的 ndarray 进行算术运算符操作是 element-wise 的,即是逐元素进行操作
  • np.mean 等操作中的 keepdim 选项,将平均掉的那一维保留,但是长度只为 1

Reference:

  • http://www.scipy-lectures.org/intro/numpy/operations.html