用fock实现简易shell(程序替换)

本文详细介绍了如何通过fork创建子进程,并利用exec函数族替换进程的用户空间代码和数据,实现进程的功能转换。同时,提供了简单的shell实现示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

fork之后就会创建则进程,数据、堆、栈有两份,代码仍然为一份但是这个代码段成为两个进程的共享代码段都从fork函数中返回,当父子进程有一个想要修改数据或者堆栈时,两个进程真正分裂子进程在创建的时候会复制父进程的代码。

不管是共用代码还是复制代码,这就相当于父进程创建了一个和自己功能完全相同的进程,这样一来,子进程就只能进行和父进程一样的操作动作,这样做实际意义并不大,那怎么让子进程干和父进程不一样的事情?

程序替换:子程序通常调用exec函数,替换掉该进程的用户空间代码和数据,从新程序的启动历程开始执行,exec函数并不是创建进程,所以exec之后,进程的id并未改变

exec系列函数

需要头文件  #include <unistd.h>

   int execl(const char *path, const char *arg, ...);
   int execlp(const char *file, const char *arg, ...);
   int execle(const char *path, const char *arg, ..., char * const envp[]);
   int execv(const char *path, char *const argv[]);
   int execvp(const char *file, char *const argv[]);
   int execvpe(const char *file, char *const argv[], char *const envp[]);

l(list):表示采用列表

v(vector):参数用数组

p(path):自动搜索环境变量

e(env):自己维护的环境变量

集体操作看下面代码

#include<stdlib.h>
#include<unistd.h>

int main()
{
    char* const argv[] = {"ps", "-ef", NULL};
    char* const envp[] = {"PATH=/bin:/usr/bin", "TERM=console", NULL};

    execl("/bin/ps", "ps", "-ef", NULL);

    //带p可以使用环境变量PATH,无需路径
    execlp("ps", "ps", "-ef", NULL);

    //带e需要自己组装环境变量
    execle("ps", "ps", "-ef", NULL, envp);

    execv("/bin/ps", argv);

    execvp("ps", argv);

    execve("/bin/ps", argv, envp);

    exit(0);
}

这个程序只需要执行一个上述 exec 函数,就可将这段代码生成的可执行程序编程ps -ef 这条命令,上述代码只有一个进程,换成两个进程,基本操作也是一样的,如下面代码,实现最简shell:

#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<sys/wait.h>
#include<stdlib.h>

char *strs[128] = {};
int argc = 0;

void stok(char *buf, const char *ch)
{
    if(buf == NULL)
    {
        return;
    }
    char *token = strtok(buf, ch);
    strs[argc] = token;
    while((token = strtok(NULL, ch)) != NULL)
    { 
        argc++;
        strs[argc] =token;
    }
    strs[argc+1] = NULL;
}

int main()
{
    char buf[256] = {};
    while(1)
    {
        pid_t pid = fork();
    
        if(pid == 0)
        {
            printf("myshell#");
            scanf("%[^\n]%*c", buf);    
            stok(buf, " "); //打断buf字符串
            execvp(strs[0], strs);
        }
        else
        {
            int status = 0;
            pid_t id = waitpid(pid,&status, 0);   
            if(id < 0)
            {
                printf("wait runing error!\n");
            }
        }
    }
    return 0;
}

在这段代码中,子程序的代码被替换,父进程调用waitpid阻塞式等待子进程返回,子进程返回之后回收子进程的资源,while循环中使用fork,创建子进程,然后在子进程中读取键盘上的输入,并将读进的字符在串在空格处打断,并在每个子串的最后加上\0,让strs数组中的每个指针都依次指向这些子串,完成之后在strs最后一个元素的后面加上一个空指针,这样做的是为了满足execvp的格式要求,然后调用execvp进行程序替换,替换完完毕之后子程序退出,父进程会受到子进程的资源,然后进入下一次循环。


 scanf("%[^\n]%*c", buf); 

使用snanf是最后的回车也会被输入,上面这句代码的意思是:当敲下回车时,这个回车会被屏蔽掉,也可写为:

fgets(buf,sizeof(buf),stdin);
buf[strlen(buf)-1]=0;






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值