固件升级时fd一直增加,升级十几次后crash

固件升级流程,升级软件向app进程发送升级指令,在app中使用system(“./app.sh upgrade start”)执行脚本,启动upgrade进行升级包接收,同时关闭app进程;但发现升级完成后fd增加,app进程打开的fd存在两份;

分析原因:在使用 system 函数创建子进程执行脚本时,子进程会继承父进程的文件描述符。当你在脚本中使用 kill 命令杀死进程A时,并不会直接导致文件描述符减少。

如果子进程没有显式地关闭这些文件描述符,它们将继续存在,即使父进程被脚本中的 kill 命令杀死也不会释放。

解决方式:在父进程中调用 fork() 函数生成一个子进程,然后在子进程中关闭所有文件描述符再执行 system() 命令,可以确保子进程中不会有非必要的文件描述符被继承过去。

实现代码:

void exec_system(char* command)
{
    pid_t pid = -1;
    int status = 0; 
    pid = fork(); 
    if(pid == -1){
       ::exit(-1);
    } else if (pid == θ){
        //0,1,2是标准的输入输出,不关闭;关闭其他fd,在system中不继承该子进程的fd文件描述符
        for (int fd =·3; fd <·sysconf(_SC_OPEN_MAX); fd++)  {
           ::close(fd);
        }
        system(command); //执行脚本
        ::exit(0);·//·退出子进程
    }else {
        waitpid(pid,&status,0);// 等待子进程退出
    } 

waitpid() 函数是用来等待一个特定的子进程结束并返回子进程的状态的。在父进程中调用 fork() 函数会创建一个新的子进程,两个进程(即父进程和子进程)会同时运行。如果不等待子进程结束,父进程可能会在子进程之前结束,导致子进程成为孤儿进程或僵尸进程。这可能会导致一些问题,例如:

  • 孤儿进程:如果父进程先于子进程结束,子进程将成为孤儿进程,这意味着没有父进程来捕获它的退出状态,该子进程的进程标识符(PID)将被 1 号进程 init(或 systemd)收养,从而成为 init 进程的子进程。
  • 僵尸进程:父进程在子进程结束后不调用 wait() 或 waitpid() 函数,子进程将成为僵尸进程。这意味着子进程已经退出了,但是它的进程描述符仍然存在,并且占用一些内核数据结构。如果产生大量僵尸进程,可能会耗尽系统的资源。
posted @ 2025-12-17 10:42  我自逍遥笑  阅读(10)  评论(0)    收藏  举报