news 2026/4/23 12:17:24

system函数与exec函数族

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
system函数与exec函数族

文章目录

  • system函数
    • 函数原型
      • 功能
      • 参数
      • 返回值
        • 返回值获取shell退出状态
    • 实现原理
    • 安全风险
      • system运行任意的shell命令
      • 命令注入漏洞
  • exec函数族
    • 核心功能
    • 函数原型
      • 命名规律
      • 参数对比
      • 返回值
      • 常见错误码
    • 应用例程
      • execl() - 参数列表形式
      • execv() - 参数数组形式
      • execle() - 自定义环境变量
      • execve() - 系统调用(最底层)
      • execlp() - 自动搜索PATH
      • execvp() - PATH搜索 + 参数数组
      • 标准模式:fork + exec

system函数

函数原型

#include<stdlib.h>intsystem(constchar*command);

功能

  • 执行shell命令,就像在终端中输入命令一样
system("ls -la");// 执行ls命令system("echo Hello");// 执行echo命令system("gcc test.c");// 执行编译命令

参数

  • 有效命令:执行命令
  • NULL:检查shell是否可用
  • 空字符串:""直接返回成功
#include<stdlib.h>#include<stdio.h>intmain(){// 1. 执行简单命令system("pwd");// 打印当前目录// 2. 执行带参数的命令system("ls -l /tmp");// 3. 执行复杂命令(管道、重定向等)system("ps aux | grep bash > output.txt");// 4. 使用变量构建命令charfilename[100]="test.txt";charcommand[200];snprintf(command,sizeof(command),"cat %s",filename);system(command);return0;}

返回值

  • == 127:启动shell失败,/bin/sh 不存在或不可执行
  • == -1:其他错误,fork失败、内存不足等
  • 其他值:命令执行状态,shell的退出状态编码
intret=system(command);if(ret==-1){// system()调用本身失败perror("system() failed");}elseif(WIFEXITED(ret)){// 命令正常结束printf("命令退出码: %d\n",WEXITSTATUS(ret));if(WEXITSTATUS(ret)==127){printf("错误: shell无法启动或命令不存在\n");}elseif(WEXITSTATUS(ret)==126){printf("错误: 命令不可执行\n");}}elseif(WIFSIGNALED(ret)){// 命令被信号终止printf("命令被信号终止: %d\n",WTERMSIG(ret));}
返回值获取shell退出状态
// 要获取真正的退出状态,需要处理返回值intstatus=system("ls /nonexistent");if(status==-1){printf("system()调用失败\n");}else{// 使用wait相关的宏if(WIFEXITED(status)){intexit_code=WEXITSTATUS(status);printf("命令退出码: %d\n",exit_code);// ls /nonexistent 会返回 2}}

实现原理

system("ls -l") ↓ fork() 创建子进程 ↓ 子进程:execl("/bin/sh", "sh", "-c", "ls -l", NULL) ↓ shell解析命令:ls -l ↓ shell执行ls命令 ↓ shell退出,返回ls的退出状态 ↓ 父进程接收状态,返回给调用者
// system() 大致相当于以下代码:intsystem(constchar*command){if(command==NULL){// 检查shell是否存在returnshell_exists()?1:0;}pid_tpid=fork();if(pid==0){// 子进程:执行shellexecl("/bin/sh","sh","-c",command,(char*)NULL);_exit(127);// 如果execl失败}elseif(pid>0){// 父进程:等待子进程结束intstatus;waitpid(pid,&status,0);returnstatus;// 返回shell的退出状态}else{// fork失败return-1;}}

安全风险

system运行任意的shell命令

#include<stdio.h>#include<stdlib.h>intmain(intargc,constchar*argv[]){charbuf[1024]={};while(1){printf("命令->");fgets(buf,1024,stdin);system(buf);}return0;}

命令注入漏洞

  • 如果键入rm -rf *;会造成不可挽回的后果
// ⚠️ 危险!可能被攻击!charuser_input[100];printf("输入文件名: ");fgets(user_input,sizeof(user_input),stdin);charcommand[200];sprintf(command,"rm %s",user_input);// ⚠️ 危险!system(command);// 如果用户输入: "test.txt; rm -rf /"// 实际执行: rm test.txt; rm -rf /

exec函数族

核心功能

  • 进程替换
    • 不创建新进程:替换当前进程的代码和数据
    • 继承PID:保持原进程ID不变
    • 全新开始:从新程序的main()函数开始执行

函数原型

// 所有exec函数都在 <unistd.h> 中#include<unistd.h>intexecl(constchar*path,constchar*arg0,...,(char*)NULL);intexecv(constchar*path,char*constargv[]);intexecle(constchar*path,constchar*arg0,...,(char*)NULL,char*constenvp[]);intexecve(constchar*path,char*constargv[],char*constenvp[]);intexeclp(constchar*file,constchar*arg0,...,(char*)NULL);intexecvp(constchar*file,char*constargv[]);

命名规律

execl - l: list (参数列表) execv - v: vector (参数数组) execle - e: environment (传递环境变量) execlp - p: PATH (在PATH中查找程序) execvp - p + v: PATH + 参数数组 execve - 系统调用 (v + e)

参数对比

函数程序路径参数传递方式环境变量PATH搜索
execl()完整路径参数列表继承当前
execv()完整路径参数数组继承当前
execle()完整路径参数列表自定义
execve()完整路径参数数组自定义
execlp()文件名参数列表继承当前
execvp()文件名参数数组继承当前

返回值

  • 执行成功:不返回(进程被替换)
  • 执行失败:返回 -1,设置 errno
// 重要:exec成功时,后面的代码不会执行!if(execl("/bin/ls","ls",NULL)==-1){// 只有exec失败时才会执行到这里perror("exec failed");exit(1);}// exec成功后,这里的代码永远不会执行printf("这行不会打印!\n");

常见错误码

#include<errno.h>intret=execvp("nonexistent",args);if(ret==-1){switch(errno){caseENOENT:printf("程序不存在\n");break;caseEACCES:printf("没有执行权限\n");break;caseENOMEM:printf("内存不足\n");break;caseE2BIG:printf("参数列表太长\n");break;caseENOEXEC:printf("不是可执行文件\n");break;default:perror("exec失败");}exit(1);}

应用例程

execl() - 参数列表形式

intexecl(constchar*path,constchar*arg0,...,(char*)NULL);// 参数以列表形式传递,以NULL结束// 示例:执行 ls -l /tmpexecl("/bin/ls","ls","-l","/tmp",NULL);// 相当于终端命令:$ ls -l /tmp

execv() - 参数数组形式

intexecv(constchar*path,char*constargv[]);// 参数以数组形式传递// 示例:执行 ls -l /tmpchar*args[]={"ls","-l","/tmp",NULL};execv("/bin/ls",args);// argv数组必须以NULL结束!

execle() - 自定义环境变量

intexecle(constchar*path,constchar*arg0,...,(char*)NULL,char*constenvp[]);// 示例:执行程序并传递新环境变量char*env[]={"MYVAR=hello","PATH=/usr/bin",NULL};execle("/bin/sh","sh","-c","echo $MYVAR",NULL,env);

execve() - 系统调用(最底层)

intexecve(constchar*path,char*constargv[],char*constenvp[]);// 示例:完全控制参数和环境变量char*args[]={"myprog","-v",NULL};char*env[]={"DEBUG=1","LOG_LEVEL=debug",NULL};execve("/usr/local/bin/myprog",args,env);

execlp() - 自动搜索PATH

intexeclp(constchar*file,constchar*arg0,...,(char*)NULL);// 示例:不需要完整路径execlp("ls","ls","-l","/tmp",NULL);// 系统会在PATH中查找ls// 相当于:execl("/bin/ls", "ls", "-l", "/tmp", NULL);

execvp() - PATH搜索 + 参数数组

intexecvp(constchar*file,char*constargv[]);// 示例:最常用的组合char*args[]={"gcc","test.c","-o","test",NULL};execvp("gcc",args);// 自动在PATH中找gcc

标准模式:fork + exec

#include<unistd.h>#include<sys/wait.h>#include<stdio.h>#include<stdlib.h>intmain(){pid_tpid=fork();if(pid==-1){perror("fork失败");exit(1);}if(pid==0){// 子进程:执行新程序printf("子进程 PID=%d 准备执行新程序\n",getpid());// 方法1:使用execlexecl("/bin/ls","ls","-l","/tmp",NULL);// 方法2:使用execvp// char *args[] = {"ls", "-l", "/tmp", NULL};// execvp("ls", args);// 如果exec失败perror("exec失败");_exit(1);// 子进程结束}else{// 父进程:等待子进程printf("父进程 PID=%d 等待子进程\n",getpid());intstatus;waitpid(pid,&status,0);if(WIFEXITED(status)){printf("子进程退出码: %d\n",WEXITSTATUS(status));}}return0;}
  • 资源在exec后保持不变:
      1. 进程ID和父进程ID
      1. 实际用户ID和实际组ID
      1. 进程组ID和会话ID
      1. 控制终端
      1. 当前工作目录
      1. 文件创建掩码
      1. 文件锁
      1. 未处理的闹钟
      1. 信号处理方式(但被忽略的信号保持忽略)
  • 资源在exec后变化:
      1. 代码段、数据段、堆栈(全新)
      1. 文件描述符的close-on-exec标志
      1. 共享内存、内存映射文件
      1. 线程(所有线程终止)
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/23 12:17:23

GitHub项目集成PyTorch-CUDA-v2.8镜像的最佳实践

GitHub项目集成PyTorch-CUDA-v2.8镜像的最佳实践 在深度学习项目协作中&#xff0c;你是否曾遇到这样的场景&#xff1a;同事兴奋地分享一个新模型训练结果&#xff0c;你满怀期待地克隆代码、安装依赖&#xff0c;却在运行时发现 torch.cuda.is_available() 返回 False&#…

作者头像 李华
网站建设 2026/4/23 12:17:17

Markdown表格美化技巧:清晰呈现模型评估指标

技术文档的优雅之道&#xff1a;用 Markdown 表格清晰呈现模型评估与环境配置 在深度学习项目中&#xff0c;我们常常面临一个看似简单却影响深远的问题&#xff1a;如何让实验结果一目了然&#xff1f;当团队成员打开一份训练报告时&#xff0c;是希望看到一段段零散的文字描述…

作者头像 李华
网站建设 2026/4/13 3:52:28

尾递归优化实战指南:前端开发者如何用JS写出高性能递归代码

尾递归优化实战指南&#xff1a;前端开发者如何用JS写出高性能递归代码尾递归优化实战指南&#xff1a;前端开发者如何用JS写出高性能递归代码从“普通递归”说起&#xff1a;一段看似人畜无害的阶乘尾调用&#xff1a;让递归“断舍离”的哲学规范很丰满&#xff0c;现实很骨感…

作者头像 李华
网站建设 2026/4/21 18:50:02

Anaconda配置PyTorch环境并安装torchaudio教程

Anaconda配置PyTorch环境并安装torchaudio教程 在深度学习项目中&#xff0c;一个稳定、可复现的开发环境往往是成功的关键。尤其是在语音处理这类对依赖敏感的任务中&#xff0c;哪怕只是PyTorch和torchaudio版本不匹配&#xff0c;也可能导致整个训练流程失败。许多开发者都曾…

作者头像 李华
网站建设 2026/4/22 23:41:03

清华镜像源配置成功后仍无法加速?排查DNS污染问题

清华镜像源配置成功后仍无法加速&#xff1f;排查DNS污染问题 在人工智能开发的日常中&#xff0c;你是否经历过这样的场景&#xff1a;明明已经配置了清华PyPI镜像源&#xff0c;执行 pip install torch 却依然卡在“Retrying (Retry(total4…”上&#xff0c;下载速度只有几…

作者头像 李华
网站建设 2026/4/18 20:16:48

PyTorch-CUDA-v2.8镜像用户权限安全管理最佳实践

PyTorch-CUDA-v2.8镜像用户权限安全管理最佳实践 在AI研发环境日益容器化的今天&#xff0c;一个预装了PyTorch与CUDA的Docker镜像看似只是“省去了pip install的时间”&#xff0c;实则牵动着整个团队的开发效率与系统安全。尤其当多个研究人员共享同一GPU服务器时&#xff0c…

作者头像 李华