前言
- 最近在调试软件时,碰到了一个比较【迷惑】的标志位问题,一个32位的mask,软件调试,发现获取的值不对
- 问题发出去【求助】的瞬间,我顺便看了一下这个文件的所在目录,对比了下 Linux目录下的文件,【豁然开朗】了
-
fcntl.h
,我问了一个问题,随后发现这个问题说明我【基础知识】不好,这里其实是【八进制】数值表示方法,只是编程中接触的少而已。 - 注意:这里是【八进制】,C语言基础啊,我一时竟没有看出来,记录下来,以此勉励。
问题描述
- 环境:RT-Thread 4.1.0
- vconsole 软件包:用于终端切换
- 问题:console shell 终端切换后,发现无法正常的输入
- RT-Thread Studio gcc 环境验证:问题必现
- Keil MDK5 arm_clang 环境验证:问题必现
- 替换RT-Thread 内核,发现是一个【RT_USING_POSIX_STDIO】导致的,不开启这个宏,是正常的,开启这个【RT_USING_POSIX_STDIO】,发现了异常
- 经过软件排查,发现切换后,有个软件逻辑一直无法跑通
调试流程
- 软件调试:在这个函数打上断点后,控制台随便按个键盘按键,发现能触发【接收回调】,问题就是:这个【等待队列】是空的,没有唤醒【shell 进程】
static rt_err_t fops_rx_ind(rt_device_t dev, rt_size_t size)
{
rt_wqueue_wakeup(&(dev->wait_queue), (void*)POLLIN);
return RT_EOK;
}
- 开机不切换串口,这个唤醒后,也进入这个【接收回调】函数,并且整个【等待队列】不为空,唤醒等待队列,就触发【shell 线程】正常读取,shell 功能正常
切换逻辑
- 切换时,需要设置一个【标志位】,有了这个标志位,【shell 终端对象】读数据时,就不再:【无限等待】,而这个标志位没有置位
- 软件调试发现一个【奇怪的问题】:标志位跟自己想的不一样
static int _fops_ioctl(struct dfs_fd *fd, int cmd, void *args)
{
rt_device_t device;
int flags = (int)(rt_base_t)args;
int mask = O_NONBLOCK | O_APPEND;
device = (rt_device_t)fd->data;
switch (cmd)
{
case FIONREAD:
break;
case FIONWRITE:
break;
case F_SETFL:
flags &= mask;
fd->flags &= ~mask;
fd->flags |= flags;
break;
}
return rt_device_control(device, cmd, args);
}
#define O_NONBLOCK 04000
#define O_APPEND 02000
- 这个:
int mask = O_NONBLOCK | O_APPEND;
获取的,不是:0x6000,而是:0xC00。 - 这个 04000 ,有个 0 前导,就是 【八进制】
- 也就是这里的 O_NONBLOCK 04000,没有
0x
前导,是个【八进制】数,十六进制为:0x800
-
O_APPEND 02000
转换为十六进制: 0x400
-
0x800 | 0x400 确实为: 0xc00
基础知识
- 【八进制】用的比较少,可能思维里按【十六进制】处理,会造成困扰
- 这个基础知识,还是要多看,软件编程会涉及多方面的知识,要多积累
问题解决
- 其实就是上面:vconsole.c 中的:
_fops_ioctl
没有处理:F_SETFL
,导致fd->flags
没有正确的设置上,从而接收回调【唤醒后】,让【shell 线程】去读取【输入的字符】,导致又进入了【wait forever】,从而【shell 线程】死等在【老的终端】,新切换的终端,等待队列为空,导致【shell 线程】一直挂起。
小结
- 注意【八进制】的表示方法:数字
0
作为前导,而不是字母O
。八进制的表示方法用的不多,但不能误当成【十进制】,也不能误当成【十六进制】 - Linux系统中采用三位八进制的数字来表示文件的操作权限,方便和用户组对应容易看。这个知识点后面深入看一下。
-
O_NONBLOCK
O_APPEND
这种,一般用于:文件的操作,是文件的属性