问题背景

要做一个需求,大概是检测到某输入重启,于是写一个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. 什么是报表工具?和 EXCEL 有什么区别?

    报表是什么? 带数据的表格和图表就都是报表,像工资表,考勤表,成绩表,资产负载表等等都是报表. 那报表工具,顾名思义就是用来做报表的工具,那 Excel 是不是也算报表工具?广义上讲当然也算.但 IT ...

  2. 重新点亮shell————文本搜索[九]

    前言 简单整理一下文本搜索. 正文 文本搜索需要学下面: 元字符 扩展元字符 文件的查找命令find 例子1: 例子2(通配符): 例子3(正则表达): 例子4(可以根据文件类型匹配): 例子5(找到 ...

  3. native react 代码智能提示

    背景 在vscode 中,虽然有插件可以达到代码提示的效果但是不是很嗨. 所以加上这些: 全局安装typings: npm install typings -g 1 安装react和react-nat ...

  4. 关于<property name="hibernate.hbm2ddl.auto"></property>中的参数填写

    hibernate的数据库表自动生成参数 关于<property name="hibernate.hbm2ddl.auto"></property>中的参数 ...

  5. 力扣189(java)-轮转数组(中等)

    题目: 给你一个数组,将数组中的元素向右轮转 k 个位置,其中 k 是非负数. 示例 1: 输入: nums = [1,2,3,4,5,6,7], k = 3输出: [5,6,7,1,2,3,4]解释 ...

  6. 大数据时代下,App数据隐私安全你真的了解么?

    ​简介:你是否有过这样的经历:你和朋友聊天表达你近期想要购买某件商品,第二天当你打开某购物软件时,平台向你推送的商品正是你想要购买的:或者,你是否接到过陌生来电,他们准确的报出了你的名字和年龄.... ...

  7. Serverless Devs 2.0 开箱测评:Serverless 开发最佳实践

    ​简介: 当下,Serverless 概念很火,很多同学被 Serverless 的优势吸引过来,比如它的弹性伸缩,免运维,高可用,资费少.但真正使用起来去落地的时候发现问题很多,大型项目如何组织函数 ...

  8. js的几个截取

    jsfun(){         let str = '01234567'         let str1         str1 = str.slice(2,5)                 ...

  9. LLM优化:开源星火13B显卡及内存占用优化

    1. 背景 本qiang~这两天接了一个任务,部署几个开源的模型,并且将本地经过全量微调的模型与开源模型做一个效果对比. 部署的开源模型包括:星火13B,Baichuan2-13B, ChatGLM6 ...

  10. Flink Forward #Asia2020 流批一体及数仓资料整理

    阿里云实时计算负责人 - 王峰(莫问)/ FFA_2020-Flink as a Unified Engine - Now and Next-V4 2020年Flink 基于Flink 的流批一体数仓 ...