1.文件描述符的分配规则
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
int fd = open("myfile", O_RDONLY);
if(fd < 0){
perror("open");
return 1;
}
printf("fd: %d\n", fd);
close(fd);
return 0;
}
当我们打开一个文件,他的fd是3,因为0 1 2 已经被占用了,当我们关闭0 和 1 ,再看这段代码
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
close(0);
//close(2);
int fd = open("myfile", O_RDONLY);
if(fd < 0){
perror("open");
return 1;
}
printf("fd: %d\n", fd);
close(fd);
return 0;
}
会发现fd是0或者2 ,可见文件描述符的分配规则是找到当前没有被使用的最小的一个下标,作为新的文件描述符,
2.重定向的本质
如果我们关闭文件描述符 1 ,看代码
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
int main()
{
close(1);
int fd = open("myfile", O_WRONLY|O_CREAT, 00644);
if(fd < 0){
perror("open");
return 1;
}
printf("fd: %d\n", fd);
fflush(stdout);
close(fd);
exit(0);
}
会发现,本来应该打印到显示器上的内容,打印到文件"myfile"里面了,这是因为我们关闭了文件描述符1,然后打开一个文件,myfile的文件描述符就是1,所以,printf默认是向stdout输出的,但是stdout指向的文件描述符是1,所以就打印到myfile文件中了,这种现象叫做重定向输出
重定向的本质是什么呢?
3.dup2系统调用
#include <unistd.h>
int dup2(int oldfd, int newfd);
dup2是一个用于复制文件描述符的系统调用函数,,可以将一个文件描述符引用复制到另一个文件描述符中,这通常用于改变标准输入输出流的指向
示例代码
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
int main() {
int fd = open("myfile", O_CREAT | O_RDWR);
if (fd < 0) {
perror("open");
return 1;
}
close(1);
dup2(fd, 1);
for (;;) {
char buf[1024] = {0};
ssize_t read_size = read(0, buf, sizeof(buf) - 1);
if (read_size < 0) {
perror("read");
break;
}
printf("%s", buf);
fflush(stdout);
}
return 0;
}
ssize_t read_size = read(0, buf, sizeof(buf) - 1);
这段代码就是从标准输入读取数据,然后printf输出到文件标识符为1的文件中,也就是myfile中,
4.缓冲区的理解
缓冲区就是一段内存空间,为了给上层提供高效的IO体验,间接提高整体的效率,缓冲区有用户级缓冲区和内核级缓冲区,
缓冲区的刷新策略
1.立即刷新,fflush(stdout)
2.行刷新,显示器
3.全刷新,缓冲区写满才刷新,普通文件
4.特殊情况,进程退出,系统会自动刷新
5.在shell中加入重定向
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<errno.h>
#include<ctype.h>
#include<sys/stat.h>
#include<fcntl.h>
#define SIZE 512
#define ZERO '\0'
#define SEP " "
#define NUM 32
//宏函数
#define SkipPath(cwd) do{ cwd+=(strlen(cwd)-1);while(*cwd!='/')cwd-- ;}while(0)
#define SkipSpace(cmd,pos) do{while(1){if(isspace(cmd[pos]))pos++;else break;}}while(0)
//重定向定义
#define None_Redir 0
#define In_Redir 1
#define Out_Redir 2
#define App_Redir 3
int redir_type=None_Redir;
char*filename=NULL;
char cwd[SIZE*2];
char* gArgv[NUM];
int lastcode=0;
void Die()
{
exit(1);
}
const char* GetHome()
{
const char*home=getenv("HOME");
if(home==NULL)return "/";
return home;
}
const char* GetUserName()
{
const char* name=getenv("USER");//从环境变量中获取USER路径
if(name==NULL)return "None";
return name;
}
const char* GetHostName()
{
const char* name=getenv("HOSTNAME");//从环境变量中获取HOSTNAME路径
if(name==NULL)return "None";
return name;
}
const char* GetCwd()
{
const char* cwd=getenv("PWD");//从环境变量中获取PWD路径
if(cwd==NULL)return "None";
return cwd;
}
void MakeCommandLineAndPrint()
{
char line[SIZE];
const char* username=GetUserName();
const char* hostname=GetHostName();
const char* cwd=GetCwd();
SkipPath(cwd);//分割路径,只需要最后面的一个路径
//将用户名,主机名,路径都写入line中,cwd+1的原因是
snprintf(line,sizeof(line),"[%s@%s %s]> ",username,hostname,strlen(cwd)==1?"/":cwd+1);
printf("%s",line);
fflush(stdout);
}
int GetUserCommand(char command[],size_t n)
{
char* s=fgets(command,n,stdin);
if(s==NULL)return -1;
command[strlen(command)-1]=ZERO;
return strlen(command);
}
void CheckRedir(char cmd[])
{
// > >> <
// "ls -a -l -n > myfile.txt"
int pos=0;
int end=strlen(cmd);
while(pos<end)
{
if(cmd[pos]=='>')
{
if(cmd[pos+1]=='>')
{
cmd[pos++]=0;
pos++;
redir_type=App_Redir;
SkipSpace(cmd,pos);
filename=cmd+pos;
}
else
{
cmd[pos++]=0;
redir_type=Out_Redir;
SkipSpace(cmd,pos);
filename=cmd+pos;
}
}
else if(cmd[pos]=='<')
{
cmd[pos++]=0;
redir_type=In_Redir;
SkipSpace(cmd,pos);
filename=cmd+pos;
}
else
{
pos++;
}
}
}
void SplitCommand(char command[],size_t n)
{
(void)n;
//"ls -a -l -n"-> "ls" "-a" "-l" "-n"
gArgv[0]=strtok(command,SEP);
int index=1;
while((gArgv[index++]=strtok(NULL,SEP)));
}
void Cd()
{
const char*path=gArgv[1];
if(path==NULL)path=GetHome();
chdir(path); //改变当前工作路径
char temp[SIZE*2];
getcwd(temp,sizeof(temp));//获取当前工作路径
snprintf(cwd,sizeof(cwd),"PWD=%s",temp);
putenv(cwd);
}
int CheckBuildin()
{
int yes=0;
const char* enter_cmd=gArgv[0];
if(strcmp(enter_cmd,"cd")==0)
{
yes=1;
Cd();
}
else if(strcmp(enter_cmd,"echo")==0&&strcmp(gArgv[1],"$?")==0)
{
yes=1;
printf("%d\n",lastcode);
lastcode=0;
}
return yes;
}
void ExecuteCommand()
{
pid_t id=fork();
if(id<0)Die();
else if(id==0)
{
//重定向设置
if(filename!=NULL)
{
if(redir_type==In_Redir)
{
int fd=open(filename,O_RDONLY);
dup2(fd,0);
}
else if(redir_type==Out_Redir)
{
int fd=open(filename,O_WRONLY|O_CREAT|O_TRUNC,0666);
dup2(fd,1);
}
else if(redir_type==App_Redir)
{
int fd=open(filename,O_WRONLY|O_CREAT|O_APPEND,0666);
dup2(fd,1);
}
else
{}
}
//child
execvp(gArgv[0],gArgv);
exit(errno);
}
else
{
//father
int status=0;
pid_t rid=waitpid(id,&status,0);
if(rid>0)
{
lastcode=WEXITSTATUS(status);
if(lastcode!=0)
printf("%s:%s:%d\n",gArgv[0],strerror(lastcode),lastcode);
}
}
}
int main()
{
int quit=0;
while(!quit)
{
redir_type=None_Redir;
//1.输出命令行
MakeCommandLineAndPrint();
//2.获取用户命令字符串
char usercommand[SIZE];
int n=GetUserCommand(usercommand,sizeof(usercommand));
if(n<=0)return 1;
//2.1 checkredir
CheckRedir(usercommand);
//3.命令行字符串分割
SplitCommand(usercommand,sizeof(usercommand));
//4.检测命令是否是内建命令
n=CheckBuildin();
if(n) continue;
//5.执行命令
ExecuteCommand();
}
return 0;
}