20155321 第七周课堂实践

实践任务

  • 使用fork,exec,wait实现mybash
  • 写出伪代码,产品代码和测试代码
  • 发表知识理解,实现过程和问题解决的博客(包含代码托管链接)

预备知识

  • fork()
    • 目的:创建一个与原来进程几乎完全相同的进程,但是假如两个进程也可以完成不同的请求
    • 练习代码如下:
   #include <unistd.h>  
   #include <stdio.h>   
   int main ()   
    {   
        pid_t pid; //fpid表示fork函数返回的值  
        int count=0;  
        pid=fork();   
    
        if (pid < 0)   
            printf("error in fork!");   
        else if (pid == 0) {  
            printf("i am the child process, my process id is %d/n",getpid());   
            count++;  
        }  
        else {  
            printf("i am the parent process, my process id is %d/n",getpid());   
            count++;  
        }  
        printf("统计结果是: %d/n",count);  
        return 0;  
    } 
  • 执行结果

  • 注意点:第二个count值不会是2,因为count是一个独立变量,而不是父进程和子进程共用的

  • 参考资料

  • exec()

    • 目的:提供一个在进程中启动另一程序执行的方法。它可根据指定的文件名或目录名找到可执行文件,并用它来取代原调用进程的数据段、代码段和堆栈段。执行完后,原调用进程的内容除了进程号外,其他均被新程序的内容替换
    • 函数原型:int execve(const char *filename, char *const argv[], char *const envp[]);其中执行程序由filename决定,filename必须是一个二进制的可执行文件
  • wait()

    • 目的:用在父进程中等待回收子进程的资源,而防止僵尸进程的产生
  • 函数原型:int waitpid(pid_t pid, int* statloc, int options);

  • 参考资料

任务分析

  • bash是一个交互型的应用程序,主要解析并代表用户执行一系列的命令。

  • 在我的mybash中主要实现两个功能,分别是若用户输入的是Linux命令,则直接执行此命令;若用户输入的是一个可执行文件则运行此文件。

  • 因此可得出初步的思路

主函数{
    while(1)  //不断循环执行命令
    {
        得到用户输入的命令;
        调用子函数1解析此命令
    }
}

子函数1{
    调用子函数2对用户输入的字符串进行解析,根据空格区分各个参数,为之后调用execve函数做准备;
    
    调用execve函数执行子进程;
    
}
  • 具体实现代码如下,参考了课本P513-P526的内容
#include <stdio.h>
#include <unistd.h>
#include <wait.h>
#include <stdlib.h>
#include <string.h>
#define MAX 128

void eval (char *cmdline);  //对用户输入的命令进行解析
int parseline (char *buf, char **argv);
int builtin_command(char **argv);

int main()
{
	char cmdline[MAX];
	
	while(1){
		printf("> ");
		fgets(cmdline,MAX,stdin);
		if(feof(stdin))
		{
			printf("error");
			exit(0);
		}
		eval(cmdline);
	}
}

void eval(char *cmdline)
{
	char *argv[MAX];
	char buf[MAX];
	int bg;
	pid_t pid;
	char *envp[]={0,NULL};
	strcpy(buf,cmdline);
	bg = parseline(buf,argv);//解析以空格分隔的命令行参数,填入argv数组中
	if(argv[0]==NULL)
		return;
	if(!builtin_command(argv)) //调用此函数检查用户输入的命令是否为内置的Linux命令,是则执行并返回1,不是则返回0
	{	
	if((pid=fork()) == 0)
	{
		if(execve(argv[0],argv,envp) < 0) {
			printf("%s : Command not found.
",argv[0]);
			exit(0);
		}
	}

	if(!bg){
		int status;
		if(waitpid(-1,&status,0) < 0)  //相当于调用wait函数
			printf("waitfg: waitpid error!");
	}
	else
		printf("%d %s",pid, cmdline);
	return;
	}
}

int builtin_command(char  **argv)
{
	if(!strcmp(argv[0], "quit"))
		exit(0);
	if(!strcmp(argv[0],"&"))
		return 1;
	return 0;
}

int parseline(char *buf,char **argv)//解析以空格分隔的命令行参数,并构造最终传给execve函数的argv向量
{
	char *delim;
	int argc;
	int bg;

	buf[strlen(buf)-1]=' ';
	while(*buf && (*buf == ' '))
		buf++;

	argc=0;
	while( (delim = strchr(buf,' '))){   //从字符串buf中寻找空格字符第一次出现的位置
		argv[argc++] = buf;
		*delim= '';
		buf = delim + 1;
		while(*buf && (*buf == ' '))
			buf++;
	}

	argv[argc] = NULL;
	if(argc == 0)
		return 1;
	if((bg=(*argv[argc-1] == '&')) != 0)
		argv[--argc] = NULL;
	return bg;
}

问题及解决

  • 一开始在执行命令的时候,直接输入ls、ps命令发现没办法正常执行,但是执行一个可执行文件就没有问题

    根据提示得知,是因为没有找到此命令,猜测应该是路径有问题,第一个命令行参数argv[0]应该给出完整的路径名,因此将命令改为/bin/ls、/bin/ps就没问题了

  • 目前的程序在执行Linux命令时还是需要把完整路径名给打出来,还不能像终端那样直接打命令,在这一点上要是能改进就好了

  • 运行结果如下

原文地址:https://www.cnblogs.com/rafell/p/7690764.html