文章目录
1.相关知识
1.1字符串切割函数
1.2chdir(): 进入目标路径
1.3putenv()添加一个环境变量
1.4父进程最多能创建几个子进程?
#include <stdio.h>
#include <unistd.h>
int main()
{
int max_cnt = 0;
while(1)
{
pid_t id = fork();
if(id < 0)
{
printf("fork error: %d\n", max_cnt);
break;
}
else if(id == 0)
{
while(1)
{
sleep(1);
}
}
max_cnt++;
}
}
- 最后的max_cnt就是创建的子进程的个数
ps axj | grep "a.out" | grep -v grep | wc -l
: 双引号目的是不计算head那一行(ps axj默认显示head 这里为了更精确的显示进程数)- 此时的a.out成为了孤儿进程
killall a.out
手动杀死
对于进程的认识
之前我们讲过进程是程序运行的实体 如今我们了解到 程序运行前可能就已经创建好了进程 即程序还没运行 也能存在进程
程序替换在Windows下的应用
vs2019运行其上的程序 vs是一个进程 它创建一个子进程进行程序替换来执行用户编写的程序
1.5shell的环境变量来源?
- 环境变量是写在配置文件中的
- shell启动时会从配置文件中读取获取起始的环境变量
2.模拟实现shell
- 模拟实现的shell下删除: ctrl+backspace
- 模拟实现下table/上下左右箭头无法使用[demo]
shell执行的命令通常由两种
- 子进程执行的第二/三方提供的磁盘中有具体二进制文件的可执行程序(pwd/ls/touch)[用户是第一方 OS是第二方]
- 父进程执行的内置命令: shell内部自己实现的方法 如cd/export(这些方法就是让父进程执行来对父进程进行某些操作)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#define NUM 1024
#define SIZE 32
#define SEP " "
//获取用户输入的命令行字符串
char cmd_line[NUM];
//将获取的字符串一个个分割开来
char* g_argv[SIZE];
// 环境变量的buffer 目的是新增的环境变量不被下一次memset清空
char g_tmpenv[64];
// shell运行原理: 父进程解析命令 等待子进程执行命令
int main()
{
// extern char** environ;//debug
//命令行解释器是一个常驻内存的进程(直到用户退出)
while (1)
{
//显示提示信息
printf("[root@localhost myshell]# ");
fflush(stdout);
memset(cmd_line, '\0', sizeof cmd_line);
///获取用户输入的命令行字符串 "ls -a -l -i"
if (fgets(cmd_line, sizeof cmd_line, stdin) == NULL)
{
continue;
}
//"ls -a -l -i\n\0"==>//"ls -a -l -i\0\0" 删除换行符以更贴切xshell
cmd_line[strlen(cmd_line) - 1] = '\0';
//debug: printf("echo: %s\n", cmd_line);//是否成功获得命令行字符串
//命令行字符串解析:"ls -a -l " -> "ls" "-a" "-l"
g_argv[0] = strtok(cmd_line, SEP); //第一次调用传入原始字符串
//一些特殊扩展的选项
int index = 1;
if (strcmp(g_argv[0], "ls") == 0)
{
g_argv[index++] = "--color=auto";
}
if (strcmp(g_argv[0], "ll") == 0)
{
g_argv[0] = "ls";
g_argv[index++] = "-l";
g_argv[index++] = "--color=auto";
}
while (g_argv[index++] = strtok(NULL, SEP)); //后续解析原始字符串,传入NULL
//debug: 查看是否传给解析命令行字符串
//for(index = 0; g_argv[index]; index++)
// printf("g_argv[%d]: %s\n", index, g_argv[index]);
//内置命令/内建命令: 父进程(shell)自己执行的命令 本质是shell中的一个函数调用
if (strcmp(g_argv[0], "cd") == 0)
{
if (g_argv[1] != NULL)
chdir(g_argv[1]);
continue;
}
if (strcmp(g_argv[0], "export") == 0 && g_argv[1] != NULL)
{
strcpy(g_tmpenv, g_argv[1]);
putenv(g_tmpenv);
//debug1: 查看是否传给添加env
//int ret = putenv(g_tmpenv);
//if (ret == 0)
// printf("%s export success\n", g_argv[1]);
//
//debug2: 显示所有env来查看是否传给添加env
//for(int i = 0; environ[i]; i++)
// printf("%d: %s\n", i, environ[i]);
continue;
}
//子进程执行获取到的命令
pid_t id = fork();
if (id == 0)
{
//debug: 子进程被替换前是否拿到env
//printf("child's TMPENV: %s\n", getenv("TMPENV"));
//printf("child's PATH: %s\n", getenv("PATH"));
//ls -a -l
//execvpe(g_argv[0], g_argv, environ);//ok: 显示传env 但没必要
execvp(g_argv[0], g_argv);//ok test.c成功获取env
exit(1);
}
//father
int status = 0;
pid_t ret = waitpid(id, &status, 0);
if (ret > 0)
printf("exit code: %d\n", WEXITSTATUS(status));
}
}