涉及到的相关函数
获取用户名 :
uid_t getuid(void);
struct passwd *getpwuid(uid_t uid);
//getpwuid 返回一个结构体指针,可以用来获取下列结构
struct passwd {
char *pw_name; /* username */
char *pw_passwd; /* user password */
uid_t pw_uid; /* user ID */
gid_t pw_gid; /* group ID */
char *pw_gecos; /* user information */
char *pw_dir; /* home directory */
char *pw_shell; /* shell program */
};
获取当前工作目录 :
char *getcwd(char *buf, size_t size);
getcwd可以将当前工作的绝对路径填充进buf指向的空间;
大小不超过size; 失败返回NULL指针, 并设置errno;
切换工作路径 :
int chdir(const char *path);
改变当前进程的工作路径,成功返回 0, 失败返回-1;
并设置errno;
The current working directory is the starting point for interpreting relative pathnames (those not starting with '/').
切换路径后修改命令行的提示
void swim_dir() {
if (chdir(vec[1]) < 0){
perror("chdir()");
return;
}
has_ch_dir = 1;
}
重定位功能相关函数
复制文件描述符
int dup(int oldfd); int dup2(int oldfd, int newfd); //dup用未使用的最小文件file descriptor 复制当前oldfd,并返回; //dup2将之前打开的newfd用来复制oldfd, 它会前关闭之前的newfd再 //指向oldfd,如果oldfd是无效的,那么它啥事也不做; 失败返回:-1,并设置errno;
啥是文件描述符 ?
文件描述符是数组下标, 数组里存放了打开文件结构体的指针, 指向的结构体里又有指向inode的指针; 该数组存在在每个进程空间里, 系统会默认打开默认stream: stdin/stdout/stderr;

fork() 复制产生子进程
fork可以产生一个新进程,通过duplicate 当前进程
fork后父子进程的区别:
- fork的返回值不一样, pid不一样, ppid不一样;
- 未决信号和文件锁不继承
- 资源利用量清零;
注意 : fork()进行写时复制(谁改变原内容, 谁复制)
pipe() 实现IPC机制
调用pipe函数时在内核中开辟一块缓冲区(称为管道)用于通信, 有一个读端和写端, 通过 filedes参数传给用户程序的两个文件描述符, filedes[0]指向管道的读端, filedes[1]指向管道的写端;
/* On Alpha, IA-64, MIPS, SuperH, and SPARC/SPARC64; see NOTES */
struct fd_pair {
long fd[2];
};
struct fd_pair pipe();
/* On all other architectures */
int pipe(int pipefd[2]);

管道用法的栗子
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(){
pid_t pid;
int fd[2];
if (pipe(fd) < 0) {
perror("pipe()");
exit(1);
}
if ((pid = fork()) < 0){
perror("fork()");
exit(1);
}
int n;
char buf[20];
if (pid > 0) {
close(fd[0]);
write(fd[1], "hello pipe\n", 11);
wait(NULL);
}
else{
close(fd[1]);
sleep(1);
n = read(fd[0], buf, 20);
write(1, buf, n);
}
return 0;
}
命令的执行相关函数
当进程调用一种exec函数时, 改函数的用户空间代码和数据完全被新程序替代, 从新程序的启动例程开始执行
//exec函数族
int execl(const char *pathname, const char *arg, .../* (char *) NULL */);
int execlp(const char *file, const char *arg, ... /* (char *) NULL */);
int execle(const char *pathname, const char *arg, ... /*, (char *) NULL, char *const envp[] */);
int execv(const char *pathname, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[], char *const envp[]);
调用exec函数的调用规则:
一般父进程fork()后, 子进程调用exec函数族执行新的进程, 父进程调用wait()可以等待子进程退出
// fork()/ wait() / execl()
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main(){
pid_t pid;
puts("Begin!");
fflush(NULL);
//注意在fork前刷新缓冲区;
pid = fork();
if (pid < 0) {
perror("fork()");
exit(1);
}
if (pid == 0) {
//子进程执行replace成其他进程映像;
execl("/bin/date", "date", "+%s", NULL);
perror("execl()");
exit(1);
}
wait(NULL);
//父进程隔这儿等着收尸
puts("End!");
return 0;
}
程序流程图

代码总览
/***************************************************************
> File Name: fish.c
> Author: Feiger
> Mail:1162006607@qq.com
> Created Time: Mon 24 May 2021 09:15:26 PM CST
**************************************************************/
#include <pwd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <glob.h>
#include <sys/wait.h>
#include <sys/fcntl.h>
#include <time.h>
#define MAX_N 1024
#define N 64
#define RED(msg) "\033[31m"#msg"\033[0m"
#define GOLD(msg) "\033[33m"#msg"\033[0m"
#define BLUE(msg) "\033[32m"#msg"\033[0m"
char *vec[MAX_N] = {0};
char *cmd[N][N] = {0};
char talk[][MAX_N] = {
{"Blue... Blue!! Blue ?\n"},
{"Are you teaching me how to do the job?\n"},
{"I am a fish, not a shell, try some simple cmd!\n"},
{"How dare you let me do so many homework?\n"},
{"Can you say the language of fish ??\n"},
{"Let me do some simple work!\n"}
};
int has_ch_dir = 1;
char path[MAX_N] = {0};
struct passwd *usr;
void prompt() {
char buf[MAX_N] = {0};
if (has_ch_dir) {
has_ch_dir = 0;
if (getcwd(buf, MAX_N) == NULL) {
perror("getcwd()");
exit(1);
}
int cnt = 0;
for (int i = 0; buf[i]; i++) {
if (buf[i] == '/') {
cnt = 0;
continue;
}
path[cnt++] = buf[i];
}
path[cnt] = '\0';
}
fprintf(stdout, RED(%s) "@" GOLD(fish:), usr->pw_name);
fprintf(stdout, BLUE(%s $), path);
}
void analysis(char *str, char **vec) {
char argu[MAX_N][MAX_N] = {0};
memset(vec, 0, sizeof(char *) * MAX_N);
int cnt = 0;
for (int i = 0,j = 0,t = 1; str[i] && str[i] !='\n'; i++){
if (str[i] == ' ') {
if (t) {cnt++,j = 0, t = 0;}
continue;
}
t = 1;
argu[cnt][j++] = str[i];
}
if (argu[0][0] == 0) return;
for (int i = 0; i <= cnt; i++) {
vec[i] = strdup(argu[i]);
}
return;
}
void my_execv() {
pid_t cpid;
if ((cpid = fork()) < 0) {
perror("fork()");
return;
}
if (cpid == 0) {
execvp(vec[0], vec);
perror("myfish");
exit(1);
}
else{
int t;
if (wait(&t) < 0){
exit(1);
}
}
}
int redirctB() {
int fd = open(cmd[1][0], O_WRONLY|O_CREAT, 0644);
if (fd < 0) {
perror("open()");
return 1;
}
fflush(stdout);
int out = dup(STDOUT_FILENO);
if (dup2(fd, STDOUT_FILENO) < 0 || out < 0){
perror("dup()");
return 1;
}
pid_t pid = fork();
if(pid < 0) {
perror("fork()");
return 1;
}
if (pid == 0) {
execvp(cmd[0][0], cmd[0]);
perror("execvp()");
exit(1);
}
dup2(out, STDOUT_FILENO);
close(fd);
wait(NULL);
return 0;
}
int redirctL() {
int fd = open(cmd[1][0], O_RDONLY);
if (fd < 0) {
perror("open()");
return 1;
}
fflush(stdin);
int infd = dup(STDIN_FILENO);
if (dup2(fd, STDIN_FILENO) < 0 || infd < 0){
perror("dup()");
return 1;
}
pid_t pid = fork();
if (pid < 0) {
perror("fork()");
return 1;
}
if (pid == 0) {
execvp(cmd[0][0], cmd[0]);
perror("execvp()");
exit(1);
}
dup2(infd, STDIN_FILENO);
close(infd);
wait(NULL);
return 0;
}
int redirPip() {
int fd[2];
pid_t pid1, pid2;
if (pipe(fd) < 0) {
perror("pipe()");
return 1;
}
fflush(stdout);
pid1 = fork();
if (pid1 < 0) {
perror("fork()");
return 1;
}
if (pid1 == 0) {
close(fd[0]);
dup2(fd[1], STDOUT_FILENO);
execvp(cmd[0][0], cmd[0]);
perror("execvp()");
exit(1);
}
pid2 = fork();
if (pid2 < 0){
perror("fork()");
return 1;
}
if (pid2 == 0){
close(fd[1]);
dup2(fd[0], STDIN_FILENO);
execvp(cmd[1][0], cmd[1]);
perror("execvp()");
exit(1);
}
close(fd[0]);
close(fd[1]);
waitpid(pid1, NULL, 0);
waitpid(pid2, NULL, 0);
return 0;
}
void swim_dir() {
if (chdir(vec[1]) < 0){
perror("chdir()");
return;
}
has_ch_dir = 1;
}
int redirect() {
char *flag = NULL, cnt = 0;
if (vec[0] == NULL) return 0;
if (!strcmp(vec[0], "cd")) {
swim_dir();
return 0;
}
memset(cmd, 0, sizeof(cmd));
for (int i = 0,t = 0; vec[i]; i++) {
if (!strcmp(vec[i], "|")||!strcmp(vec[i], "<") || !strcmp(vec[i], ">")) {
flag = strdup(vec[i]);
cnt++;
t = 0;
continue;
}
cmd[cnt][t++] = vec[i];
}
if (strcmp(cmd[0][0], "blue") == 0) {
printf("%s", talk[0]);
return 0;
}
srand(time(0));
if (cnt > 1) {
printf("%s", talk[rand() % 6]);
return 0;
}
if (flag == NULL) {
my_execv();
return -1;
}
if(strcmp(flag, ">") == 0) {
redirctB();
}
else if (strcmp(flag, "<") == 0){
redirctL();
}
else if (strcmp(flag, "|") == 0){
redirPip();
}
return 0;
}
int main(int argc, char *argv[]){
char *buff = NULL;
size_t buff_size = 0;
fprintf(stdout, BLUE( Blue. Blue. Blue...\n));
fprintf(stdout, "I'm a fish, nice to meet you!\n\n");
usr = getpwuid(getuid());
if (usr == NULL) {
perror("getpwuid()");
exit(1);
}
while (1) {
prompt();
if (getline(&buff, &buff_size, stdin) < 0){
perror("getline()");
break;
}
analysis(buff, vec);
redirect();
}
return 0;
}
运行截图:



本文详细介绍了使用C语言实现Shell的过程,包括获取用户名、切换工作目录、重定位功能相关函数如fork(),以及命令执行的相关函数。通过示例和程序流程图,帮助读者理解如何在Linux环境下创建和执行进程。


被折叠的 条评论
为什么被折叠?



