Linux进程控制
进程创建
fork函数初识
fork() 是Linux内核提供的系统调用,作用是从已存在的父进程中,创建一个全新的子进程。调用fork后,系统会复制父进程的PCB、地址空间、文件描述符、信号处理方式等大部分进程资源,生成一个和父进程几乎完全一致的子进程。
函数原型:
#include <unistd.h>
pid_t fork(void);
进程调用 fork ,当控制转移到内核中的 fork 代码后,内核做:
- 分配新的内存块和内核数据结构给子进程
- 将父进程部分数据结构内容拷贝至子进程
- 添加子进程到系统进程列表当中
fork返回,开始调度器调度
调用 fork() 之后,会分裂出两个完全独立的进程:父进程、子进程;两个进程都会从 fork 之后的代码开始,各自独立往下执行。
fork函数返回值
- 子进程返回 0:子进程永远只有一个父进程,无需标识父进程ID,统一返回0
- 父进程返回 子进程PID(正整数):一个父进程可以创建多个子进程,父进程通过子进程PID区分不同子进程
- 调用失败返回 -1:子进程创建失败,不会生成新进程
写时拷贝
定义:fork 创建子进程时,只拷贝父进程的 PCB 和页表,不拷贝任何物理内存数据;父子进程共享同一块物理内存页,并标记为只读。只有当任意一方尝试写入 / 修改数据时,内核才单独拷贝一份新的物理内存页给该进程,实现地址空间隔离。

核心价值
- 节省物理内存:不做无意义的全额拷贝,只有真正写的时候才分配新内存
- 极大加快 fork 速度:只复制页表,不用拷贝大量数据,fork 瞬间完成
- 保证进程独立性:逻辑上父子进程地址空间完全独立,底层用延迟拷贝优化性能
fork常规用法
- 父子进程执行不同代码逻辑:父进程负责监听、等待、管理资源,子进程负责执行具体业务任务,实现任务并行处理。例如,父进程等待客户端请求,生成子进程来处理请求
- 创建子进程后执行程序替换:子进程通过
exec系列函数加载全新程序
fork调用失败的原因
- 系统进程数达到上限
- 系统内存资源不足
进程终止
进程终止的本质是操作系统回收进程占用的内存、文件等系统资源,销毁进程的代码、数据及相关内核数据结构,暂留 PCB 保存退出状态,等待父进程回收后彻底释放所有内核资源
进程退出场景
-
正常退出 — 代码跑完,结果正确:进程完整执行完所有代码逻辑,程序正常走到末尾
退出码固定为 0(操作系统约定:0 代表进程运行成功。) -
正常退出 — 代码跑完,结果错误:进程依旧完整跑完所有代码,没有崩溃、没有被强行杀死,但是业务逻辑执行失败、功能没达到预期
退出码非 0 值(范围 1~255,不同数字可以用来标识不同错误原因) -
异常终止 — 代码没跑完,强制终止:程序代码没有执行完毕,中途直接结束
没有自定义有效退出码,进程终止原因由 信号编号 标识
退出码
- 进程退出码是子进程终止后存放在 PCB 中的执行状态信息,专门提供给父进程;父进程通过
wait/waitpid读取退出码,用于判断子进程运行结果;若父进程不读取,子进程将滞留 PCB 成为僵尸进程 - main 函数的返回值 = 进程的退出码
echo $?= 打印最近一个进程的退出时的退出码- Linux 错误码是系统调用或库函数调用失败时,内核通过全局变量
errno设置的编号,每个编号对应一种错误原因 - 退出码和错误码没有自动绑定关系,但程序员可以手动把 errno 设为退出码
示例:
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main()
{
int i = 0;
for(; i < 200; i++)
{
printf("%d->%s\n", i, strerror(i));
}
FILE *fp = fopen("log.txt", "r");
if(fp == NULL)
return errno;
return 0;
}
进程常见退出方法
从main函数返回
- main 函数的返回值 = 进程的退出码
main函数结束,表示进程结束;普通函数的return仅代表函数返回- 底层:main 函数执行 return 后,会自动调用 exit() 函数
示例:
#include <stdio.h>
int main() {
printf("进程正常退出\n");
return 0; // 退出码0,代表成功
}
exit() 函数(C 标准库函数)
头文件 & 原型
#include <stdlib.h>
void exit(int status);
- 代码任意位置都能调用(main 函数 / 普通函数 / 循环中),调用后直接终止进程
- 参数
status= 进程退出码(0~255) - 是对系统调用
_exit()的封装增强 - 会刷新所有 IO 缓冲区
示例:
#include <stdio.h>
#include <stdlib.h>
void fun()
{
printf("fun begin!\n");
exit(40);
printf("fun end!\n");
}
int main()
{
fun();
printf("main!\n");
return 0;
}
_exit() 函数(Linux 系统调用)
头文件 & 原型
#include <unistd.h>
void _exit(int status);
- 代码任意位置调用,直接进入内核终止进程。
- 参数
status= 进程退出码。 - 不会刷新 IO 缓冲区
示例:
#include <stdio.h>
#include <unistd.h>
int main() {
printf("Hello World"); // 无换行,数据在缓冲区
_exit(0); // 暴力退出,缓冲区数据丢失,屏幕无输出
}

进程等待
进程等待:父进程通过系统调用(wait/waitpid),主动检测子进程的退出状态,回收子进程的 PCB 内核资源,让子进程彻底消亡的过程。
简单说:子进程退出后,父进程必须来「收尸」,这个收尸动作就是进程等待
进程等待的必要性
- 解决僵尸进程,避免内存泄漏:子进程退出后,PCB资源不会自动释放,会保留退出状态信息,若父进程不等待回收,PCB 会一直占用内核内存 → 僵尸进程,僵尸进程无法被 kill 杀死,大量产生会造成内存泄漏
- 获取子进程退出状态:父进程可以通过等待函数,获取子进程退出码,判断子进程是否正常执行完毕
进程等待的方法
Linux 仅提供两个系统调用实现进程等待
1. wait () 函数(阻塞式等待)
头文件 & 函数原型
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status);
核心功能:阻塞式等待任意一个子进程退出,自动回收子进程资源
参数详解status 是输出型参数(内核向用户传递数据):
- 传
NULL:父进程不关心子进程退出状态,只负责回收资源(最简单用法) - 传
&status(整型变量地址):内核会把子进程的退出信息写入该变量,后续用宏解析
返回值详解
- 成功:返回退出子进程的 PID(>0)
- 失败:返回 -1(父进程没有子进程可等待)
核心特性
- 阻塞等待:父进程调用后会暂停运行(卡住),直到子进程退出才继续执行;
- 等待任意子进程:只要有一个子进程退出,函数立即返回
示例:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{
pid_t id = fork();
if(id == 0)
{
//子进程
int cnt = 5;
while(cnt)
{
printf("我是一个子进程, pid : %d, ppid : %d\n", getpid(), getppid());
sleep(1);
cnt--;
}
exit(0);
}
sleep(10);
//父进程
pid_t rid = wait(NULL);
if(rid > 0)
{
printf("wait sucess, rid: %d\n", rid);
}
sleep(10);
return 0;
}
2. waitpid() (万能等待)
wait() 是 waitpid() 的简化封装,waitpid 功能完全覆盖 wait
头文件 & 函数原型
#include <sys/types.h>
#include <sys/wait.h>
pid_t waitpid(pid_t pid, int *status, int options);
核心功能:支持指定等待某个子进程、非阻塞等待,灵活度拉满
三大参数详解
参数 1:pid_t pid → 指定等待的子进程
pid = -1:等待任意子进程(等价于wait());pid > 0:等待PID 等于该值的指定子进程;pid = 0/pid < -1:等待进程组(极少使用)。
参数 2:int *status → 退出状态
和 wait 的参数完全一致,传 NULL 不关心状态
参数 3:int options → 等待模式
options = 0:阻塞等待(和wait行为一致)options = WNOHANG:非阻塞等待(父进程不卡住,立刻返回)
返回值详解
- 返回 > 0:成功回收子进程,返回子进程 PID;
- 返回 0:仅非阻塞模式生效 → 子进程还未退出;
- 返回 -1:调用失败(无子进程、参数错误)。
核心特性
- 支持指定子进程等待;
- 支持非阻塞等待(父进程可以边等边做其他事)

补充:strerror()(字符串转换函数)
- 头文件:
#include <string.h> - 函数原型:
char *strerror(int errnum); - 作用:把 数字错误码 → 人类能看懂的英文错误描述字符串
- 输入:错误码(如 2)
- 返回值:指向错误信息字符串的指针(如
"No such file or directory") - 示例:
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main()
{
FILE *fp = fopen("log.txt", "r"); // 打开不存在的文件
if(fp == NULL) // 函数调用失败
{
// 打印错误码 + 文字描述
printf("fopen 失败!错误码:%d,错误原因:%s\n", errno, strerror(errno));
return 1;
}
return 0;
}
子进程退出状态 status 参数
status不能简单的当作整形来看待,可以当作位图来看待,status 是 32 位整型,内核只使用 status 的 低 16 位 存储退出信息
我们把 status 的 低 16 位(bit 0 ~ bit 15) 作为研究对象:
1. 子进程正常退出:
高 8 位 = 进程退出码(0~255),低 8 位 = 0
实战举例
- 子进程
exit(1),退出码 =1 - 1 的二进制:
00000001 - 存入高 8 位,低 8 位填 0
- 低 16 位最终二进制:
00000001 00000000 - 转换成十进制:256
所以直接打印status会输出 256,而不是 1
手动解析
// 2. 获取退出码:右移8位,取高8位
(status >> 8) & 0xFF;
2. 被信号杀死:
高 8 位 = 无意义(填 0),第 8 位 = core dump 标志(0/1),低 7 位 = 终止信号编号(1~64)
手动解析
// 2. 获取信号编号:取低7位
status & 0x7F;
解析 status 的 4 个核心宏函数
Linux 提供 4 个宏,专门用来拆封 status
| 宏函数 | 功能作用 | 配套使用宏 |
|---|---|---|
WIFEXITED(status) |
判断子进程是否正常退出,正常返回 非0 | WEXITSTATUS |
WEXITSTATUS(status) |
获取子进程正常退出的退出码(仅在 WIFEXITED 为真时使用) | - |
WIFSIGNALED(status) |
判断子进程是否异常退出(被信号杀死),异常返回 非0 | WTERMSIG |
WTERMSIG(status) |
获取杀死子进程的信号编号(仅在 WIFSIGNALED 为真时使用) | - |
示例:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{
pid_t id = fork();
if(id == 0)
{
//子进程
int cnt = 3;
while(cnt)
{
sleep(3);
printf("我是一个子进程, pid : %d, ppid : %d\n", getpid(), getppid());
sleep(1);
cnt--;
int *p = 0;
*p = 100;
}
exit(1);
}
//父进程
int status = 0;
pid_t rid = waitpid(id, &status, 0);
if(rid > 0)
{
if(WIFEXITED(status))
printf("wait sucess, rid: %d, status: %d, exit signal: %d\n", rid, WEXITSTATUS(status), status&0x7F);
else
printf("子进程异常退出!\n");
}
else
{
printf("wait failed: %d: %s\n", errno, strerror(errno));
}
return 0;
}
阻塞等待与非阻塞等待
1. 阻塞等待(options=0)
定义:
父进程调用等待函数后,暂停自身所有执行,放弃 CPU,进入休眠状态;直到子进程退出,内核唤醒父进程,父进程才恢复运行、回收子进程
2. 非阻塞等待(options=WNOHANG)
定义:
父进程调用等待函数时,设置 WNOHANG 选项:
- 子进程已退出 → 回收并返回子进程 PID
- 子进程未退出 → 函数立刻返回 0,父进程不休眠、不卡死,继续执行自己的任务
- 父进程可以轮询检查子进程状态
示例:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{
pid_t id = fork();
if(id == 0)
{
//子进程
int cnt = 3;
while(1)
{
sleep(3);
printf("我是一个子进程, pid : %d, ppid : %d\n", getpid(), getppid());
sleep(1);
cnt--;
}
exit(1);
}
// 父进程
while(1)
{
int status = 0;
pid_t rid = waitpid(id, &status, WNOHANG);
if(rid > 0)
{
printf("wait sucess, rid: %d, status: %d, exit signal: %d\n", rid, WEXITSTATUS(status), status&0x7F);
break;
}
else if(rid == 0)
{
printf("本轮调用结束,子进程没有退出\n");
sleep(1);
}
else
{
printf("等待失败\n");
break;
}
}
return 0;
}
进程程序替换
通过 exec 系列函数,保留当前进程的壳(PID、PCB、文件描述符、内存结构),彻底替换进程的代码段、数据段、堆、栈,加载一个全新的可执行程序运行。
为什么需要进程程序替换?
用 fork 创建的子进程默认只能执行和父进程一模一样的代码,但实际场景中,我们需要子进程:
- 执行系统命令(
ls/pwd/cp) - 执行另一个自己写的二进制程序
- 执行独立的业务逻辑
此时就必须用程序替换,让子进程抛弃父进程的代码,执行全新程序
程序替换的原理

在不创建新进程、不改变进程 PID、不销毁 PCB 的前提下,清空当前进程用户地址空间的旧代码、旧数据,把磁盘上一个新的可执行程序(ELF 文件)加载到当前进程的地址空间,重置运行上下文,从新程序入口开始执行。
简单来说:换代码、换数据、不换进程、不换 PID、不换 PCB
核心特性
- 调用成功 → 无返回值:原进程的代码、数据被彻底覆盖销毁,函数没有地方可以返回,直接执行新程序
- 调用失败 → 才返回 -1:只有文件不存在、权限不足、路径错误时,才会退回原进程继续执行
exec系列函数
exec 函数,底层功能完全一致,仅传参方式、环境变量、路径搜索不同
命名魔法规则
函数名由 4 个字母 组合而成,每个字母代表一种传参规则:
| 字母 | 英文全称 | 含义 |
|---|---|---|
| l | list | 列表传参:参数逐个手写(如 ls, -l, NULL) |
| v | vector | 数组传参:参数放入 char* 数组,传入数组地址 |
| p | path | 自动搜索 PATH 环境变量:不用写程序全路径 |
| e | env | 支持自定义环境变量:覆盖系统默认环境变量 |



通用头文件
#include <unistd.h>
参数规则
- 第一个参数:要执行的程序路径 / 命令名
- 第二个参数:
argv[0]→ 程序自身名称 - 中间参数:命令选项(如
-l、-a) - 所有参数 / 环境变量,结尾必须加 NULL
- 进程属性(PID、文件描述符、PCB)全程不变
1. execl
- 原型:
int execl(const char *path, const char *arg, ..., NULL); - 特性:列表传参 + 必须全路径 + 默认环境变量
- 示例:
execl("/usr/bin/ls", "ls", "-l", "-a", NULL);
2. execlp(常用)
- 原型:
int execlp(const char *file, const char *arg, ..., NULL); - 特性:列表传参 + 自动搜 PATH + 默认环境变量
- 示例:
execlp("ls", "ls", "-l", "-a", NULL);
3. execv
- 原型:
int execv(const char *path, char *const argv[]); - 特性:数组传参 + 全路径 + 默认环境变量
- 示例:
char *argv[] = {"ls", "-l", NULL};
execv("/usr/bin/ls", argv);
4. execvp
- 原型:
int execvp(const char *file, char *const argv[]); - 特性:数组传参 + 自动搜 PATH + 默认环境变量
- 示例:
char *argv[] = {"ls", "-a", "-l", NULL};
execvp(argv[0], argv);
5. execle
- 原型:
int execle(const char *path, const char *arg,..., NULL, char *envp[]); - 特性:列表传参 + 全路径 + 自定义环境变量
- 示例:
char *env[] = {"MY_TEST=123", NULL};
execle("./test", "test", NULL, env);
6. execvpe
- 原型:
int execvpe(const char *file, char *const argv[], char *const envp[]); - 特性:数组传参 + 搜 PATH + 自定义环境变量
- 示例:
char *argv[] = {"ls", "-l", NULL};
char *env[] = {"MY_ENV=hello", NULL};
execvpe("ls", argv, env);
综合示例:
要求被替换的子进程,使用全新的env列表:通过新增方式给子进程
other.cc文件
#include <iostream>
#include <cstdio>
#include <unistd.h>
int main(int argc, char *argv[], char *env[])
{
std::cout << "hello C++, My Pid is: " << getpid() << std::endl;
for(int i = 0; i < argc; i++)
{
printf("argv[%d]: %s\n", i, argv[i]);
}
printf("\n");
for(int i = 0; env[i]; i++)
{
printf("env[%d]: %s\n", i, env[i]);
}
return 0;
}
proc.c文件
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
char *const addenv[] = {
(char*const)"MYVAL=123456789",
(char*const)"MYVAL1=123456789",
(char*const)"MYVAL2=123456789",
NULL
};
int main()
{
printf("我的程序要运行了\n");
if(fork() == 0)
{
printf("I am Child, My Pid is: %d\n", getpid());
sleep(1);
char *const argv[] = {
(char*const)"other",
(char*const)"-a",
(char*const)"-b",
(char*const)"-c",
(char*const)"-d",
NULL
};
for(int i = 0; addenv[i]; i++)
{
putenv(addenv[i]);
}
extern char **environ;
exit(1);
}
waitpid(-1, NULL, 0);
printf("我的程序运行完毕了\n");
return 0;
}
7. execve(底层唯一系统调用)
- 原型:
int execve(const char *path, char *const argv[], char *const envp[]); - 特性:数组传参 + 全路径 + 自定义环境变量
- 地位:内核只认它,所有库函数最终都调用它
自主Shell命令行解释器
Shell 整体运行框架原理
Shell 的生命周期只有一套无限循环逻辑,这是所有 Shell(bash/zsh/sh)的底层骨架:
启动 Shell → 打印命令提示符 → 获取用户命令 → 解析命令 → 执行命令 → 回到命令提示符(循环)
核心模块实现原理
- 打印命令提示符: 调用系统函数获取 用户名、主机名、当前目录
- 获取用户输入命令: 使用
fgets()读取整行输入(要手动把末尾的\n替换为字符串结束符\0) - 命令解析: 用
strtok()字符串分割函数按空格切割命令,生成argv数组 - 执行命令:
- 内建命令: 不创建子进程,由 Shell 进程自身直接执行系统调用(如:
cd、echo、export) - 普通命令:
fork()创建子进程,子进程execvp("ls", argv)程序替换,执行ls,父进程wait()阻塞等待子进程

补充1:putenv()
- 头文件:
#include <stdlib.h> - 函数原型:
int putenv(char *string); - 作用:为当前进程 添加 / 修改 环境变量
环境变量不存在 → 新增
环境变量已存在 → 覆盖修改
作用范围:仅当前进程 - 参数规则:参数必须是固定格式字符串:
"变量名=变量值" - 示例:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
// 全局变量(永久有效,putenv安全使用)
char env_buf[] = "MYTEST=hello_putenv";
int main() {
// 1. 设置环境变量
putenv(env_buf);
// 2. 获取环境变量(getenv 配套使用)
printf("获取环境变量:%s\n", getenv("MYTEST"));
return 0;
}
补充2:chdir()
- 头文件:
#include <unistd.h> - 函数原型:
int chdir(const char *path); - 作用:修改当前进程的 当前工作目录 (CWD)
- 参数
path:目标路径(可以是绝对路径或相对路径)
示例:"/home"、".."、"~/Desktop"
实现源码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unordered_map>
#define COMMAND_SIZE 1024
#define FORMAT "[%s@%s %s]# "
// 下面是shell定义的全局数据
// 1. 命令行参数表
#define MAXARGC 128
char *g_argv[MAXARGC];
int g_argc = 0;
// 2. 环境变量表
#define MAX_ENVS 100
char *g_env[MAX_ENVS];
int g_envs = 0;
// 3. 别名映射表
std::unordered_map<std::string, std::string> alias_list;
// for test
char cwd[1024];
char cwdenv[1024];
// last exit code
int lastcode = 0;
const char *GetUserName()
{
const char *name = getenv("USER");
return name == NULL ? "None" : name;
}
const char *GetHostName()
{
const char *hostname = getenv("HOSTNAME");
return hostname == NULL ? "None" : hostname;
}
const char *GetHostName()
{
const char *hostname = getenv("HOSTNAME");
return hostname == NULL ? "None" : hostname;
}
const char *GetPwd()
{
//const char *pwd = getenv("PWD");
const char *pwd = getcwd(cwd, sizeof(cwd));
if(pwd != NULL)
{
snprintf(cwdenv, sizeof(cwdenv), "PWD=%s", cwd);
putenv(cwdenv);
}
return pwd == NULL ? "None" : pwd;
}
const char *GetHome()
{
const char *home = getenv("HOME");
return home == NULL ? "" : home;
}
void InitEnv()
{
extern char **environ;
memset(g_env, 0, sizeof(g_env));
g_envs = 0;
// 本来要从配置文件来
// 1. 获取环境变量
for(int i = 0; environ[i]; i++)
{
// 1.1 申请空间
g_env[i] = (char*)malloc(strlen(environ[i])+1);
strcpy(g_env[i], environ[i]);
g_envs++;
}
g_env[g_envs++] = (char*)"HAHA=for_test"; // for_test
g_env[g_envs] = NULL;
// 2. 导成环境变量
for(int i = 0; g_env[i]; i++)
{
putenv(g_env[i]);
}
environ = g_env;
}
// command
bool Cd()
{
if(g_argc == 1)
{
std::string home = GetHome();
if(home.empty()) return true;
chdir(home.c_str());
}
else
{
std::string where = g_argv[1];
// cd - / cd ~
if(where == "-")
{
// Todu
}
else if(where == "~")
{
// Todu
}
else
{
chdir(where.c_str());
}
}
return true;
}
void Echo()
{
if(g_argc == 2)
{
// echo "hello world"
// echo $?
// echo $PATH
std::string opt = g_argv[1];
if(opt == "$?")
{
std::cout << lastcode << std::endl;
lastcode = 0;
}
else if(opt[0] == '$')
{
std::string env_name = opt.substr(1);
const char *env_value = getenv(env_name.c_str());
if(env_value)
std::cout << env_value << std::endl;
}
else
{
std::cout << opt << std::endl;
}
}
}
std::string DirName(const char *pwd)
{
#define SLASH "/"
std::string dir = pwd;
if(dir == SLASH) return SLASH;
auto pos = dir.rfind(SLASH);
if(pos == std::string::npos) return "BUG?";
return dir.substr(pos+1);
}
void MakeCommandLine(char cmd_prompt[], int size)
{
snprintf(cmd_prompt, size, FORMAT, GetUserName(), GetHostName(),DirName(GetPwd()).c_str());
//snprintf(cmd_prompt, size, FORMAT, GetUserName(), GetHostName(),GetPwd());
}
void PrintCommandPrompt()
{
char prompt[COMMAND_SIZE];
MakeCommandLine(prompt, sizeof(prompt));
printf("%s", prompt);
fflush(stdout);
}
bool GetCommandLine(char *out, int size)
{
char *c = fgets(out, size, stdin);
if(c == NULL) return false;
return true;
}
// 3. 命令行分析 "ls -a -l" -> "ls" "-a" "-l"
bool CommandParse(char *commandline)
{
#define SEP " "
g_argc = 0;
// 命令行分析 "ls -a -l" -> "ls" "-a" "-l"
g_argv[g_argc++] = strtok(commandline, SEP);
while((bool)(g_argv[g_argc++] = strtok(nullptr, SEP)));
g_argc--;
return g_argc > 0 ? true : false;
}
void PrintArgv()
{
for(int i = 0; g_argv[i]; i++)
{
printf("argv[%d]->%s\n", i, g_argv[i]);
}
printf("argc: %d\n", g_argc);
}
bool CheckAndExecBuiltin()
{
std::string cmd = g_argv[0];
if(cmd == "cd")
{
Cd();
return true;
}
else if(cmd == "echo")
{
Echo();
return true;
}
else if(cmd == "export")
{
}
else if(cmd == "alias")
{
}
return false;
}
int Execute()
{
pid_t id = fork();
if(id == 0)
{
// child
execvp(g_argv[0], g_argv);
exit(1);
}
int status = 0;
// father
pid_t rid = waitpid(id, &status, 0);
if(rid > 0)
{
lastcode = WEXITSTATUS(status);
}
return 0;
}
int main()
{
// shell启动的时候,从系统获取环境变量
// 我们的环境变量信息应该从父shell统一来
InitEnv();
while(true)
{
// 1. 输出命令行提示符
PrintCommandPrompt();
// 2. 获取用户输入的命令
char commandline[COMMAND_SIZE];
if(!GetCommandLine(commandline, sizeof(commandline)))
continue;
// 3. 命令行分析 "ls -a -l" -> "ls" "-a" "-l"
if(!CommandParse(commandline))
continue;
//PrintArgv();
// 4. 检测并处理内键命令
if(CheckAndExecBuiltin())
continue;
// 5. 执行命令
Execute();
}
//clenup();
return 0;
}
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)