当前路径即为当前进程的工作路径,故其工作路径可以被修改
文件描述符的分配规则:
在当前进程中,从files_struct数组当中,找到当前没有被使用的 最小的一个下标,作为新的文件描述符。
程序替换时不影响pcb及其内部的pid细节。
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<assert.h>
#include<ctype.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<errno.h>
#define NUM 1024
#define NONE_REDIR 0
#define INPUT_REDIR 1
#define OUTPUT_ENDIR 2
#define APPEND_ENDIR 3
#define trimSpace(start) do{\
while(isspace(*start)) ++start;\
}while(0)
#define OPT_NUM 64
char lineCommand[NUM];
char* myargv[OPT_NUM];
int lastCode=0;
int lastSig=0;
int redirType=NONE_REDIR;
char* redirFile=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 == '>')
{
//"ls -a >> file.log"
redirType=APPEND_ENDIR;
start++;
}
else
{
redirType=OUTPUT_ENDIR;
}
trimSpace(start);
redirFile=start;
break;
}
else if(*start=='<')
{
*start='\0';
start++;
trimSpace(start);
redirType=INPUT_REDIR;
redirFile=start;
break;
}
else
{
start++;
}
}
}
int main()
{
while(1)
{
redirType=NONE_REDIR;
redirFile=NULL;
errno=0;
printf("用户名@主机名 当前路径#");
fflush(stdout);
char* s=fgets(lineCommand,sizeof(lineCommand)-1,stdin);
assert(s!=NULL);
(void)s;
lineCommand[strlen(lineCommand)-1]=0;
//printf("test:%s\n",lineCommand);
commandCheck(lineCommand);
//字符串切割
myargv[0]=strtok(lineCommand," ");
int i=1;
if(myargv[0] != NULL && strcmp(myargv[0],"ls")==0)
{
myargv[i++]=(char*)"--color=auto";
}
if(myargv[0]!=NULL&&myargv[1]!=NULL&&strcmp(myargv[0],"echo")==0)
{
if(strcmp(myargv[1],"$?")==0)
{
printf("%d,%d\n",lastCode,lastSig);
}
else
{
printf("%s\n",myargv[1]);
}
continue;
}
while(myargv[i++]=strtok(NULL," "));
//若是cd命令则不需要创建子进程,让shell执行对应的命令,本质是执行系统接口。
//向cd这种不需要子进程执行而是shell执行的命令---内建/内置命令
if(myargv[0]!=NULL&&strcmp(myargv[0],"cd")==0)
{
if(myargv[1]!=NULL) chdir(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(redirType)
{
case NONE_REDIR:
break;
case INPUT_REDIR:
{
int fd=open(redirFile,O_RDONLY);
if(fd<0)
{
perror("open");
exit(errno);
}
dup2(fd,0);
}
break;
case OUTPUT_ENDIR:
case APPEND_ENDIR:
{
umask(0);
int flags=O_WRONLY | O_CREAT;
if(redirType==APPEND_ENDIR) flags |= O_APPEND;
else flags|=O_TRUNC;
int fd=open(redirFile,flags,0666);
if(fd<0)
{
perror("open");
exit(errno);
}
dup2(fd,1);
}
break;
default:
printf("bug?\n");
}
execvp(myargv[0],myargv);
exit(1);
}
//if(id==0)
//{
// execvp(myargv[0],myargv);
// exit(1);
//}
int status=0;
pid_t ret=waitpid(id,&status,0);
assert(ret>0);
(void)ret;
lastCode=((status>>8)&0xff);
lastSig=(status&0x7f);
}
return 0;
}
一个简单的shell脚本,命令行解释器。
缓冲区的本质就是一段内存。
FILE的本质是结构体,结构体中有一段为缓冲区。
#include<stdio.h>
#include<string.h>
#include<unistd.h>
int main()
{
//C接口
printf("hello printf\n");
fprintf(stdout,"hello fprintf\n");
const char* fputsString="hello fputs\n";
fputs(fputsString,stdout);
//系统接口
const char* wstring ="hello write\n";
write(1,wstring,strlen(wstring));
return 0;
}
结果:
hello printf
hello fprintf
hello fputs
hello write
cat myfile.c
#include<stdio.h>
#include<string.h>
#include<unistd.h>
int main()
{
//C接口
printf("hello printf\n");
fprintf(stdout,"hello fprintf\n");
const char* fputsString="hello fputs\n";
fputs(fputsString,stdout);
//系统接口
const char* wstring ="hello write\n";
write(1,wstring,strlen(wstring));
fork();
return 0;
}
结果:
[lzy@centos myfile]$ ./myfile > log.txt
[lzy@centos myfile]$ cat log.txt
hello write
hello printf
hello fprintf
hello fputs
hello printf
hello fprintf
hello fputs
缓存区刷新策略问题
共识:如果有一块数据,一次性写入到外设效率最高。
缓冲区会结合具体的设备,定制自己的刷新策略。
a.立即刷新-----无缓冲
b.行刷新(\n)------行缓存---显示器
c.缓冲区满-----全缓冲---磁盘文件
1.用户强制刷新
2.进程退出---一般要进行缓冲区刷新
//代码结束之前,进行子进程创建
//1.没有> ,可以得到4条输出结果
//stdout默认使用行刷新,在fork之前,三条c函数已经将数据打印到了显示器上,FILE,及进程内部不在存在对应的数据了
//2.如果进行 > , 写入的目标不再是显示器(stdout) 而是普通文件,采用的刷新策略是全缓存,之前的3条c函数,虽然带了\n,但是不足以将stdout缓冲区写满!数据并没有刷新
//执行fork()函数时,stdout属于父进程,创建子进程,后进程退出!!!先退出的要进行缓冲区刷新(修改)
//写时拷贝,!!! 数据会显示两次
//3.write没有FILE,而是fd,就没有c提供的缓冲区。