进程替换(简单的函数使用)

什么是进程替换

进程替换就是用新进程来替换当前进程. 在 bash 中, 进程替换其实就是命令替换和管道的组合.

替换原理:
我们知道子进程存在的意义不仅仅是它能帮助父进程进行压力分摊, 最主要的功能其实是让子进程去完成其他的工作, 也就是进程替换. 子进程通过调用 exec 系列函数时, 当前进程的虚拟地址空间上的各个数据段被磁盘上指定的新程序给替换掉, 本质上并没有创建新的进程, 进程的 ID 也没有发生变化.
替换函数:

#include<unistd.h>

int execl(const char *path, const char *arg, ...);
int execle(const char *path, const char *arg, ..., char *const envp[]);
int execv(const char *path, char *const argv[]);
int execve(const char *path, char *const argv[], char *const envp[]);
int execlp(const char *file, const char *arg, ...);
int execvp(const char *file, char *const argv[]);
1l(list):表示参数采用列表。 
(2v(vector):参数用数组。 
(3p(path):自动搜索环境变量PATH。 
(4e(env):自己维护环境变量。 

在这里插入图片描述

函数关系:
在这里插入图片描述

事实上, 只有 execve 是系统调用接口, 其他都是通过调用 execve 来实现的

进程替换的简单使用:

execl:

#include <stdio.h>
#include <unistd.h>
int main() {
	execl("/bin/ls", "ls", "-l", NULL);
	return 0;
}

execlp:

#include <stdio.h>
#include <unistd.h>
int main() {
	execlp("ls", "ls", "-l", NULL);
	return 0;
}

execle:

#include <stdio.h>
#include <unistd.h>
int main() {
	char* const envp[] = {
		"PATH=/bin:/usr/bin",
		"TERM=console",
		NULL
	};
	execle("/bin/ls", "ls", "-l", NULL, envp);
}

execv:

#include <stdio.h>
#include <unistd.h>
int main() {
	char* const argv[] = {
		"ls",
		"-l",
		NULL
	};
	execv("/bin/ls", argv);
}

execvp:

#include <stdio.h>
#include <unistd.h>
int main() {
	char* const argv = {
		"ls",
		"-l",
		NULL
	};
	execvp("ls", argv);
}

execve:

#include <stdio.h>
#include <unistd.h>
int main() {
	char* const argv[] = {
		"ls",
		"-l",
		NULL
	};
	char* const envp[] = {
		"PATH=/bin:/usr/bin",
		"TERM=console",
		NULL
	};
	execve("/bin/ls", argv, envp);
}

效果图:
在这里插入图片描述

当然, 在实际应用中我们一般是将 exec 系列函数放到子进程中, 让子进程进行进程替换, 来完成其他任务.

minishell(迷你shell)

通过进程替换来实现一个简单的 minishell:

#include <stdio.h>                                                                                                                      
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <string.h>
// 命令行
char command[1024];
int do_face() {
  // 初始化命令行缓冲区 --- 全局变量默认初始化为 NULL
  memset(command, 0x00, 1024);
  printf("[user@localhost]$ ");
  // 刷新缓冲区 --- 将缓冲区中的内容输出到屏幕上
  fflush(stdout);
  // %[^\n]: 接收到 '\n' 就停止接收
  // %*c 接收最后一个字符但是不赋予 command
  if (scanf("%[^\n]%*c", command) == 0) {
    // 如果只输入 '\n' 则将 '\n' 吃掉
    getchar();
    return -1;
  }
  return 0;
}
// 将命令行中的字符串切割成各个命令
char** do_parse(char* cmd) {
  // 保存各个字符串命令
  static char* argv[32];
  // 下标
  int argc = 0;
  char* cur = cmd;
  while (*cur != '\0') {
    if (*cur != ' ') {
      argv[argc++] = cur;
      while (*cur != ' ' && *cur != '\0') {
        ++cur;
      }
      if (*cur != '\0') {
        *cur = '\0';
        ++cur;
      }
    }
    while (*cur != '\0' && *cur == ' ') {
      ++cur;
    }
  }
  // 在最后一个命令后面加 NULL
  argv[argc] = NULL;
  return argv;
}
// 进程替换, 让子进程来完成shell调用各个命令的任务
void do_exec() {
  pid_t pid = fork(); 
  char** argv = {NULL};
  if (pid < 0) {
    perror("fork error");
    exit(-1);
  } else if (pid == 0) {
    argv = do_parse(command);
    // 如果命令行不为空, 则进程替换
    if (argv[0] != NULL) {
      execvp(argv[0], argv);
    } else {
      exit(0);
    }
  } else {
    // 进程等待
    waitpid(pid, NULL, 0);
  }
}
int main() {
  while (1) {
    if (do_face() < 0) {
      continue;
    }
    do_exec();
  }
  return 0;
}                              

在这里插入图片描述

参考文章:
进程的替换
进程替换

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值