news 2026/4/15 6:04:58

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/3 2:50:24

python flask django线上读书会俱乐部交流系统vue

目录已开发项目效果实现截图关于博主开发技术路线相关技术介绍核心代码参考示例结论源码lw获取/同行可拿货,招校园代理 &#xff1a;文章底部获取博主联系方式&#xff01;已开发项目效果实现截图 同行可拿货,招校园代理 ,本人源头供货商 python flask django线上读书会俱乐部…

作者头像 李华
网站建设 2026/4/12 19:20:34

Docker Compose编排PyTorch-CUDA-v2.8实现多节点训练模拟

Docker Compose编排PyTorch-CUDA-v2.8实现多节点训练模拟 在深度学习模型日益庞大的今天&#xff0c;动辄上百亿参数的网络结构早已让单卡训练变得捉襟见肘。一个典型的Transformer模型在单张A100上跑完一个epoch可能需要数小时&#xff0c;而团队却苦于没有真实的多机集群来验…

作者头像 李华
网站建设 2026/4/13 1:02:08

清华镜像源配置教程:加速PyTorch及相关库的安装流程

清华镜像源配置教程&#xff1a;加速PyTorch及相关库的安装流程 在深度学习项目开发中&#xff0c;环境搭建往往是第一步&#xff0c;却常常成为最耗时、最令人头疼的一环。你是否经历过这样的场景&#xff1a;深夜赶论文复现代码&#xff0c;pip install torch 卡在 10% 长达…

作者头像 李华
网站建设 2026/4/13 10:05:26

计算机Java毕设实战-基于springboot的家政服务撮合与评价平台保洁、月嫂、养老护理、家电维修等多个领域【完整源码+LW+部署说明+演示视频,全bao一条龙等】

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

作者头像 李华
网站建设 2026/4/12 16:56:30

Docker Compose部署多个PyTorch-CUDA实例实现负载均衡

Docker Compose部署多个PyTorch-CUDA实例实现负载均衡 在构建高并发AI推理服务时&#xff0c;一个常见的痛点是&#xff1a;单个GPU实例面对突发流量时迅速达到算力瓶颈&#xff0c;响应延迟飙升&#xff0c;甚至出现请求超时。而与此同时&#xff0c;显卡的算力却并未被完全压…

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

PyTorch-CUDA-v2.8镜像支持Windows Subsystem吗?

PyTorch-CUDA-v2.8 镜像在 WSL 中的可行性与实践路径 在现代 AI 开发中&#xff0c;一个常见的痛点是&#xff1a;如何在 Windows 系统上构建一个既接近原生 Linux 体验、又能充分发挥本地 GPU 性能的深度学习环境&#xff1f;许多开发者曾被迫在“双系统切换”或“虚拟机性能…

作者头像 李华