当前位置: 首页 > news >正文

简单shell

目录

预备知识

fork

进程等待

wait

waitpid

环境变量

概念

分类

常见的环境变量及其用途

环境变量的查看与设置

exec系列

 函数解释

命名理解

简单shell


预备知识

fork

fork 是 Linux 和许多其他类 Unix 系统中的一个重要系统调用,它用于创建一个新的进程,这个新进程是当前进程的复制品,称为子进程。子进程会获得父进程当前状态的副本,包括父进程的内存布局、环境变量、打开的文件描述符等。但是,子进程和父进程是两个独立的执行线程,它们可以独立地运行程序的不同部分。

以下是关于 fork 的一些关键点:

  1. 复制:子进程是父进程的一个副本。但是,这种复制是“写时复制”(Copy-On-Write, COW)的。这意味着,如果子进程和父进程都不修改它们共享的内存区域,那么这些区域实际上不会被复制,从而节省内存和 CPU 时间。
  2. 返回值fork 系统调用在父进程中返回新创建的子进程的进程 ID(PID),而在子进程中返回 0。这是区分父进程和子进程的一个常用方法。
  3. 资源分配:子进程会获得其父进程所拥有资源的副本,如文件描述符、环境变量等。但是,这些资源并不一定是物理上独立的;它们通常是通过引用计数或其他机制来共享的。
  4. 并发:由于子进程和父进程是两个独立的执行线程,因此它们可以并发地执行不同的任务。这使得 fork 成为实现多进程并发和并行处理的重要工具。
  5. 进程间通信:由于子进程和父进程是独立的,因此它们之间需要进行通信以共享数据或协调行为。这可以通过各种进程间通信(IPC)机制来实现,如管道、消息队列、信号量、共享内存等。
  6. 终止和等待:当子进程终止时,它会留下一个僵尸进程(Zombie Process),直到其父进程调用 wait 或 waitpid 系统调用来获取其终止状态并释放其占用的资源。

需要注意的是,虽然 fork 是一个强大的工具,但它也有一些限制和开销。例如,由于需要复制父进程的状态,因此 fork 的开销通常比创建线程要大。此外,由于子进程和父进程是独立的,因此它们之间的通信和同步也需要额外的开销和复杂性。因此,在选择使用 fork 还是其他并发机制(如线程或协程)时,需要根据具体的应用场景和需求进行权衡。

进程等待

wait

  • 功能wait函数用于使父进程等待其任意一个子进程结束,并回收该子进程的资源。当子进程结束时,内核会向父进程发送SIGCHLD信号,父进程可以通过wait函数来响应这个信号并获取子进程的状态。
  • 函数原型pid_t wait(int *wstatus);
    • 参数wstatus是一个指向整数的指针,用于存储子进程的退出状态。如果父进程不关心子进程的退出状态,可以将此参数设为NULL。
    • 返回值是已结束子进程的进程ID(PID),如果出现错误则返回-1。
  • 特点wait函数会阻塞父进程的执行,直到有一个子进程结束。如果调用wait时子进程已经结束,则wait会立即返回子进程的结束状态值。

waitpid

  • 函数原型:#include #include pid_t wait(int*status); 返回值: 成功返回被等待进程pid,失败返回-1
  • 返回值:
    • 当正常返回的时候waitpid返回收集到的子进程的进程ID;
    • 如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0;
    • 如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在;
  • 参数:
    • pid:
      • Pid=-1,等待任一个子进程。与wait等效。
      • Pid>0.等待其进程ID与pid相等的子进程。
    • status:
      • WIFEXITED(status): 若为正常终止子进程返回的状态,则为真。(查看进程是否是正常退出)
      • WEXITSTATUS(status): 若WIFEXITED非零,提取子进程退出码。(查看进程的退出码)
    • options:
      • WNOHANG: 若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。若正常结束,则返回该子进 程的ID。

环境变量

概念

  • 定义:环境变量是系统预定义的参数,相当于全局变量,存在于所有的shell中,具有继承性。它们可存储有关shell会话和工作环境的信息。
  • 作用:环境变量允许在内存中存储数据,以便运行在shell上的程序和脚本访问。这些数据可以用来识别用户、系统、Shell的特性以及任何其它需要存储的数据。

分类

  • 按作用范围分
    • 系统环境变量:公共的,对全部的用户都生效。
    • 用户环境变量:用户私有的、自定义的个性化设置,只对该用户生效。
  • 按生存周期分
    • 永久环境变量:在环境变量脚本文件中配置,用户每次登录时会自动执行这些脚本,相当于永久生效。
    • 临时环境变量:使用时在Shell中临时定义,退出Shell后失效。

常见的环境变量及其用途

  • PATH:指定命令的搜索路径,由冒号分隔的目录列表,用于告诉系统在哪里可以找到可执行文件。
  • HOME:指定用户的主工作目录,即用户登录到Linux系统时,默认的目录。
  • SHELL:当前Shell的路径,通常指向用户当前使用的shell解析器,如/bin/bash
  • LD_LIBRARY_PATH:C/C++语言动态链接库文件搜索的目录,对C/C++程序员来说非常重要。
  • CLASSPATH:JAVA语言库文件搜索的目录,对JAVA程序员来说非常重要。

环境变量的查看与设置

  • 查看
    • 使用env命令可以查看当前用户全部的环境变量。
    • 使用echo $环境变量名可以查看特定环境变量的值。
  • 设置
    • 使用export命令可以设置新的环境变量或修改现有环境变量的值。例如,export PATH=$PATH:/new/directory
    • 如果希望环境变量永久生效,需要在登录脚本文件中(如/etc/profile/etc/profile.d/~/.bash_profile~/.bashrc等)进行配置

exec系列

#include <unistd.h>

int execl(const char *path, const char *arg, ...);

int execlp(const char *file, const char *arg, ...);

nt execle(const char *path, const char *arg, ...,char *const envp[]);

int execv(const char *path, char *const argv[]); i

nt execvp(const char *file, char *const argv[]);

int execve(const char *path, char *const argv[], char *const envp[]);

这些函数的主要目的是在当前进程的上下文中执行一个新程序,从而替换当前进程的映像。它们允许程序在运行时加载新的可执行文件,实现动态性和灵活性。

 函数解释

  • 这些函数如果调用成功则加载新的程序从启动代码开始执行,不再返回。
  • 如果调用出错则返回-1
  • 所以exec函数只有出错的返回值而没有成功的返回值

命名理解

  • l(list) : 表示参数采用列表
  •  v(vector) : 参数用数组
  • p(path) : 有p自动搜索环境变量PATH
  • e(env) : 表示自己维护环境变量

简单shell

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<ctype.h>
#include<unistd.h>
#include<fcntl.h>
#include<errno.h>
#include<sys/wait.h>
#define SIZE 512
#define ZERO '\0'
#define SEP " "
#define NUM 32
#define SkipPath(p) do{p+=strlen(p)-1;while(*p!='/')p--;}while(0)
#define SkipSpace(cmd,pos) do{ \while(1){\if(isspace(cmd[pos]))\pos++;\else break;\}}while(0)
#define none_type 0
#define in_type 1
#define out_type 2
#define app_type 3
int redir_type=none_type;
char* filename=NULL;char* gArgv[NUM];
int lastcode=0;
void Die()
{exit(1);
}
const char* GetHostname()
{const char* hostname=getenv("HOSTNAME");if(hostname==NULL)return "/";return hostname;
}
const char* GetUsername()
{const char* user=getenv("USER");if(user==NULL)return "none";return user;
}
const char* Getcwd()
{const char* cwd=getenv("PWD");if(cwd==NULL) return "none";return cwd;
}
void MakeCommmandLine()
{char line[SIZE];const char* hostname=GetHostname();const char* username=GetUsername();const char* cwd=Getcwd();SkipPath(cwd);snprintf(line,sizeof(line),"[%s@%s %s]> ",username,hostname,strlen(cwd)==1?"/":cwd+1);printf("%s",line);fflush(stdout);
}
int GetUserCommad(char commad[],size_t n)
{char* s=fgets(commad,n,stdin);if(s==NULL)return -1;commad[strlen(commad)-1]=ZERO;return strlen(commad);}
void CheckRedir(char cmd[])
{int pos=0;int end=strlen(cmd);while(pos<end){if(cmd[pos]=='>'){if(cmd[pos+1]=='>'){cmd[pos++]=0;pos++;redir_type=app_type;SkipSpace(cmd,pos);filename=cmd+pos;}else {cmd[pos++]=0;redir_type=out_type;SkipSpace(cmd,pos);filename=cmd+pos;  }}else if(cmd[pos]=='<'){cmd[pos++]=0;redir_type=in_type;SkipSpace(cmd,pos);filename=cmd+pos;}else{pos++;}}
}
void SplitCommad(char command[])
{gArgv[0]=strtok(command,SEP);int index=1;while((gArgv[index++]=strtok(NULL,SEP)));
}
void ExecuteCommand()
{pid_t id=fork();if(id<0)Die();else if (id==0){//子进程if(filename!=NULL){if(redir_type==in_type){int fd=open(filename,O_RDONLY);dup2(fd,0);}else if (redir_type==out_type){int fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666);dup2(fd, 1); }else if (redir_type==app_type){int fd = open(filename, O_WRONLY | O_CREAT | O_APPEND, 0666);dup2(fd, 1);}}execvp(gArgv[0],gArgv);exit(errno);}else{//父进程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_type;filename=NULL;//创建一个自己的命令行MakeCommmandLine();//获取用户命令char usercommad[SIZE];int n=GetUserCommad(usercommad,sizeof(usercommad));if(n<=0)return 1;CheckRedir(usercommad);//测试一下//   printf("cmd: %s\n", usercommad);//   printf("redir: %d\n", redir_type);//   printf("filename: %s\n", filename);//分割字符串SplitCommad(usercommad);//执行命令ExecuteCommand();}return 0;
}

http://www.lryc.cn/news/388251.html

相关文章:

  • Spring Boot + FreeMarker 实现动态Word文档导出
  • 3D生物打印的未来:多材料技术的突破
  • 充电宝口碑哪个好?好用充电宝品牌有哪些?好用充电宝推荐
  • Pytorch-----(6)
  • leetcode hot100 第三题:最长连续序列(Java)
  • 利用Jaspar进行转录因子结合位点预测
  • Ubuntu添加系统字体
  • 深度学习相关概念及术语总结2
  • 基于改进滑模、经典滑模、最优滑模控制的永磁同步电机调速系统MATLAB仿真
  • windows环境下创建python虚拟环境
  • Fragment切换没变化?解决办法在这里
  • Linux系统防火墙iptables(下)
  • 你需要精益管理咨询公司的N+1个理由
  • [机器学习]-3 万字话清从传统神经网络到深度学习
  • 网络安全等级保护2.0(等保2.0)全面解析
  • 用Lobe Chat部署本地化, 搭建AI聊天机器人
  • 基于ARM的通用的Qt移植思路
  • IT专业入门,高考假期预习指南
  • 芯片详解——AD7606C
  • IDEA 编译单个Java文件
  • 人工智能业务分析
  • 随机文本生成器
  • java中输入输出流的继承关系
  • c++应用网络编程之一基本介绍
  • Web后端开发概述环境搭建项目创建servlet生命周期
  • Java 位运算详解
  • 智能体实战:开发一个集成国内AI平台的GPTs,自媒体高效智能助手
  • 完美世界|单机版合集(共22个版本)
  • Jenkins的一些记录
  • 讲讲js中的prototype和__proto__