对 jiffies 溢出、回绕及 time_after 宏的理解

时隔数年之后,打算重写这一篇文章,虽然这几年并没有机会继续深入了解 Linux 系统,但是陆续熟悉了 μC/OS、FreeRtos 等实时操作系统。对 C 的理解也有所长进。闲话少说,但是在开始正文之前,请读者们打开计算器,切换到程序员模式,方便查看二进制数😄。


Linux 内核源码里用 jiffies 这个变量来记录系统启动以来所经历的 tick 数,timeout、delay、sleep 之类的实现会依赖于它。它的类型是 unsigned long,其每毫秒自加 1,假设它是 u32(无符号32位),那么约 49.71 天后会溢出。题外话是,现在 64 位操作系统已是主流,所以 unsigned long 很可能要比 u32 大,但这并不会导致计算逻辑发生变化

那么当这个数发生溢出回绕时,程序对时间的判断是不是会发生问题?我们知道一年到头不关机的服务器多的是。所以一个靠谱的操作系统,一定是在这个数产生溢出时也不会出现问题的,所以我们是不是要写几个 if else 来应对这种情况?答案是,并不需要,在 Linux 源码中有这样一个宏用来应对这一情况,而且相当简单:

1
2
3
4
5
6
7
#define time_after(a,b)\
(typecheck(unsigned long,a) &&\
typecheck(unsigned long,b) &&\
((long)(b)-(long)(a)<0) // 当 a 在 b 的后面(大于等于),此宏为 true


// timeout 为超时时间点,jiffies >= timeout 代表超时,宏结果为 true
time_after(jiffies,timeout);

Read More

Hexo 一键部署时添加 README.md

hexo d -g 一键部署时,README.md 会被删掉。对于项目下必须有 README 的强迫症患者会比较纠结。 我的解决方法是,在博客源码根目录新建一个脚本文件,随便起个名比如 deploy,内容如下。

1
2
3
4
hexo g
echo Copy README.md
cp ./README.bak ./public/README.md
hexo d

这是 Git Bash 的语法,如果用的是 Windows cmd 则需要酌情修改。

然后把提前准备好的 README.md 也放在根目录下,重命名为 README.bak(与脚本命令对应)。之后通过在 Git Bash 中运行 ./deploy 来进行一键部署。

原因是执行 hexo d 时会完全拷贝 public 下的文件到 .deploy 下,同时删除多余的文件,所以提前将 README 放到 .deploy 下是不行的。而执行 hexo g 时,public 文件夹下的文件会被完全更新,同样会删除多余文件,所以也不能提前放在 public 下。理论上应该放在 source 文件夹里,但是 .md 文件会被渲染成 html 文件,所以也不行。而在 hexo g 之后将 README 拷贝到 public 下则是一个可行的方法。

Windows7 修复 UEFI 分区

想来会进到这篇帖子的同志应该不需要我普及关于这个 efi 启动分区的基础常识了。关于我是怎么把该分区搞坏的我也就不提了。 那么直接进入正题,我使用的修复工具是一个烧了 windows 官方镜像的 U 盘,我比较喜欢这一方式,因为做 PE 盘也挺麻烦,而且可能要用到第三方的工具,心里没底。

Read More

VC窗口程序闪烁的问题解决

这里介绍一下 win32 C api 写的 windows 程序在调整窗口大小时闪烁严重的问题的解决方法,纯属个人经验,有不完善或不恰当之处欢迎指正。

首先,主窗口需要设置 WS_CLIPCHILDREN 风格,即子窗体裁剪,也就是说,子窗体(控件)覆盖的区域,由子窗体自行重绘,父窗体不管。这时你会发现有控件的地方都不闪了。

然后,截取 WM_ERASEBKGND 消息,这里是填充背景的地方,也就是以背景色填充擦除掉杂色,自己进行处理,不该擦的地方就别擦。那该擦的地方自己怎么来擦呢?看例程

1
2
3
4
//hBKBrush = CreateSolidBrush(GetSysColor(COLOR_3DFACE));
hBKBrush = GetSysColorBrush(COLOR_3DFACE);
FillRect(hdc, &rect, hBKBrush);
//DeleteObject(hBKBrush);

简单说就是 Get 到系统颜色,然后画上去就是了。 如果窗体内有 Group box 控件,记得设置 WS_EX_TRANSPARENT 属性,不然会有很奇怪的现象。

如果只是单纯的想更改背景的颜色可以这么写:

1
wcex.hbrBackground = (HBRUSH)(COLOR_3DFACE+1);

LINK : fatal error C1047 解决方法

使用静态库的时候出现的问题,根据错误描述,怀疑是lib不兼容,且我的lib是VS2005编写的,而引入lib却是在VS2008下。根据MSDN的提示,似乎只能把lib的工程转换为VS2008的工程并重新编译才能解决,但仔细思索之后感觉这个现象实在不符合逻辑,向下兼容是这种工具最基本的要求。

进行了一些尝试,解决方法如下: 打开lib的工程,依旧使用VS2005,在Release模式下:project属性->General->Project Defaults分栏下的 Whole Program Optimization 由 Use Link Time Code Generation 改为No Whole Program Optimization 即可。

个人理解,如果静态库进行了优化,那么引入它的工程就无法获得完整的信息,于是有出错的可能(特别是又跨了版本),而静态库尽量保存完整的信息,优化交给引入的工程来做,如此也比较符合逻辑。欢迎大家斧正。

PS:如果lib的工程自己没有,那么在引入它的工程进行上述的设置也能避掉错误,但似乎程序就完全没有优化了,似乎不妥。

补充:在引入 lib 的工程编译到 link 阶段,出现 warning LNK4099: PDB ‘vc80.pdb’ was not found 警告,解决方法是,打开 lib 工程,在 project属性->General->Debug Information Format 项设置为 C7 Compatible (/Z7)

NTFS 文件夹权限修改

将某个文件夹或文件的所有权限开放给所有用户

1
2
3
4
SECURITY_DESCRIPTOR sd;
InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION); // 初始化
SetSecurityDescriptorDacl(&sd, TRUE, NULL, FALSE); // NULL 代表允许所有请求
SetFileSecurity(name, DACL_SECURITY_INFORMATION, &sd); // name 可以是文件名也可以是目录名

SECURITY_ATTRIBUTES 设置低权限

Windows 从 Vista 开始重新修改了其系统的权限管理机制,于是现在就会碰到一些 xp 能过而 win7 不能过的代码。比如 Service 程序和一般应用程序用共享内存的方式来通讯,CreateFileMapping 的第二个参数我们往往都是填 NULL 使用默认权限(Service 的默认权限自然是 Service 级别的啦),于是在 Vista 以上的系统中运行于 user 权限应用程序打不开这一段共享内存。

网上很多帖子都说在遇到这种问题的时候让应用程序请求管理员权限,这个办法是能解决问题的,但是,我们很多时候是不希望程序获得管理员权限的,比如一台公用的PC,我们可能希望除了真正的管理员以外的用户都不知道管理员密码,以方便管理。于是在运行时会请求管理员权限的程序,都不能够使用了,只有管理员自己能用,这样的程序。。。挺二的,于是我们必须让程序在 user 权限下就完成所有工作。

解决方法是 Service 以较低的权限来创建共享内存,user 应用程序便能直接打开。以下是 Service 端的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
HANDLE hMapFile;
unsigned char *pBuf;
SECURITY_ATTRIBUTES SecAttr;
SECURITY_DESCRIPTOR SecDesc;

SecAttr.nLength = sizeof(SecAttr);
SecAttr.bInheritHandle = FALSE;
SecAttr.lpSecurityDescriptor = &SecDesc;

InitializeSecurityDescriptor(&SecDesc, SECURITY_DESCRIPTOR_REVISION);
SetSecurityDescriptorDacl(&SecDesc, TRUE, 0, FALSE);

hMapFile = CreateFileMapping(INVALID_HANDLE_VALUE, &SecAttr, PAGE_READWRITE, 0, BUF_SIZE, MAPPING_NAME);
if(hMapFile == NULL)
{
return GetLastError();
}

而应用程序端可以使用 OpenFileMapping()CreateFileMapping() 打开共享内存。

VC 的 ASCII 和 Unicode 转换相关的函数

ASCII 和 Unicode 的互相转换,有两种写法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
size_t wchar2char(wchar_t* tchSrc, char *chDst)
{
size_t len = wcslen(tchSrc);
len = wcstombs(chDst, tchSrc, len+1); //转换成功返回为非负值
return len;
}
//mbstowcs


int ascii2Unicode (char* Src, wchar_t * Dst)
{

int n;
n = MultiByteToWideChar(CP_ACP, 0, Src, -1, NULL, 0);
MultiByteToWideChar(CP_ACP, 0, Src, -1, (LPWSTR)Dst, n);
return n;
}
int Unicode2Ascii (wchar_t* Src, char* Dst)
{

int n;
n = WideCharToMultiByte(CP_ACP, 0, Src, -1, NULL, 0,0,0);
WideCharToMultiByte(CP_ACP, 0, Src, -1, Dst, n, 0, 0);
return n;
}