位掩码 (权限模型、组合标志)

Published Tue 22 August 2017 in 软件工程

by HanXiao  


位掩码就是一批不重复、只有一位是 1 其它位都是 0 的二进制位, 如 0001、0010、0100、1000, 这就是一组位掩码.

使用 GoLang 的 const 与 iota 可以很方便的生成一组位掩码:

const (
  Noob           = 1 << iota // 1 << 0 = 000000001 = 1
  Hipster                    // 1 << 1 = 000000010 = 2
  UnixWizard                 // 1 << 2 = 000000100 = 4
  StartupFounder             // 1 << 3 = 000001000 = 8
)

例用位运算的 & (与) 和 | (或), 位掩码可以用来实现权限模型组合标志等功能.

权限模型

Linux 中的用户权限也是类似如此实现的, 原理是利用 & 运算, 只有待运算的两位同时为 1 时, 运算的结果才为 1, 否则为 0.

权限

如图所示, 假设这是四个通道, 要想通过通道, 就要拥有对应通道的权限. 一种设计方案是用二进制的 0 和 1 来控制权限, 0 表示没有权限, 1 表示有权限, 那么:

  • 第一个通道的权限就是 1 0 0 0
  • 第二个通道的权限就是 0 1 0 0
  • 第三个通道的权限就是 0 0 1 0
  • 第四个通道的权限就是 0 0 0 1

好, 现在通道的权限设置好了, 用户的权限也是用四个二进制来表示, 用户想要通过第一个通道, 那他的第一位二进制就要为 1, 其他位不重要 (因为通道权限的后三位是 0, 所以不管用户权限的后三位是什么, & 运算的结果都是 0).

当验证时, 拿用户的二进制权限数据和通道对应的二进制权限数据进行 & 运算, 得到的结果如果不为 0, 则表示用户拥有通过这个通道的权限, 如果为 0, 则没有对应的权限.

例如, 此时有一个用户想要通过第一个通道, 其拥有的权限是 1 0 0 0.

权限

OK! 验证通过.

如果其拥有的权限是 0 1 0 0.

权限

很遗憾, 验证结果为 0, 验证通过失败.

这是用户单一权限的情况, 如果用户想要多个权限, 比如拥有通过第 1、2 两个通道的权限, 其用户权限该如何表达? 那么可以把两个单一权限进行 | 运算, 比如 1000 | 0100 = 1100. 这就是组合标志.

组合标志

组合标志的一种运用是在权限模型中设置用户权限. 另外在很多系统或语言提供的 API 中也会用到组合标志, 例如 C++ API CreateFile, 它的一个参数就是设置打开文件的标志: GENERIC_READ | GENERIC_WRITE

其原理是利用 | 运算, 待运算的两位中只要有一个是 1, 运算的结果就是 1, 又由于同一批位掩码互不重复、并且只有一位是 1, 所以对位掩码进行 | 运算能得到一个唯一的标志. 例如 1100 只可能是 10000100 进行 | 运算得来.