浅谈字体

之前使用 Matplotlib 画图的时候发现不能正常显示中文。在解决问题的过程中了解了一下字体显示的一些知识,在这里记录一下。 一、编码与字体 我们知道,为了让字符能够存储在计算机中,我们为每个字符分配对应的数码。这种人为约定称为字符编码。常见的编码包括 ASCII、GBK、Unicode 等等。字符编码是字符的数据表示。 然而,仅有编码依然不能在计算机中显示字符。因为符号是一种图形,若我们不能确定字符所对应的图形的样式,那么我们就无法在屏幕中看到字符。同样的,这种字符到对应图形的约定称为字形,字形的集合即字体。字体是字符的图像表示。 于是,这里我们就有了两种不同的映射关系。一种是字符到字符编码的映射关系,这一关系确定了字符的存储方式;另一种是字符到字体的映射关系,这一关系确定了字符的显示方式。 因此,简单来说,计算机显示字符的过程就是将编码数据转换为字形图像的过程。 二、编码的分裂和统一 然而这一过程并不真的那么简单。原因之一便是从计算机技术早期遗留下来的一系列互不兼容的编码方式。 在互联网还未诞生的时候,各个国家和地区为了在计算机中显示自己的文字,各自开发出自己的字符编码方式,例如简体中文的 GB2312、繁体中文的 Big5、日文的 Shift_JIS 等等。然而在互联网的国际化场景下,不同的编码方式带来了交流上的困难。 所以这时候 Unicode 标准应运而生。Unicode 由统一码联盟推动,旨在使用 Unicode 取代现存的字符编码。该标准整理并编码了世界上大部分的文字系统,使得电脑能以通用划一的字符集来处理和显示文字。 然而 windows 下的默认编码方式还是 gbk。 Unicode 编码空间区间为 $[0, 17 \times 2^{16})$,但目前只使用了其中很少一部分。该编码空间被划分为了 17 个平面,其中 4-13 号均未使用。其他平面的主要内容为: 0 号平面(0x0000-0x​FFFF)称为基本多文种平面,包含了几乎所有现代语言的字符。 1 号平面(0x10000-0x1FFFF)称为多文种补充平面,包括了绝大多数古代文字,现时已不再使用或很少使用的符号等。 2、3 号平面(0x20000-0x​2FFFF、0x30000-0x​3FFFF)称为补充表意文字平面,用于中日韩统一表意文字中未被包含在早期编码标准中的文字。 14 号平面(0xE0000-0xEFFFF)为特别用途补充平面。 15、16 号平面(0xF0000-0x​10FFFF)为私人使用平面。(例如苹果公司的图标字符) Unicode 虽然制定了统一的字符编码方式,但是直接使用 Unicode 编码进行存储的效率并不高。因此需要对 Unicode 进行进一步编码,这就带来了 UTF(Unicode Transformation Format),包括 UTF-32、UTF-16 和 UTF-8。 UTF-32 使用定长的 32 位对 Unicode 进行编码。根据 Unicode 的编码空间我们知道,最多只需要 21 位即可表示所有编码。因此 UTF-32 中的 11 位始终为 0。这使得 UTF-32 的空间效率很低。所以这种编码方式主要用在系统的内部 API 中。...

五月 5, 2024 · 2 分钟 · 255 字 · Wokron

不务正业:玩个 zsh

一、前言 不知道为什么,这学期看到好几个人都用 zsh。最近恰好有时间,我也是时候换一个 shell 了。 不过嘛,shell 毕竟是用来工作的,不是一件艺术品,所以这里的配置也是以实用为主了。本文也没有用到 oh-my-zsh,用一个专门的框架来管理 shell 的配置在我看来还是太过沉重了。 本篇文章所用的环境当然是 linux。更加具体来说是 debian 系的发行版。 二、zsh 的安装 这里当然不会仔细介绍 zsh,将网上许多其他文章都重复过的内容再重复一遍。zsh 只是一个 shell,只不过这个 shell 有较强的可扩展性,同时具有一些比较有用的特性,仅此而已。 安装 zsh,只需要一条命令 apt-get install zsh 之后可以查看 /etc/shells 文件的内容,该文件记录了系统中已有的所有 shell,此时应当也有 zsh。 cat /etc/shells 本人的输出结果是这样的 # /etc/shells: valid login shells /bin/sh /bin/bash /usr/bin/bash /bin/rbash /usr/bin/rbash /usr/bin/sh /bin/dash /usr/bin/dash /bin/zsh /usr/bin/zsh 之后首次运行 zsh zsh 应该会出现如下内容,因为此时我们并未创建 zsh 的配置文件。正如 bash 的 .bashrc。这里只需要选择 0 即可。之后会在用户目录创建一个空的 .zshrc,该文件将在下一小节中详细说明。选择后重新输入命令 zsh 进入 zsh,此时应该可以看到输入提示符等内容。 This is the Z Shell configuration function for new users, zsh-newuser-install....

十月 21, 2023 · 4 分钟 · 663 字 · Wokron

Linux-Mint双系统的安装及美化

一、前言 我的笔记本现在用起来很慢了。每次听到它嗡嗡的风扇声却又看不到它跑不出来结果的时候,就感觉它好似一头老驴,使劲却又力不从心,腿打着颤却也拉不动身后的货。所以我谋划着新买一台,就让之前的那台好好休息吧。 一想到买台新的笔记本,我的思绪就沿着这条道一直走下去:“要买什么样的配置呢?买了新的电脑要做些什么项目呢?要玩什么游戏呢?” 诸如此类。装一个 linux 系统也是这时产生的想法。有人可能会想,“这有什么用呢?难道 windows 就不能用吗?如果不得不用 linux,wsl 也是很好地办法,或者用虚拟机,甚至直接用 docker,都可以解决。” 确实,如果就满足当下的使用而言,将 linux 作为一个个人使用的真正的系统,相对于 windows 似乎并没有什么优点。 不过呢,我选择折腾这么一阵也并没有什么经过考量的理由,而仅仅是因为自己在主观上更加喜欢 linux 罢了。在我不算太长的接触并学习 linux 的时间里,我从这个系统中感受到了设计的一致性,这是我在更长时间的对 windows 的接触和学习中所没有体会到的。当然,或许在之后看来,我现在的理解也不过是浅薄的认识罢了。但是现在,我还是决定安装一个 linux 系统。 如果有读者的话,希望不要嫌弃我太过啰嗦(笑)。 二、为什么是薄荷 众所周知,linux 是内核,许多不同的组织在 linux 内核的基础上增加了其他必要的软件和应用,开发了不同的发行版。发行版的江湖中帮派林立,主要有三大派系,debian 系、redhat 系和 suse 系。各个派系中又有无数相互关联却又相互区分的发行版。如 debian 的 ubuntu、deepin;redhat 的 fedora、suse 的 opensuse。只需要把这些发行版的大名亮出来,就足以让人眼花缭乱了。 本人也在这些发行版中漂移不定了一段时间,但最终选择了 debian 系的 mint(薄荷)。主要有一下几个原因 debian 系有着 apt 的超级牛力加持,.deb 格式的软件包使用作为广泛 mint 基于 ubuntu,ubuntu 是使用最为广泛的 linux 发行版 mint 精简了 ubuntu 下的一些功能,如 snap;并对初学者较为友好 三、安装操作系统 我新买的电脑是联想拯救者 R9000P,配置如下 设备 配置 处理器 AMD Ryzen 9 7945HX 内存 16G 硬盘 1T 显卡 Nvidia 4060 要安装的操作系统配置...

六月 24, 2023 · 2 分钟 · 386 字 · Wokron

系统编程之线程管理

一、Linux 多线程简述 进程和线程的关系老生常谈。线程是最小的调度单位,进程是最小的资源分配单位。同一进程中的多个线程是在共享的内存空间中并发的多道执行路径,它们共享一个进程的资源。 对于Linux来说,Linux线程属于用户级线程,即线程的调度是在用户空间执行的。也就是说,Linux线程的实现是在内核之外的,多线程的概念对于内核来说并不是真实存在的,而只是通过线程库中的程序模拟的并发效果。 Linux线程遵循POSIX线程接口,称为pthread。pthread在其他平台也有对应的实现,如在windows。 二、线程操作 (1)库的使用 在开始多线程编程之前,需要说明一下 pthread.h 库。在编译使用pthread.h库的代码时,一般需要加-lpthread。pthread在glibc2.34之前是在glibc里面的,之后分出来变成一个单独的库,因此有的情况下,不加-lpthread也能编译成功。 (2)基本操作 创建线程 int pthread_create(pthread_t _Nullable * _Nonnull __restrict, const pthread_attr_t * _Nullable __restrict, void * _Nullable (* _Nonnull)(void * _Nullable), void * _Nullable __restrict); 该函数中第一个参数为指向一个线程标识变量的指针。第二个参数用来手动设置线程的各项属性,一般可以用NULL选择默认属性。第三个参数为一个函数指针,表示新建线程时需要执行的函数。注意该函数的参数类型和返回值类型,使用时需要进行强制类型转换。第四个参数为传递给函数的参数,也就是线程执行的函数的参数。不传递参数时可设置为NULL。 如下举一个创建线程的例子。 pthread_t tid; if (pthread_create(&tid, NULL, do_something, NULL)) { // error handler } 线程退出 void pthread_exit(void *ral_ptr); 当某一线程执行该函数时,会导致该线程结束。结束时会将ral_ptr指针传递给pthread_join 函数的 rval_ptr 线程取消 int pthread_cancel(pthread_t tid); 某一线程调用该函数,可以终止同一进程内的其他线程。 tid 即要终止的线程。 线程挂起 int pthread_join(pthread_t thread, void **rval_ptr); 某一线程调用该函数会阻塞该线程,直到参数 thread 所指示的线程退出。第二个参数为一个指向 pthread_exit 所设置的 ral_ptr 指针的指针。...

十二月 22, 2022 · 4 分钟 · 745 字 · Wokron

系统编程之进程间通信

一、进程间通信简述 进程是程序的一次运行的动态过程,为了完成一个任务,很多进程之间需要进行通信,从而相互合作以实现需要的功能。操作系统内核中提供了进程间通信的方法,主要有以下几种: 管道: 管道是最基本的进程通信机制,可以想象成一个管道,两端分别连着 2 个进程,一个进程往里面写,一个进程从里面读。如果读或写管道的时候没有内容可供读或写,进程将被阻塞,直到有内容可供读写为止。 消息队列: 消息队列本质上在内核空间中开辟了一块内存空间,这块内存是其他进程可以访问到的,在其中使用链表的方式实现了一个队列,进程可以向该队列中发送数据块或读取数据块,从而达到进程间通信的目的。其中每个数据块包含两部分,首先是一个类型为 long 的 type,然后是具体的数据,数据块的 type 可以作为进程之间相互约定好的协议。例如一个进程发送 type 为123的消息,另一个进程接收 type 为123 的消息,后者便可确认这就是前者发送的信息,并信任该数据块中的数据。 信号量: 不同进程之间存在对资源的竞争,信号量就是用来标明可用资源的数量的数据结构,本质是为了实现多个进程之间的同步。需要注意,信号量(semaphore)与 “信号”(signal)没有关系。 共享内存: 共享内存的本质就是把两个或多个进程的虚拟地址映射到同一块物理内存。这样,一个进程通过对这块内存的读写就能被其他进程访问到,从而实现进程间通信的功能。 二、进程间通信操作 (1)准备操作 获取 key #include <sys/types.h> #include <sys/ipc.h> key_t ftok( const char * fname, int id ); 共享内存,消息队列,信号量等进程间通信方式都需要寻找一个中间介质来进行通信。不同的介质需要用不同的信息来进行区分,这就是进程间通信的 key。ftok() 函数就可以生成一个唯一的 key,该函数获取一个文件路径和一个字序号,生成一个用于区分的 key。 注意,选择文件路径只是因为文件的编号是独有的。设置的文件路径与代码和程序并没有什么关系。 命令管理进程间通信 若没有调用控制函数进行删除,则已分配的进程间通信不会自动释放。如果共享内存,消息队列,信号量在新进程执行时依旧有之前残留的信息,可能导致程序运行结果错误。可以通过 ipcs 和 ipcrm 命令进行管理。 ipcs # 显示所有进程间通信信息 ipcrm -q MsgID # 删除消息队列 ipcrm -s SemID # 删除信号量 ipcrm -m ShrID # 删除共享内存 (2)消息队列 消息队列获取 #include <sys/types....

十一月 16, 2022 · 5 分钟 · 962 字 · Wokron

系统编程之信号及信号处理

一、信号简介 (1)信号含义 软中断信号(signal,又简称为信号)用来通知进程发生了异步事件。在软件层次上是对中断机制的一种模拟;在原理上,一个进程收到一个信号与处理器收到一个中断请求可以说是一样的。信号是进程间通信机制中唯一的异步通信机制,一个进程不必通过任何操作来等待信号的到达,事实上,进程也不知道信号到底什么时候到达。进程之间可以互相通过系统调用 kill 发送软中断信号。内核也可以因为内部事件而给进程发送信号,通知进程发生了某个事件。信号机制除了基本通知功能外,还可以传递附加信息。 (2)信号分类 可以使用kill -l命令查看当前系统支持的所有信号: 信号值小于 SIGRTMIN(<=34)的信号都是不可靠信号。它的主要问题是信号可能丢失。 信号值位于 SIGRTMIN 和 SIGRTMAX 之间的信号都是可靠信号,这些信号支持排队,不会丢失。 (3)信号的产生 信号可以由一下几种方式产生: 键盘事件:ctrl+c ctrl+\ ctrl+Z 等 非法内存:如果内存管理出错,系统就会发送一个信号进行处理 硬件检测到异常:如段错误,除 0,总线错误等 环境切换:比如说从用户态切换到其他态,状态的改变也会发送一个信号,这个信号会告知给系统 系统调用:如调用kill,raise,sigsend ,sigqueue函数等 (4)信号处理 进程可以通过三种方式响应信号: 接受默认处理 忽略信号(某些信号不能被忽略,如 SIGKILL 和 SIGSTOP) 捕捉信号并执行信号处理程序 二、信号操作 (1)信号发送 系统调用中用于发送信号的函数有 kill() raise() abort() 等。 kill() 函数 #include <signal.h> int kill(pid_t pid, int sig); //第一个参数pid代表接受信号的进程PID,第二个参数代表要发送的信号 参数 pid 会影响 kill()函数的作用,取值分为以下四种情况 若 pid>0,则发送信号 sig 给进程号为 pid 的进程。 若 pid=0,则发送信号 sig 给当前进程所属进程组的所有进程。 若 pid=-1,则发送信号 sig 给除 1 号进程和当前进程外的所有进程。 若 pid<-1,则发送信号 sig 给属于进程组 pid 的所有进程。 segqueue() 函数 sigqueue()函数支持发送信号的同时传递参数,需要配合 sigaction() 函数一起使用。...

十一月 13, 2022 · 3 分钟 · 584 字 · Wokron