学习目标:
进程替换
学习内容:
所谓的进程替换,不会创建新的进程,只是将进程执行的指令和数据替换成新的程序。
内容:fork和vfork,死锁
int exec(const char *path,char *argv0,char *argv1, ... ,char *argvn,(cahr*)0 );//l--list ,从argv0到argvn对应的就是新程序main方法的参数
//本身main的argv[0]是执行的命令,是要传的;但在这里,可以不传argv[0]
int execv(const char *path,char *argv[]);//v--向量
int execle(const char *path,char *argv0,char *argv1, ... ,char *argvn,(cahr*)0 ,char *envp[]);//环境变量
int execve(const char *path,char *argv[],char *envp[]);//是唯一的系统调用
int execlp(const cahr* file,char *argv0,char *argv1, ... ,char *argvn,(cahr*)0);//file如果指定路径,有就去找,如果没有,就通过环境变量PATH查找
int execvp(const char *file,char *argv[]);
strace ./ppp
如果exec这些方法调用成功,则会用新程序替换当前进程的程序,在exec调用之后的所有指令都不会被调用,无法接受返回值。如果exec调用失败,替换不成功,exec之后的指令才会执行。
程序mian.c + exec.c
仿写bash:命令解析器
功能:获取用户输入的命令,根据命令执行相应的操作
大部分的命令都不是集成在bash中,而是单独实行的 e,g ls ps pwd su touch等
代码:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<assert.h>
#include<fcntl.h>
#include<sys/wait.h>
#include<sys/types.h>
#include<signal.h>
#include<sys/utsname.h>
#include<pwd.h>
#define STRLEN 128
void Zombie(int sign)
{
wait(NULL);
}
void ShowTagInfo()
{
signal(SIGCHLD,Zombie);
//用户名@主机名 路径 $/#
struct utsname host_name;
uname(&host_name);
struct passwd *p=getpwuid(getuid());
assert(p!=NULL);
char now_pwd[STRLEN];
getcwd(now_pwd,STRLEN-1);
char ch[STRLEN]={0};
if(strncmp(now_pwd,p->pw_dir,strlen(p->pw_dir))==0)
{
strcat(ch,"~");
strcat(ch,now_pwd+strlen(p->pw_dir));
}
else
{
strcat(ch,now_pwd);
}
char tag='$';
if(getuid()==0)
tag='#';
//printf("%s@%s:%s%c ",getlogin(),host_name.nodename,now_pwd,tag);
printf("\033[1;36m""%s@%s:""\033[0m",getlogin(),host_name.nodename);
printf("\033[1;34m""%s""\033[0m",ch);
printf("%c ",tag);
}
void CutString(char *cmd,char *arr[])
{
char *p;
p=strtok(cmd," ");
int index=0;
while(p)
{
arr[index++]=p;
p=strtok(NULL," ");
}
}
void MyCdCommand(char *path)
{
static char old_pwd[STRLEN]={0};//保存上次所在位置
printf("输入的:%s\n",path);
char dest_path[STRLEN]={0};
if(path==NULL || strncmp(path,"~",1)==0 )//切换到家目录
{
struct passwd *pw=getpwuid(getuid());
assert(pw!=NULL);
strcpy(dest_path,pw->pw_dir);
if(strlen(path) > 1) // ~/xxx/xx
{
strcat(dest_path,path+1);
}
}
else if( strncmp(path,"-",1)==0)//切换到上一次的位置
{
if(strlen(old_pwd)==0)
{
printf("cd is error\n");
return;
}
strcpy(dest_path,old_pwd);
}
else
{
strcpy(dest_path,path);
}
char now_pwd[STRLEN]={0};
getcwd(now_pwd,STRLEN-1);
printf("It's now_pwd:%s\n",now_pwd);
if(chdir(dest_path)==-1)
{
perror("cd is error");
return;
}
memset(old_pwd,0,STRLEN);//清空一下,再拷贝
strcpy(old_pwd,now_pwd);
}
void DealExec(char *arr[],int is_back)
{
pid_t pid=vfork();
assert(pid!=-1);
if(pid==0)//前台处理
{
char file[STRLEN]="/home/zff/Desktop/系统编程/0417/shell/mybin/";
if(strstr(arr[0],"/")!=NULL)
{
strcpy(file,arr[0]);
}
else
{
strcat(file,arr[0]);
}
execv(file,arr);//如果命令不在,exec就会失败
//fflush(shutout);
perror("execv");
exit(0);
}
else//后台处理
{
if(!is_back) wait(NULL);
}
}
void AnalyzeCommand(char *arr[],int is_back)
{
//内置命令
if(strncmp(arr[0],"cd",2)==0)
{
MyCdCommand(arr[1]);
return;
}
else if(strncmp(arr[0],"exit",4)==0) exit(0);
else {
DealExec(arr,is_back);
}
//其他命令
}
int main()
{
signal(SIGCHLD,Zombie);
while(1)
{
ShowTagInfo();
char cmd[STRLEN]={0};
fgets(cmd,STRLEN-1,stdin);
cmd[strlen(cmd)-1]=0;
if(strlen(cmd)==0) continue;
int is_back=0;
for(int i=0;i<strlen(cmd);i++)
{
if(cmd[i]=='&')
{
is_back=1;
cmd[i]=' ';
break;
}
}
char *arr[STRLEN]={0};
CutString(cmd,arr);
AnalyzeCommand(arr,is_back);
}
}
完成shell:
ls.c中:
扫描目录:即让shell做一次表达式的通配符扩展,与目录操作有关的函数在dirent.h头文件中。它使用一个名为dir的结构作为目录操作的基础,DIR* 就是一个指向这个结构的目录流指针,就和文件操作那的FILE*类似。
DIR* opendir(const char *path);//返回一个目录流
struct dirent * readdir(DIR *dirp);//返回目录流dirp中下一个目录项的数据,读完了就返回null
int close(DIR *dirp);//关闭掉
su.c实现:
流程:
有待完成:shell几个.文件…