(一)前言

 在君正Zeratul_T31_开发指南中明确规范,禁止在主程序中使用system 等系统调用接口函数,需要在另外的一个守护进程中去实现system函数的功能。这里有两个问题:1.system函数有哪些不安全的地方? 2.为什么不可以在主进程中去执行system,而在守护进程中却可以?

(二)为什么危险

 system 源码实现

int system(const char * cmdstring)
{pid_t pid;int status;if(cmdstring == NULL)
{return (1); //如果cmdstring为空,返回非零值,一般为1
}if((pid = fork())<0)
{status = -1; //fork失败,返回-1
}
else if(pid == 0)
{execl("/bin/sh", "sh", "-c", cmdstring, (char *)0);_exit(127); // exec执行失败返回127,注意exec只在失败时才返回现在的进程,成功的话现在的进程就不存在啦~~
}
else //父进程
{while(waitpid(pid, &status, 0) < 0){if(errno != EINTR){status = -1; //如果waitpid被信号中断,则返回-1break;}}
}return status; //如果waitpid成功,则返回子进程的返回状态
}

(1)实现

实际上system()函数执行了三步操作:
 1. fork一个子进程;
 2. 在子进程中调用exec函数去执行command;
 3. 在父进程中调用wait去等待子进程结束。

(2)执行:

system() executes a command specified in command by calling /bin/sh -c command, and returns after the command has been completed. During execution of the command, SIGCHLD will be blocked, and SIGINT and SIGQUIT will be ignored.

system()函数调用/bin/sh来执行参数指定的命令,/bin/sh 一般是一个软连接,指向某个具体的shell,比如bash,-c选项是告诉shell从字符串command中读取命令;
在该command执行期间,SIGCHLD是被阻塞的,好比在说:hi,内核,这会不要给我送SIGCHLD信号,等我忙完再说;
 在该command执行期间,SIGINT和SIGQUIT是被忽略的,意思是进程收到这两个信号后没有任何动作。

(3)返回值:

The value returned is -1 on error (e.g. fork(2) failed), and the return status of the command otherwise. This latter return status is in the format specified in wait(2). Thus, the exit code of the command will be WEXITSTATUS(status). In case /bin/sh could not be executed, the exit status will be that of a command that does exit(127).If the value of command is NULL, system() returns nonzero if the shell is available, and zero if not.

 对于fork失败,system()函数返回-1。
 如果exec执行成功,也即command顺利执行完毕,则返回command通过exit或return返回的值。(注意,command顺利执行不代表执行成功,比如command:“rm debuglog.txt”,不管文件存不存在该command都顺利执行了)
 如果exec执行失败,也即command没有顺利执行,比如被信号中断,或者command命令根本不存在,system()函数返回127.
 如果command为NULL,则system()函数返回非0值,一般为1.

(4)危险的原因

system()函数用起来很容易出错,返回值太多,而且返回值很容易跟command的返回值混淆。这里推荐使用popen()函数替代
 popen()函数较于system()函数的优势在于使用简单,popen()函数只返回两个值:成功返回子进程的status,使用WIFEXITED相关宏就可以取得command的返回结果;失败返回-1,我们可以使用perro()函数或strerror()函数得到有用的错误信息。

(三)为什么不建议在主进程中执行

 为什么不可以在主进程中去执行system,而在守护进程中却可以?
主要是因为主进程的堆栈空间一般是比较大的,执行system 函数会调用fork 创建一个子进程,子进程是拷贝的主进程的内存空间,但需要实际写入数据的时候就会进行实际的内存地址拷贝,在嵌入式设备中,内存有限,当内存不够的时候,程序执行会变慢,并且容易出错

关于fork函数的作用,《Linux程序设计》中是这样解释的:

 我们可以通过调用fork创建一个新进程。这个系统调用复制当前进程,在进程表中新建一个新的表项,新表项中的许多属性与当前进程是相同的。新进程几乎与元进程一模一样,执行的代码也完全相同,但是新进程有自己的数据空间、环境和文件描述符。

 这个解释其实过于笼统,很多细节问题都没有说。下面就简单说一下调用fork时发生的一些细节问题。或者叫fork函数的特点:

 首先,现在的UNIX系统和Linux系统都采用写时复制技术(COW:Copy On Write)。使用这种技术,当调用fork函数时,新的进程只是拥有自己的虚拟内存空间,而没有自己的物理内存空间。新进程共享源进程的物理内存空间。而且新内存的虚拟内存空间几乎就是源进程虚拟内存空间的一个复制。

 我们知道,进程空间可以简单地分为程序段(正文段)、数据段、堆和栈四部分(简单这样理解)。采用写时复制的fork函数,当执行完fork后的一定时间内,新的进程(子进程)和源进程的进程空间关系如下图:

 如上图,fork执行时,Linux内核会为新的进程P2创建一个虚拟内存空间,而新的虚拟空间中的内容是对P1虚拟内存空间中的内容的一个拷贝。而P2和P1共享原来P1的物理内存空间。

 当然要理解“写时复制”中,上图中所展示的状态是会发生变化的。什么时候回发生变化呢?就是,父子两个进程中任意一个进程对数据段、栈区、堆区进行写操作时,上图中的状态就会被打破,这个时候就会发生物理内存的复制,这也就是叫“写时复制”的原因。发生的状态转变如下:

 我们发现,P2有了属于自己的物理内存空间。值得注意的是,各个段之间发生的变化应当是独立的,也就是说,如果只有数据段发生了写操作那么就只有数据段进行写时复制。而堆、栈区域依然是父子进程共享。还有一个需要注意的是,正文段(程序段)不会发生写时复制,这是因为通常情况下程序段是只读的。子进程和父进程从fork之后,基本上就是独立运行,互不影响了。


 此外需要特别注意的是,父子进程的文件描述符表也会发生写时复制。

引用:

 《system()、exec()、fork()三个与进程有关的函数的比较》
 《Linux下使用system()函数一定要谨慎》



######################2022.08.28######################

该博客将停止更新

新的文章内容和附件工程文件

请到 liwen01 博客首页信息查询

liwen01 2022.08.28 日更新

######################2022.08.28######################

君正Zeratul开发(6)——为什么禁止使用system相关推荐

  1. 君正Zeratul开发(4)——图像效果调试

    前言   目前而言,君正设备的isp 图像效果还是君正的工程师在负责开发调试,如果需要添加一款新的摄像头,一般也是将新摄像头的驱动,从ISVP 版本移植到Zeratul平台.这里主要介绍:(1)摄像头 ...

  2. 君正Zeratul开发(5)——快速启动优化

    前言:   Camera 需要快速启动, 以第一时间抓拍到图像,君正官方给的第一帧图像是200ms,实际应用中时间会长不少. 1.尽快运行主程序  主程序应该放置在 rootfs 中并第一时间加载运行 ...

  3. 君正Zeratul开发(2)——uboot启动分析

    前言    boot启动一般分为两个阶段,君正设备的第一阶段uboot spl 程序没有开源,用户编译的是第二阶段的boot,最后将两个阶段的boot合并到一起,写入到boot分区中去,boot分区如 ...

  4. 君正4750开发板使用日记2-Linux环境搭建与内核编译

    为什么80%的码农都做不了架构师?>>>    Linux环境搭建与内核编译 上一篇中把4750开发板与PC的硬件连通了,这一篇记录的是基本的Linux环境的搭建.其实官方文档已经比 ...

  5. 君正x2000开发板usb口读取文件

    ## 标题君正x2000开发板USB口读取文件 (由于word文档无法上传,只复制了文字,操作过程中的图片就不做展示) 开发要求 1.外设的tf卡存储录像抓图文件需要通过USB口在电脑上进行查看.拷贝 ...

  6. 君正 Halley6 开发板调试SPI LCD

    kernel版本:linux-4.4.94 CPU: X1600 LCD: 3.5 寸TFT(320×240),Model Name LQ035NC111 本⽂以芯⽚x1600, 开发板halley6 ...

  7. 君正X2000开发板开箱测试

    一  上电开机 使用附赠的电源线连接5V电源头和开发板DC电源口,即可开机. 注意:开发板输入电压为5V,切勿输入12V电源 二  连接串口终端 使用附赠的USB Type C接口连接开发板的调试接口 ...

  8. QT5.15.2源码编译后在君正MIPS架构运行播放实时视频流

    问题背景: 公司新项目需要使用君正T40 soc,平台是 MIPS 架构,但是君正在此平台未开发出图形界面工具,项目需要人机交互,于是需要使用QT实现相关需求. 问题描述: 下载QT5.15.2源码( ...

  9. adb shell 调试君正板子

    今天接到一个任务,把现有的代码移植到君正的开发板上. 下面就开始吧. 1 首先,代码移植到君正上,需要交叉编译环境. 需求方已经提供了交叉编译的tar包,直接在linux上解压缩. 然后 指定 CC= ...

最新文章

  1. 直播活动丨BMMeetup第1期:大模型Prompt Tuning技术,8场学术报告和Poster提前下载...
  2. 长沙计算机学校首问 长沙大计校区电话,长沙有哪些中专学校,长沙中专学校名单一览表...
  3. new 一个模板、类_Java必备基础-类(Class)
  4. Java中的main方法
  5. 一步步教你为网站开发Android客户端---HttpWatch抓包,HttpClient模拟POST请求,Jsoup解析HTML代码,动态更新ListView...
  6. easyui datagrid加载本地数据和网络数据
  7. linux pdf to txt,PDF转换为TXT
  8. 利用哈夫曼树编码与译码
  9. 股票休市午间可以撤单吗?
  10. 透明色的rgb值是多少_一文掌握PPT主题色原理及使用技巧
  11. 添加购物车功能全部代码
  12. Cisco Packet Tracer思科模拟器中路由器PPP封装与验证
  13. html弹窗可以复制,简单漂亮的js弹窗可自由拖拽且兼容大部分浏览器
  14. 入门级Pytorch+MINIST数据集实现手写数字识别
  15. CAM350 - 导出 DXF 文件
  16. 请问下面这段代码哪里有错? private static final String s=
  17. Esp8266 -- 心知天气get请求及url讲解说明
  18. 普通话测试app怎么样可以不交钱_考了几次普通话,仍无法达到理想成绩?
  19. dialog使用(dialog使用方法)
  20. java基础 —— 集合、异常、反射、io流、多线程

热门文章

  1. Vue相关面试问答TOP2(能不能在method中使⽤箭头函数、如何定义组件的data、v-if 和 v-show的区别、computed、watch、methods的区别、axios的特点....)
  2. 魅蓝E3发布,斥资千万购买虹软算法!
  3. 学计算机用苹果本,如何快速学会使用苹果电脑?
  4. php支付接口签名,PHP的支付宝支付接口总结
  5. 计算机网络对电视的的应用,广播电视发展中对计算机网络的应用
  6. createrepo -g /enp/comps.xml .
  7. 网页设计作业 仿苏宁易购商城网站设计——仿苏宁易购官网商城(1页) HTML+CSS+JavaScript web网页大作业
  8. 数十倍的数据量增长,传统 OLAP 还能应对吗?
  9. android background大全,Background安卓版-Background壁纸预约 _5577安卓网
  10. 【word毕业论文排版(2)】拼写检查