问题背景

要做一个需求,大概是检测到某输入重启,于是写一个demo调试一下

c语言程序,交叉编译后在adb shell下运行

思路

用 am 命令直接重启

我们先手动验证一下,发现这个设备不支持am命令吗,遂排除

用 kill 命令杀掉进程,然后重新运行

  1. 写个demo,这个程序会循环等待输入,按q退出
void test_option()
{ } int main(int argc, char **argv)
{
int ret;
char *sptr = NULL;
while(1)
{
sptr = input_fgets("Please input option: \n"); if(sptr[0] == 'q')
return 0; else if (atoi(sptr) == 1)
test_option();
}
return 0;
}
  1. 另开一个cmd窗口,打开adb shell,查询进程号并杀掉重开



    可以看到,通过另一个adb窗口可以很轻易地kill掉进程并重启

    那么 直接用system函数写入命令行执行不就完事了?? ,我也是这么认为的。。。但是出问题了!!!

  • 问题1: 我手动执行ps看进程号,但是写程序获得进程号呢?

    ps | grep nwy_data_test | awk '{print $1}' | head -n 1

    这句命令表示取出ps结果的第一列的第一行并打印!也就是上图中的22122

  • 问题2:取到22122了,怎么传给命令行呢?

一种更简单的方法是判断输入为1 直接system(killall -9 rst_test && /data/rst_test)


发现会无限循环执行!!!





3. 经过我的调试!!!发现问题出在input_fgets函数!!!

现在已知:手动kill并重启 无论怎样都没问题;写在system里,重启的进程里带标准读取输入的都会出错!!

kill当前进程 重启一个不带input_fgets函数的情况下,会kill、会正常运行新程序

kill当前进程 重启一个带input_fgets函数的情况下,会kill、会忽略fgets的阻塞!!

也就是说 只要kill当前进程后,重启的进程带fgets这种获取命令行输入的都有错!!!

static inline char *input_fgets(char *msg, ...)
{
static char ptr[130] = { 0 };
va_list ap; va_start(ap, msg);
vprintf(msg, ap);
va_end(ap);
memset(ptr, 0, sizeof(ptr));
fgets(ptr, sizeof(ptr), stdin);
if(strlen(ptr) > 0) {
if('\n' == ptr[strlen(ptr) - 1]) {
ptr[strlen(ptr) - 1] = '\0';
}
}
return ptr;
}

4. 于是,把上面函数的vprintf注释掉,发现:可以正常kill,正常启动,但是fgets直接跳过了,并没有读取到我输入的1!!!!



5. 到这里其实基本已经真相大白了,也就是手动输入命令和system输入命令是有差异的!!!

  1. 通过查询资料,发现错误大概率是由于system的底层是通过fork创建一个子进程的,而子进程会关联到父进程的输入输出流,通过kill杀掉了父进程,就影响了正常的输入输出流!!!

  2. 为了验证我的猜想

7.1 先去掉了system里的kill命令,发现一切正常!

7.2 kill后启动另一个不带stdin的进程,发现一切正常!

7.3 kill后启动另一个带输入的进程,读取标准输入流失败!!

我并没有输入任何内容!!也就是说 标准输入流有无数个空 自动读取了!!!

7.4 几个细节

scanf之前不会发生这个错误,scanf之后才发生这个错误!!

 if (ferror(stdin)) {
printf("IO错误");
}

这就可以定位到,是scanf/fgets导致的错误,一般是因为stdin标准输入流中有问题!

用 exit 命令退出adb shell,然后重新开启

system是子进程,他执行exit只会退出他的shell 不会影响父进程!!!


  • 一个有效的办法是用kill -9 $$ 结束adb shell , 但是经过测试,无论是用sh脚本配置还是system("kill -9 $$"),都不会正确执行,原因在于$$是获取当前进程id,直接手动输入表示结束adb shell,如果在sh脚本执行表示结束当前脚本,在system执行则表示结束当前子进程!!!!!

根因分析

  • system命令类似于sh脚本,实际上也是在/bin/sh中写一个脚本并执行,往往是非交互式的脚本,这个过程是在调用处新开一个子进程完成的,恰好我执行的内容是杀掉父进程并启动一个新进程,父进程有用到标准io(获取输入1并执行) 直接把他干掉就导致stdin乱了!!!!!

直接kill/killall 会提示Terminated,加上-9 会提示Killed,这是因为kill指令是通过向目标进程发送相应信号 默认是15(会等待处理完在终止),9是强制杀死!!

最终方法

  1. 用killall -2 xxx ,相当于在前台程序时执行一个ctrl+c 但他实际上并不会杀掉父进程,因为kill -2十分温和,他会等到子进程结束在杀掉父进程,而子进程又想杀死父进程,父进程要等待子进程杀死自己再去死,就导致死不掉了。。。(有局限性,比如进程中有其他线程,ctrl+c不会退出线程!!)

  1. 用exec函数族实现,无论重启几次都是原来的进程号,但是会复位到初始状态!!!



kill -2也是一种可行的办法但实际上是覆盖,没关闭原来的进程,会导致资源浪费,exec函数是最根本的解决方法会取代!!!

总结

  1. system命令几乎等于写一个sh脚本并执行,区别是system命令是新开子进程,手动执行sh脚本可以新开一个shell执行!!

  2. 子进程中杀掉父进程是非常危险的行为,会导致很多未知错误,一般不要这样做!!!!

  3. 一些优雅的发送信号方式,ctrl+z暂停,fg继续,ctrl+S挂到后台 ctrl+Q恢复到前台!

  4. 进程间通讯的方式之一 管道通信 实现方式是popen pclose

  5. fork函数 exec函数族 还可以自定义io区等...很强

  6. 排查这个bug用了两天半,居然真的用上了操作系统的知识,爽歪歪!!!

记一次bug排除心得的更多相关文章

  1. 记Weblogic部署BUG(websocket)

    将含有websocket的SSM项目部署在Weblogic上面,遇到websocket报错如下 java.lang.ClassCastException: org.springframework.se ...

  2. 【windows核心编程】注入DLL时BUG排除与调试

    DLL注入排除bug的思路步骤. 1.在VS中监视输入err,hr检查DLL是否注入成功 2.OD断点loadlibraryW,loadlibraryA是否已经注入成功,eax是否有值. 3.检查路径 ...

  3. 记一次bug查找经历

    系统采用cell插件显示汇总数据,然后发现个公司数据显示不出来,接到这个任务开始查找bug. 通过需求了解并不知道其他公司什么情况,因为就这个公司有了反馈: 本来以为很容易找到点的,毕竟数据显示不出来 ...

  4. 记一个逻辑bug

    1     从数据库中找出一个学生能选的毕业设计(毕设的select or not 字段表示本题目是否已经被选 此时就按照其值为n来查询) 2     用户选择某个毕设后,先更新毕设表(select ...

  5. 谁记录了mysql error log中的超长信息(记pt-stalk一个bug的定位过程)

    [问题] 最近查看MySQL的error log文件时,发现有很多服务器的文件中有大量的如下日志,内容很长(大小在200K左右),从记录的内容看,并没有明显的异常信息. 有一台测试服务器也有类似的问题 ...

  6. 记一款bug管理系统(bugdone.cn)的开发过程(4) - 新增BugTalk功能

    测试人员提出一个Bug,如果开发人员对Bug有疑义,会直接面对面讨论或者通过QQ等线上聊天工具讨论,但过后再去找讨论记录会很麻烦.因此BugDone提出一个全新的概念:将问题的讨论留在问题内.BugD ...

  7. 记一款bug管理系统(bugdone.cn)的开发过程(3) - 永久免费化

    BugDone永久免费了! BugDone(bug管理工具)已经发布有一阵子了,自发布以来注册用户量.项目创建量稳步提升,并且得到了很多用户的好评. 在开发BugDone工具之前,我们团队也曾为找不到 ...

  8. 记一款bug管理系统(bugdone.cn)的开发过程(2) -如何做好登录界面

    一. 做了一个大胆的决定,官网首页便是登录界面 BugDone,Bug管理工具的定位就是一款非常易用的工具,所以我们没有像其它平台那样进官网首页都是一些功能和业务的介绍. 我们觉得方便用户快速进入工作 ...

  9. 记一款bug管理系统(bugdone.cn)的开发过程(1) -- 为什么要开发一款bug开发系统

    对于从事软件研发行业的同学来说bug管理系统肯定不陌生.本人03年左右开始正式成为一名码农,工作期间接触过若干bug管理系统,如JIRA等,不过都是自行部署在公司内网的. 几年过去了,现在已经是互联网 ...

  10. 记一个小bug的锅

    人生中的第一个线上bug 我参与的第一个项目就出现了.但是自己还觉得这锅也不全是自己的,毕竟那么明显的bug出现在历史模块中(不是我写的新模块),难道测试部就没一点责任?代码走查人员就没一点责任?不过 ...

随机推荐

  1. 重新整理.net core 计1400篇[四] (.net core 修改sdk )

    前言 可能有些人还不知道什么是sdk,software development kit,中文是软件开发包的意思. 然后什么是软件开发包? 软件开发工具包是一些被软件工程师用于为特定的软件包.软件框架. ...

  2. leetcode每日一题:836. 矩形重叠

    836. 矩形重叠 矩形以列表 [x1, y1, x2, y2] 的形式表示,其中 (x1, y1) 为左下角的坐标,(x2, y2) 是右上角的坐标. 如果相交的面积为正,则称两矩形重叠.需要明确的 ...

  3. JDBC数据库汇总Attack研究

    前言 针对除Mysql的其它数据库的jdbc attack分析 H2 RCE 介绍 H2 是一个用 Java 开发的嵌入式数据库,它本身只是一个类库,即只有一个 jar 文件,可以直接嵌入到应用项目中 ...

  4. SELECT...FROM 表 a,( SELECT...FROM...WHERE...) tc...的一些注意以及多字段之间的模糊查询

    将sql查询结果作为一个表来查询的时候的一些注意事项 因为工作,发现了这种sql的写法,但是有的时候感觉并不是自己想要的结果,自己试着玩了属于是 简单来说,这个查询并不是拼接结果的,而是将结果按照一个 ...

  5. 力扣430(java)-扁平化多级双向链表(中等)

    题目: 你会得到一个双链表,其中包含的节点有一个下一个指针.一个前一个指针和一个额外的 子指针 .这个子指针可能指向一个单独的双向链表,也包含这些特殊的节点.这些子列表可以有一个或多个自己的子列表,以 ...

  6. sysAK(青囊)系统运维工具集:如何实现高效自动化运维?| 龙蜥技术

    ​简介:What is sysAK.典型工具介绍.开源 3 方面介绍了 sysAK 系统,目前 sysAK 工具集已经在龙蜥社区开源,并且在系统运维 SIG.跟踪诊断 SIG 一起共建,希望大家后期加 ...

  7. 2019-8-31-dotnet-线程静态字段

    title author date CreateTime categories dotnet 线程静态字段 lindexi 2019-08-31 16:55:58 +0800 2019-06-13 0 ...

  8. HZ2023 远足游记

    你说得对,但是我放假之前写的 P4689 代码没了 所以来摆 4.6(远足) 上午 走路,刚开始感觉没啥 走到园博园发现没预料中那么顺利 但是还感觉没啥 因为也没预料到 \(N·m\) 学校会让我们原 ...

  9. 简说Python之flask初体验

    目录 flask初体验 1.安装Flask 2.创建"Hello, World" Flask应用 3.执行结果 flask是python web的轻量框架,简单的几条命令就可以创建 ...

  10. nmcli device status状态为unmanaged

    遇到报错:(device lo not available because device is strictly unmanaged) nmcli device status 查看设备状态 nmcli ...