20165329 mybash的實現
20165329 mybash的實現
實驗要求
- 使用fork,exec,wait實現mybash
- 寫出虛擬碼,產品程式碼和測試程式碼
- 發表知識理解,實現過程和問題解決的部落格(包含程式碼託管連結)
使用fork,exec,wait實現mybash
fork
首先用man
命令檢視fork
的使用方法
- 其功能
通過複製呼叫fork的程序建立一個新程序。
- 返回值
建立子程序成功時,fork在父程序中返回子程序的pid,在子程序中返回0。失敗時,在父程序中返回-1,無子程序被建立,相應的錯誤在errno中設定。
-
[fork建立子程序程式碼]
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
char *msg;
int n;
pid_t pid;
pid = fork();
if (pid < 0) {
perror("fork failed");
exit(1);
}
if (pid == 0) {
msg = "This is in child progress\n";
n = 6;
} else {
msg = "This is in parent progress\n";
n = 3;
}
for (; n > 0; --n) {
printf("%s", msg);
//sleep(1);
}
return 0;
} - fork建立子程序程式碼
將程式碼中加入sleep(1)重新編譯並執行程式得到執行結果:
其不同的原因為當程式執行fork()語句後就又多出了一個程序,程序的執行符合“多個程序的執行方式”。
exec()
首先用man
命令檢視exec
的使用方法
- 其功能
當程序呼叫一種exec函式時,該程序的使用者空間程式碼和資料完全被新程式替換,從新程式的啟動例程開始執行。
- 返回值
這些函式如果呼叫成功則載入新的程式從啟動程式碼開始執行,不再返回,如果調用出錯則返回-1,所以exec函式只有出錯的返回值而沒有成功的返回值。
-
exec_call.c
int main(void)
{
printf("hello, world\n");
return 0;
}
-
exec_test.c
#include <unistd.h>
#include <stdio.h>
int main(void)
{
char *const argv[] ={"exec_call", NULL};
execv("./exec_call", argv);
printf(“here here\n”);
return 0;
} - 驗證exec功能程式碼
當程序呼叫execv函式時,該程序的使用者空間程式碼和資料完全被exec_call替換,從exec_call的啟動例程開始執行。呼叫execv程序後面的程式碼沒有被執行。所以出現了錯誤。
- 解決方法
在“fork建立子程序程式碼”中加入execv函式,改進的函式pro_control3.c:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
char *msg;
int n;
pid_t pid;
pid = fork();
if (pid < 0) {
perror("fork failed");
exit(1);
}
if (pid == 0) {
msg = "This is in child progress\n";
n = 6;
char *const argv[] ={"exec_call", NULL};
execv("./exec_call", argv);
}
else {
msg = "This is in parent progress\n";
n = 3;
}
for (; n > 0; --n) {
printf("%s", msg);
//sleep(1);
}
return 0;
}
wait
首先用man
命令檢視wait
的使用方法
- 功能
對於一個已經終止的子程序,用wait能夠讓系統釋放與子程序相關的資源;如果不用wait則終止的程序會變為殭屍程序。
- 返回值
若呼叫成功則返回清理掉的子程序id,若調用出錯則返回-1。
mybash程式碼
- 虛擬碼
- 對使用者輸入的命令進行解析
- 檢查使用者輸入的命令是否為內建的Linux命令
- 構造argv向量
-
mybash.c程式碼:
#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.\n",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= '\0';
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;
}
- 結果
實驗中的問題
實現mybash功能一開始沒有加/bin,ls不能用,後面發現在前面加上“/bin”就可以了。
感想
本題根據網上搜索查詢然後慢慢的一步一步做,深有體會,自己學的很多不足,以後的努力學好每一個知識點。