/** * @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; }