/**
* @file myshell.c
* @Synopsis
* 简单的交互式shell
* 用户输入一行命令 实现和shell 进程一样的效果。。
* example:
* cat demo1.txt
* ps -e
* ls -lath
* top
* who
* ....
* 输入上面这些命令和shell 实现一样的效果。
*
* 功能:
* 子父进程使用管道实现命令传输,
* 命令参数从终端输入,
* 处理参数,由父进程处理写入管道。
* 子进程从管道读取传输的命令,
* 然后交给execlp 处理。
* 这里也就是交给子进程来处理。
* 当该子进程调用这个函数时,该进程的用户空间代码和
* 数据完全被新程序替换(换脑),从新程序的启动例程开始执行。
*
* 类似于shell 层。
* shell 层属于父级,
* 当用户从终端输入命令时,
* shell 会调用fork cp 出一个新的shell 进程,
* 然后新的shell 进程调用 exec 执行新的程序。
* @author MrClimb
* @version 1.1.0
* @date 2012-05-18
*/ #include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h> int main(int argc, char **argv)
{
pid_t pid;// 进程编号
/**
* pipefd[0] stand for read file description
* pipefd[1] stand for write file description
*/
int pipefd[2];// 文件描述符
char buffer[BUFSIZ];//1024*8 = 8192
char *arguments[BUFSIZ];
size_t st;
ssize_t sst; while(1){
printf("input shell(exit):");
memset(buffer,0,BUFSIZ);
/**
* create pipe
* int pipe(int pipefd[2]);
* 创建管道
* 传出两个参数
*/
if(pipe(pipefd)==-1)
{
perror("cannot create pipe");
exit(1);
}
char param1[BUFSIZ];
char inputp[BUFSIZ];
memset(inputp,0,BUFSIZ);
// 获得终端的命令输入
fgets(inputp,BUFSIZ,stdin);
// 处理 \n 字符 但这里不完全正确。。
inputp[strlen(inputp)-1]='\0';
if(strcmp(inputp,"exit")==0)
{
exit(1);
}
int i=0;
char *sep = strtok(inputp," ");
strcpy(buffer,sep); int len1 = strlen(buffer);
arguments[0] = (char *)malloc(len1+1);
strcpy(arguments[0],buffer);
i++;
while((sep=strtok(NULL," "))!=NULL)
{
len1 = strlen(buffer);
arguments[i] = (char *)malloc(len1+1);
strcpy(arguments[i],sep);
i++;
}
arguments[i]=NULL; // 开始创建一个进程
pid = fork();
if(pid==0)
{
// puts("child into...");
int len = strlen(param1);
st = read(pipefd[0],buffer,len);
if(st<0)
{
printf("read failure...\n");
continue;
}
// 这里关掉子进程 的写入管道
// 只让子进程读数据
close(pipefd[1]);
// usleep(100);
// 进行exec 糸统调用
execvp(buffer,arguments);
}else if(pid>0)
{
// 关掉父进程从管道读取
// 这样子父行成 管道实现 环形队列 这样实现了进程间通信
close(pipefd[0]);
// puts("parent into...");
int len = strlen(param1);
// 向管道端写入
sst = write(pipefd[1],param1,len);
if(sst<0)
{
printf("write failure.....\n");
}
/**
* 这里用wait 而不用usleep
* 一个进程在终止时会关闭所有文件描述符,释放在用户空间分配的内存,
* 但PCB 还保留着,内核其中保存了一些信息,如果是正常终止则保存着退出状态,
* 如果异常终止则保存着导致该进程终止的信号是哪个。这时候可以调用wait 获取
* 这些信息,然后彻底清除掉这个进程。
* 如果一个进程已经终止,但是它的父进程尚未调用wait or waitpid 对它进行清理,这时的进程装态称为
* 僵尸zombie 进程。这里的wait 使父进程 阻塞 等待子进程终止,
* ps u 查看到信息usleep 与 wait 两都的区别。。。
* usleep 僵尸进程没有被清除。
* wait 先让子进程 终止,也可以说去清理子进程
*
*/
// usleep(100);
wait(NULL);
/**
* 父进程作清理工作。。
* 释放内存。。
*/
int i=0;
for(;arguments[i]!=NULL;i++)
{
printf("arguments[%d] memory free!\n",i);
free(arguments[i]);
}
}else{
perror("cannot fork!");
exit(1);
}
}
return 0;
}