unix系统编程day02--Liunx中exec函数族和回收进程函数讲解

exec函数组

作用:在程序运行的过程中,再执行一个完整的程序。

execlp函数

  • 解释:其中l表示list,p表示PATH。
  • 作用:借助PATH环境变量,加载一个进程,新进程会清空原进程的虚拟地址空间的0-3G空间,但是进程id不会变。
  • 函数原型:int execlp(const char * filename, const char * filename, const char * arg0, const char * arg1, … , NULL)。
  • 返回值:成功无返回,失败返回-1。
  • 函数解释:后面的参数为命令行参数,第一个参数arg0一般没有用,后面才是对应的真正的参数,并且由于是命令行参数,所以最后一定要有一个NULL参数作为哨兵。

通过运行一个程序来实现子进程运行ls命令:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
extern char ** environ;

int main(void) {
    pid_t pid = 0;
    pid = fork();
    if(pid < 0) {
        perror("fork error");
        exit(1);
    } else if(pid == 0) {
        execlp("ls", "ls", "-al", NULL);
    } else {
        sleep(1);
        printf("I'm father");
    }
    return 0;
}

注:一定要注意execlp的参数,后面的arg0一般为无用参数,一定要加NULL作为哨兵结尾!

execl函数

  • 作用:加载一个进程,通过路径+程序名,无PATH环境变量参与
  • 函数原型:int execl(const char * filename, const char * filename, const char * arg0, const char * arg1, … , NULL); 可见名字中没有了p所以没有环境变量,所以filename要写绝对路径。
  • 返回值:成功无返回,失败返回-1

使用execl调用ls:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
extern char ** environ;

int main(void) {
    pid_t pid = 0;
    pid = fork();
    if(pid < 0) {
        perror("fork error");
        exit(1);
    } else if(pid == 0) {
        execl("/bin/ls", "ls", "-al", NULL);
    } else {
        sleep(1);
        printf("I'm father");
    }
    return 0;
}

注:既然是程序的绝对路径,所以我们可以运行我们自定义的程序,还是要注意参数,最后加NULL!!!

execle函数

  • 解释:e表示environ的意思,即环境变量
  • 函数原型:int execle(const char * path, const char * arg0, const char * arg1, … , NULL, char * const envp[]);

execv函数

  • 解释:v表示argv的意思,顾名思义,后边的那一堆参数可以用一个结构体来代替了,所执行的程序依然用绝对路径表示。
  • 函数原型:int execv(const char * path, char * const argv[]);
    注:argv数组最后一个元素为NULL;
    使用execv函数执行ls的程序:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
extern char ** environ;

int main(void) {
    pid_t pid = 0;
    pid = fork();
    if(pid < 0) {
        perror("fork error");
        exit(1);
    } else if(pid == 0) {
        char *argv[] = {"ls", "-al", NULL};
        execv("/bin/ls", argv);
    } else {
        sleep(1);
        printf("I'm father");
    }
    return 0;
}

注:参数最后一定为NULL

execvp函数

  • 解释:使用环境变量(p)并且把参数用数组表示
  • 函数原型:int execvp(const char * file, char * const argv[]);

execve函数

  • 解释:使用数组参数,使用environ环境变量
  • 函数原型:int execve(const char * path, char * const argv[], char * const envp[]);
    总结:l表示参数要list出来,v表示参数要数组起来,e表示要引入环境变量,p表示用到PATH环境变量,然后组合一下总共有6个函数,两个用不到环境变量的,然后两两组合的四个,都是exec开头。所有的exec族函数成功无返回,失败返回-1。

复习使用dup2重定向输出,把输出重定向到out文件

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
extern char ** environ;

int main(void) {
    int fd = open("out", O_CREAT | O_RDWR, 0777);
    dup2(fd, 1);
    pid_t pid = 0;
    pid = fork();
    if(pid < 0) {
        perror("fork error");
        exit(1);
    } else if(pid == 0) {
        char *argv[] = {"ls", "-al", NULL};
        execv("/bin/ls", argv);
    } else {
        sleep(1);
        printf("I'm father");
    }
    return 0;
}

简单的记忆:dup2(a, b)可以简单的记为把描述符a移到描述符b的位置(看个人理解)。

回收子进程

孤儿进程

  • 孤儿进程:父进程先于子进程结束,则子进程成为孤儿进程,子进程的父进程成为init进程,称为init进程领养孤儿过程,父亲结束后,儿进程的getppid()为1,通过查看1表示的是init进程。init进程又称为init进程孤儿院。

僵尸进程

  • 僵尸进程:进程终止,父进程尚未回收,子进程的0-4G虚拟地址全部释放,但是子进程残留资源(PCB)一直存在在内核中,变成僵尸进程,残留的目的就是让父进程回收,想要父进程获得死亡状态。
    制作僵尸进程代码:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>

int main(void) {
    pid_t pid = 0;
    pid = fork();
    if(pid < 0) {
        perror("fork error");
        exit(1);
    } else if(pid == 0) {
        printf("I'm child, I Will sleep 10s\n");
        sleep(10);
        printf("-----------child die----------\n");
    } else {
        while(1) {
            sleep(1);
            printf("I'm father pid = %d, my son = %d\n", getpid(), pid);
        }
    }
    return 0;
}

我们发现:出现了一个僵尸进程
f21d12e6dad0df8933cb6416cf5dba01.png

回收子进程函数

一个进程在终止的时候会关闭所有的文件描述符,释放用户空间分配的内存等资源,但是他的PCB还保留着,内核存了一些信息:如果正常终止,则保存着退出状态,如果异常终止则保存导致异常终止的信号是哪个,这个进程的父进程可以调用wait或者waitpid获取这些信息,然后彻底清楚掉这个进程,我们知道一个进程的退出状态可以在shell中用$?查看,因为shell是他的父进程,当他终止时shell可以调用wait或者waitpid来获取他的退出状态同时彻底清楚掉这个进程。

wait函数

  • 函数原型:pid_t wait(int * staus)
  • 参数:staus为传出参数,表示进程结束的状态是正常退出还是异常退出异常的时候保存的是信号
  • 返回值:成功返回被回收的进程号,否则返回-1
  • wait会阻塞函数知道子进程被回收之后程序才会继续运行下去
  • 作用:
    1. 阻塞等待子进程退出
    2. 回收子进程的PCB资源
    3. 获取子进程的状态
      通过wait回收一个子进程的程序(暂时不获取退出状态):
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>

int main(void) {
    pid_t pid = 0, wpid = 0;
    pid = fork();
    if(pid < 0) {
        perror("fork error");
        exit(1);
    } else if(pid == 0) {
        printf("I'm child, I Will sleep 10s\n");
        sleep(10);
        printf("-----------child die----------\n");
    } else {
        wpid = wait(NULL);
        if(wpid < 0) {
            perror("wait error");
            exit(1);
        }
        while(1) {
            sleep(1);
            printf("I'm father pid = %d, my son = %d\n", getpid(), pid);
        }
    }
    return 0;
}

  • 获取子进程的退出状态,根据传出参数status
    需要掌握四个宏函数来确定具体的退出原因,我们知道在Linux中所有的异常退出都是由于信号引起。
宏函数
  1. WIFEXITED(status):如果返回为非零则表示子进程正常退出。可以记(WAIT IF EXITED)。
  2. WEXITSTATUS(status):如果1位真则执行该宏来确定返回值。可以记忆(WAIT EXIT STATUS)即wait退出状态。
  3. WIFSIGNALED(status):如果返回值为非零,那么表示异常退出,需要调用4来确定是哪个信号导致异常退出。
  4. WTERMSIG(status):如果3为真,则执行这个来查看具体是哪个信号导致程序异常退出。可以记忆 WAIT TREM SIGNAL wait的退出信号是多少
    下面我们通过wait回收子进程并确定正常退出的返回值是多少:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>

int main(void) {
    pid_t pid = 0, wpid = 0;
    int status;
    pid = fork();
    if(pid < 0) {
        perror("fork error");
        exit(1);
    } else if(pid == 0) {
        printf("I'm child, I Will sleep 10s\n");
        sleep(3);
        printf("-----------child die----------\n");
        return 19;
    } else {
        wpid = wait(&status);
        if(wpid < 0) {
            perror("wait error");
            exit(1);
        }
        if(WIFEXITED(status)) {
            printf("return value = %d\n", WEXITSTATUS(status));
        }
        if(WIFSIGNALED(status)) {
            printf("killed by signal %d\n", WTERMSIG(status));
        }
    }
    return 0;
}

执行结果
在这里插入图片描述

然后我们使用信号终止一个程序来看一下结果,代码就是把上面的代码子进程睡眠改为60秒,方便我们使用kill来终止它。
结果为:
在这里插入图片描述
可以看到被15号信号终止,查看所有的信号可以用 kill -l在终端来查看

waitpid函数

  • 函数原型:pid_t waitpidwaitpid(pid_t pid, int *stat_loc, int options);
  • 其中第三个参数options为0的时候为阻塞方式,WNOHANG为非阻塞方式
  • 作用:用来回收指定进程。
  • 第一个参数如果>0则回收指定的进程,如果为-1表示回收任意子进程,如果为0则回收和当前调用waitpid函数的进程同组的任意子进程,<-1回收指定进程组内的任意子进程
  • 返回值:如果参3为0则成功返回回收的pid否则返回-1,如果为WNOHANG如果回收成功返回对应的pid,否则返回0

总结

exec函数族:

  • execlp --p表示PATH环境变量,运行指定的程序,需要PATH环境变量的支持
    l: list
  • execl 运行自己的程序,需要指定绝对路径
  • execv:
    v:表示argv,意思是把参数放在一个char * argv[]里面
  • execve
  • execle
    回收子进程:
    wait(status)函数:
    作用:
      1. 阻塞回收子进程
      2. 回收子进程的PCB资源
      3. 获取子进程的退出状态
    1)WIFEXITED()True
        WEXITSTATUS()获取退出状态
    2)WIFSIGNALED()True
        WTERMSIG()获取让程序异常退出的信号
    waitpid:
  • 参1:
    pid>0:回收指定的pid函数
    pid=-1:回收任意子进程
    pid=0:回收本组的任意子进程
    pid<-1:回收指定组的任意子进程
  • 参2:staus同wait函数
  • 参3:options=0,阻塞方式回收,成功返回pid,失败返回-1
    options=WNOHANG,非阻塞方式回收,成功返回pid,失败返回0。
<think>我们已知用户使用的是小米设备MDZ-23-AA(小米盒子3增强版),并希望刷入Linux系统。 根据引用[1]提到的小米盒子运行linux,以及刷机成砖的解救措施,我们可以推断该设备理论上可以运行Linux,但刷机过程有风险。 引用[2]引用[3]分别提到了ADB连接线刷包精简,这些是刷机过程中可能用到的工具步骤。 然而,需要注意的是,官方通常不提供Linux系统的支持,因此需要寻找第三方开发的固件刷机方法。 步骤大致如下: 1. 准备工作:下载必要的工具第三方Linux固件。 2. 解锁Bootloader:通常需要解锁才能刷入非官方系统。 3. 刷入第三方Recovery(如TWRP):以便刷入第三方固件。 4. 刷入Linux固件。 但是,由于设备型号的具体情况,我们需要查找针对MDZ-23-AA的第三方Linux固件教程。 由于刷机有风险,可能导致设备变砖,请务必谨慎操作并备份重要数据。 具体步骤建议: 步骤1:确认设备型号硬件版本 - 确保设备型号为MDZ-23-AA(小米盒子3增强版)。 步骤2:解锁Bootloader - 参考引用[2]中的ADB连接方法,通过ADB连接设备,并尝试解锁。但小米盒子通常没有官方提供的解锁方式,可能需要寻找非官方方法。 步骤3:安装自定义Recovery - 在解锁Bootloader后,刷入第三方Recovery(如TWRP)。需要找到专为MDZ-23-AA编译的TWRP。 步骤4:刷入Linux固件 - 需要找到适用于该设备的Linux固件。由于是电视盒子,常见的第三方系统有LibreELEC(基于Linux的媒体中心系统)或Armbian等。但需要确认是否有针对MDZ-23-AA的适配版本。 步骤5:启动配置 - 刷入完成后,重启设备进入Linux系统,并进行初始配置。 注意:由于该设备的具体情况,可能没有现成的完美适配的Linux系统。用户可能需要自行编译内核或处理驱动问题。 推荐资源: - XDA Developers论坛:搜索“MDZ-23-AA”或“mibox3 pro”的相关讨论。 - 国内论坛如ZNDS智能电视网,可能有相关教程。 风险提示:刷机可能导致设备失去保修,甚至变砖。请确保按照教程操作,并自行承担风险。 由于具体操作步骤依赖于第三方资源,这里无法提供确切的命令固件下载链接。建议用户搜索针对该型号的详细教程。 根据引用[3]提到的线刷包精简,在刷机过程中可能会用到线刷工具,但通常用于刷入官方系统。对于Linux,我们更多使用卡刷(通过Recovery刷入zip包)或线刷第三方系统镜像。 因此,总结回答如下:</think>### 在小米盒子 MDZ-23-AA 上刷入 Linux 系统的步骤指南 MDZ-23-AA 是小米盒子 3 增强版(MiBox Pro),其 Amlogic S905X 芯片支持运行 Linux,但需通过第三方固件实现。以下是安全刷机流程: --- ### **一、准备工作** 1. **所需工具** - USB Type-A 转 Type-A 数据线(或双公头 USB 线) - 电脑(Windows/Linux/macOS) - 工具包下载: - [Amlogic USB Burning Tool](https://eserviceinfo.com/amlogic-usb-burning-tool/)(Windows 线刷工具)[^3] - [LibreELEC 固件](https://libreelec.tv/)(专为电视盒子优化的 Linux 系统- [TWRP Recovery](https://forum.xda-developers.com/t/recovery-unofficial-twrp-for-mdz-23-aa.4320001/)(第三方恢复工具)[^2] 2. **风险提示** - 刷机可能变砖(需短接救砖)[^1] - 备份原厂固件(使用 `adb pull /dev/block/system system.img`)[^2] - 充满设备电量,避免断电 --- ### **二、刷机步骤** #### **方法1:通过 USB Burning Tool 线刷(推荐)** ```mermaid graph TB A[下载 LibreELEC 的 .img 固件] --> B[安装 Amlogic USB Burning Tool] B --> C[加载固件文件] C --> D[盒子断电 → 按住复位键 → 插入USB线] D --> E[工具识别设备 → 点击“开始”刷入] E --> F[完成后重启设备] ``` #### **方法2:通过 TWRP Recovery 卡刷** 1. **刷入 TWRP** ```bash adb reboot bootloader # 进入Bootloader模式 fastboot flash recovery twrp_mdz23aa.img # 刷入TWRP ``` 2. **刷入 Linux 固件** - 将 LibreELEC 的 `.zip` 包存入 U 盘 - 进入 TWRP → `Install` → 选择固件刷入 --- ### **三、首次启动配置** 1. 首次启动需 5-10 分钟初始化 2. 连接网络(WiFi/有线) 3. 通过 SSH 访问系统(默认账号:`root`,密码:`libreelec`) ```bash ssh root@盒子IP地址 ``` --- ### **四、注意事项** 1. **驱动问题** - 若电脑无法识别设备,安装 [Amlogic USB 驱动](https://androidmtk.com/download-amlogic-usb-driver) 2. **变砖修复** - 短接主板触点(通常为 NAND 芯片旁的两针)强制进入刷机模式[^1] 3. **系统选择** - **LibreELEC**:媒体中心(Kodi 整合版) - **Armbian**:完整 Debian 系统(需适配内核) > ⚠️ 重要提示:不同硬件批次(如 1GB/2GB 内存版)需匹配对应固件,错误固件会导致无法启动。建议在 [XDA 开发者论坛](https://forum.xda-developers.com/f/xiaomi-mi-box.8355/) 查找设备专属教程[^2][^3]。 --- ### **相关问题** 1. 如何验证 MDZ-23-AA 的硬件版本? 2. LibreELEC Armbian 系统有哪些功能差异? 3. 刷机后如何恢复小米官方系统? [^1]: 小米盒子刷机成砖的解救措施需短接主板触点强制进入 Bootloader 模式。 [^2]: ADB 连接 TWRP 刷入是第三方系统安装的关键步骤。 [^3]: 线刷工具需配合 Sparse 格式镜像文件,确保分区表兼容性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值