先看下代码
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<ctype.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<errno.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<assert.h>
#include<string.h>
#define NUM 1024
#define OPT_NUM 64
#define trimspace(start) do{\
while(isspace(*start))\
start++;\
}while(0)
#define NONE_REDIR 0
#define INPUT_REDIR 1
#define OUTPUT_REDIR 2
#define APPEND_REDIR 3
char linecommand[NUM];
char *myargv[OPT_NUM];//指针数组
int redirnum=0;
char*redirname=NULL;
void commandcheck(char *commands)
{
assert(commands);
char*start=commands;
char*end=commands+strlen(commands);
while(start<end)
{
if(*start=='>')
{
*start='\0';
start++;
if((*start)=='>')
{
redirnum=APPEND_REDIR;
start++;
trimspace(start);
redirname=start;
}
else
{
redirnum=OUTPUT_REDIR;
trimspace(start);
redirname=start;
}
break;
}
else if(*start=='<')
{
*start='\0';
start++;
trimspace(start);
redirname=start;
redirnum=INPUT_REDIR;
break;
}
else
{
start++;
}
}
}
int main()
{
int lastcod;
int lastsig;
while(1)
{
errno=0;
redirnum=0;
redirname=NULL;
errno=0;
printf("用户名@主机名 当前路径# ");
fflush(stdout);
char* s=fgets(linecommand,sizeof(linecommand)-1,stdin);
assert(s!=NULL);
linecommand[strlen(linecommand)-1]=0;
(void)s;
commandcheck(linecommand);
myargv[0]=strtok(linecommand," ");
int i=1;
if(myargv[0]!=NULL&&strcmp(myargv[0],"ls")==0)
{
myargv[i++]=(char*)"--color=auto";
}
while(myargv[i++]=strtok(NULL," "))
if(myargv[0]!=NULL&&strcmp(myargv[0],"cd")==0)
{
if(myargv[1]!=NULL)
chdir(myargv[1]);
continue;
}
if(myargv[0]!=NULL&&myargv[1]!=NULL&&strcmp(myargv[0],"echo")==0)
{
if(strcmp(myargv[1],"$?")==0)
{
printf("%d %d\n",lastcod,lastsig);
}
else
{
printf("%s\n",myargv[1]);
}
continue;
}
#ifdef DEBUG
for(int i=0;myargv[i];i++)
{
printf("myargv[%d]:%s\n",i,myargv[i]);
}
#endif
pid_t id=fork();
assert(id!=-1);
if(id==0)
{
switch(redirnum)
{
case NONE_REDIR:
break;
case INPUT_REDIR:
{
int fd=open(redirname,O_RDONLY);
if(fd<0)
{
perror("open");
exit(errno);
}
dup2(fd,0);
}
break;
case OUTPUT_REDIR:
case APPEND_REDIR:
{
umask(0);
int flags=O_WRONLY|O_CREAT;
if(redirnum==APPEND_REDIR)
flags |=O_APPEND;
int fd=open(redirname,flags,0666);
if(fd<0)
{
perror("open");
exit(errno);
}
dup2(fd,1);
}
break;
default:
printf("bug?\n");
break;
}
execvp(myargv[0],myargv);
exit(-1);
}
int status=0;
waitpid(id,&status,0);
assert(id>0);
(void)id;
lastcod=((status>>8)&0xFF);
lastsig=((status)&0x7F);
}
return 0;
}
命令是要给子进程来执行的,真正重定向的工作也要子进程完成。
如何重定向需要父进程给子进程传递信息。包括redienum,redirname。
重定向由子进程执行,这里的重定向不影响父进程。因为进程的独立性。
子进程是以父进程进行拷贝产生的,所以子进程的文件描述符表也是父进程复制的,它俩不是共用一个文件描述符表。而且如果共用一张表,子进程对文件描述符标的改动,父进程的文件描述符表会进行同样的改动,显然不符合进程的独立性。
但是父子进程文件描述符表存的地址是同一个文件的,父子进程是由进程管理的,文件不受进程的控制,文件不会因为拷贝子进程而进行拷贝。
子进程对文件进行操作,不会影响父进程对文件的操作,因为文件中存在引用计数。
close一个文件其实就是让引用计数-1。
要判断一个对象或结构体属于哪个进程或文件,就把它去掉,看它对那部分影响最大,就是那部分的。
执行程序替换的时候,曾经进程打开的重定向文件不会受到影响。
进程和被打开的文件和PCB等属于内核数据结构,程序替换时,进程的代码和数据与内存进行替换并不影响内核数据结构。