0
点赞
收藏
分享

微信扫一扫

Spring和SpringMVC,SpringBoot区别的文章

扒皮狼 2023-10-25 阅读 40

文章目录


一、环境变量

1.什么是环境变量

首先,在百度百科中,环境变量的解释是这样的:

指令本质上就是编译好的程序和脚本,被存储在特定的路径下(默认/user/bin/)。

比如我们执行 ls 指令,实际上就是执行这个程序或者脚本,而我们要执行一个程序就必须找到该程序,我们通常要运行一个可执行程序,是用 ./a.out 来执行的,./表示在当前目录,a.out表示一个可执行程序。所以我们要执行 ls 指令,就要找到 ls 所在的路径,而环境变量的作用就是让系统从指定的路径去找,而在PATH 中有/uer/bin 路径,所以我们就能执行所有的指令了。

在这里插入图片描述

常用的10个环境变量如下:

环境变量名称作用
HOME用户的主目录(也称家目录)
SHELL用户使用的 Shell 解释器名称
PATH定义命令行解释器搜索用户执行命令的路径
EDITOR用户默认的文本解释器
RANDOM生成一个随机数字
LANG系统语言、语系名称
HISTSIZE输出的历史命令记录条数
HISTFILESIZE保存的历史命令记录条数
PS1Bash解释器的提示符
MAIL邮件保存路径

Linux 作为一个多用户多任务的操作系统,能够为每个用户提供独立的、合适的工作运行环境,因此,一个相同的环境变量会因为用户身份的不同而具有不同的值。

2.环境变量的分类

按照变量的生存周期划分,Linux 变量可分为两类:

按作用的范围分,在 Linux 中的变量,可以分为环境变量和本地变量:

3.查看环境变量

使用 echo 命令查看单个环境变量,例如:echo $PATH;使用 env 查看当前系统定义的所有环境变量;使用 set 查看所有本地定义的环境变量。查看 PATH 环境的实例如下:

在这里插入图片描述

常用的命令如下:

例如我们要设置一个新的环境变量,然后再清除:

在这里插入图片描述

`

4.设置环境变量

在 Linux 中设置环境变量有三种方法:

1.所有用户永久添加环境变量: vi /etc/profile,在 /etc/profile 文件中添加变量。

2.当前用户永久添加环境变量: vi ~/.bashrc,在用户目录下的 ~/.bashrc 文件中添加变量。

3.临时添加环境变量 : 可通过 export 命令,如运行命令export HELLO=100

5.获取环境变量

  • 命令行第三个参数
#include <stdio.h>
int main(int argc, char *argv[], char *env[])
{
	 int i = 0;
	 for(; env[i]; i++){
	 printf("%s\n", env[i]);
	 }
	 return 0;
}
  • 通过第三方变量environ 获取
#include <stdio.h>
int main(int argc, char *argv[])
{
	 extern char **environ;
	 int i = 0;
	 for(; environ[i]; i++){
	 printf("%s\n", environ[i]);
	 }
	 return 0;
}

lib.c中定义的全局变量environ指向环境变量表,environ没有包含在任何头文件中,所以在使用时要用extern声明。

  • 通过系统调用获取
#include <stdio.h>
#include <stdlib.h>
int main()
{
 	printf("%s\n", getenv("PATH"));
 	return 0;
}

二、进程控制

1.进程终止

正常退出:

异常退出:

_exit():

 #include <unistd.h>
 void _exit(int status);

DESCRIPTION:

_eixt()函数立即终止进程,关闭所有属于该进程的文件描述符,其子进程被1号init 进程领养,然后向父进程发送SIGCHLD 信号。

值状态作为进程的退出状态返回到父进程,并且可以使用 wait(2) 系列调用之一来收集。

exit():

 #include <unistd.h>
 void exit(int status);

return是一种更常见的退出进程方法。执行return n等同于执行exit(n),因为调用main的运行时函数会将main的返回值当做 exit的参数。

2.进程等待

进程等待的必要性:

wait():用来等待任何一个子进程退出,由父进程调用。

#include<sys/types.h>
#include<sys/wait.h>
pid_t wait(int*status);

返回值:
 成功返回被等待进程pid,失败返回-1。
参数:
 输出型参数,获取子进程退出状态,不关心则可以设置成为NULL
wait方式:
 阻塞式等待,等待的子进程不退出时,父进程一直不退出;

waitpid():

pid_ t waitpid(pid_t pid, int *status, int options);

参数:
pid:
 Pid=-1,等待任一个子进程。与wait等效。
 Pid>0.等待其进程ID与pid相等的子进程。
 
status:
 WIFEXITED(status): 若为正常终止子进程返回的状态,则为真。(查看进程是否是正常退出)
 WEXITSTATUS(status): 若WIFEXITED非零,提取子进程退出码。(查看进程的退出码)
 
options:
	当options参数为0时,与wait功能相同,仍是阻塞式等待,不提供额外功能
	如果为下列常量按位或则提供更多功能:
 WCONTINUED:若实现支持作业控制,那么由pid指定的任一子进程在暂停后已经继续,但状态尚未报告,则返回状态
 WNOHANG:若由pid指定的子进程并不是立即结束,则waitpid不阻塞,即此时以非阻塞方式(轮询式访问的必要条件)等待子进程,并且返回0。若正常结束,则返回该⼦进程的ID。
 WUNTRACED:若实现支持作业控制,而pid指定的任一子进程已经暂停,且其状态尚未报告,则返回其状态。

返回值:
当正常返回的时候,waitpid返回收集到的子进程的进程ID;
如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0;
如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在;
当pid所指示的子进程不存在,或此进程存在,但不是调用进程的子进程,waitpid就会出错返回,这时errno被设置为ECHILD;

获取子进程status:

我们可以通过status & 0x7f来判断异常信号是否为0;若为0,则正常退出,然后可以通过(status >> 8) & 0xff来获取子进程返回值。sys/wait.h中提供了一些宏来简化这些操作:

if (WIFEXITED(status)) {
    // 正常退出:((status) & 0x7f) == 0
    // 打印退出码:(status >> 8) & 0xff
    printf("child return: %d\n", WEXITSTATUS(status));
} else if (WIFSIGNALED(status)) {
    // 异常退出:((signed char) (((status) & 0x7f) + 1) >> 1) > 0
    // 打印异常信号值:(status) & 0x7f
    printf("child signal: %d\n", WTERMSIG(status));
}

我们用这段代码来读取子进程的status,以获取它的退出码和异常信号值,以及父进程收到的SIGCHLD信号:

#include<stdio.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<unistd.h>
#include<stdlib.h>


void catchsig(int sig)
{
    printf("catch a sig : %d , pid->%d\n",sig,getpid());
}

int main()
{
    signal(SIGCHLD,catchsig);
    pid_t id = fork();

    if(id<0)
    {
        printf("fork error!\n");
        exit(-1);
    }
    else if(id==0)
    {
        int cnt = 3;
        while(cnt)
        {
            printf("this is a child process! id-->%d   pid-->%d  ppid--> %d\n",cnt--,getpid(),getppid());
        }
        //exit(-1);
       int i =  10/0;
    }

    sleep(2);
    int status = 0;
    pid_t ret = waitpid(id,&status,0);
    if(id>0)
        //0-7:终止信号 15-8:退出状态
        printf("wait success:%d , sign number:%d , child exit code:%d\n",ret,(status & 0x7F),(status>>8 & 0xFF));

    return 0;
}

运行结果如下:

在这里插入图片描述
 其实,⼦进程在终⽌时会给⽗进程发SIGCHLD信号,该信号的默认处理动作是忽略,⽗进程可以⾃定义SIGCHLD信号的处理函数,这样⽗进程只需专⼼处理⾃⼰的⼯作,不必关⼼⼦进程了,⼦进程终⽌时会通知⽗进程,⽗进程在信号处理函数中调⽤wait清理⼦进程即可。一般情况下父进程收到这个信号的默认处理是忽略这个信号,即就是不做任何处理。

3.进程替换

替换原理:

替换函数:

#include <unistd.h>

       extern char **environ;

       int execl(const char *path, const char *arg, ...);
       int execlp(const char *file, const char *arg, ...);
       int execle(const char *path, const char *arg, ... , char * const envp[]);
       int execv(const char *path, char *const argv[]);
       int execvp(const char *file, char *const argv[]);
       int execvpe(const char *file, char *const argv[], *const envp[]);
       
	这些函数如果调用成功则加载新的程序从启动代码开始执行,不再返回。
	如果调用出错则返回-1,所以exec函数只有出错的返回值而没有成功的返回值。

命名理解:

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

其中*path表示的是路径,arg表示的是要执行的程序,“…”表示的就是可变参数列表,即命令行上怎么执行这里就写入什么参数。必须以NULL作为参数列表的结束。

#include<unistd.h>    
#include<stdio.h>    
#include<sys/wait.h>    
#include<stdlib.h>    
int main()    
{    
  if(fork()==0)    
  {    
    printf("command begin\n");    
    execl("/usr/bin/ls","ls","-a","-l",NULL);                                                                                                            
    printf("command fail\n");    
    exit(1);    
  }    
  waitpid(-1,NULL,0);    
  printf("wait child success\n");    
  return 0;    
}    

当子进程执行完打印command begin的语句的时候,进行进程的替换。其中替换的是/usr/bin/ls,在命令行要输入的是ls -a -l,将程序运行起来:

在这里插入图片描述

下面是这些函数的用法:

#include <unistd.h>
int main()
{
 	char *const argv[] = {"ps", "-ef", NULL};
 	char *const envp[] = {"PATH=/bin:/usr/bin", "TERM=console", NULL};
 	
 	execl("/bin/ps", "ps", "-ef", NULL);
 	
 // 带p的,可以使用环境变量PATH,无需写全路径
 	execlp("ps", "ps", "-ef", NULL);
 	
 // 带e的,需要自己组装环境变量
 	execle("ps", "ps", "-ef", NULL, envp);
 	
 	execv("/bin/ps", argv);
 
 // 带p的,可以使用环境变量PATH,无需写全路径
 	execvp("ps", argv);
 	
 // 带e的,需要自己组装环境变量
 	execve("/bin/ps", argv, envp);
 	
 	exit(0);
}

操作系统实际上只提供了一个接口那就是:execve,其他的函数都是对该接口封装而成的库函数。它们的底层都是使用execve来进行实现的。

三、实现一个简单的shell

以下代码利用程序替换实现了shell 的基本功能,包括重定向,退出码等功能,后续还可以再补充。

#include<stdio.h>
#include<ctype.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<assert.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<string.h>
#define NUM 1024//缓冲区大小
#define COM_NUM 64//存放指令字符串的最大值
#define NONE_REDIR 0
#define INPUT_REDIR 1
#define OUPUT_REDIR 2
#define APPEND_REDIR  4
char lineCommand[NUM];//输入缓冲区
char *myargv[COM_NUM];//存放一个个的指令字符串
int exit_code=0;//退出码
int exit_sign=0;//退出信号值
int redirType = NONE_REDIR;//读方式
char* myFile = NULL;

//跳过空格
#define trimSpace(start) do{while(isspace(*start)) ++start;}while(0)


void commandCheck(char* commands)
{
    assert(commands);
    char *start = commands;
    char *end = commands+strlen(commands);

    while(start<end)
    {
        if(*start == '>')
        {
            *start = '\0';
            start++;
            if(*start == '>') // >> 表示追加重定向
            {
                start++;
                redirType = APPEND_REDIR;
            }
            else // > 表示输出重定向 
            {
                
                redirType = OUPUT_REDIR;
            }

            trimSpace(start);
            myFile = start;
            break;
        }
        else if(*start == '<') // < 表示输入重定向
        {
            *start = '\0';
            start++;
            redirType = INPUT_REDIR;
            trimSpace(start);
            myFile = start;
            break;
        }
        else 
            start++;
    }
}

//最新添加重定向功能!!!!!
int main()
{
    while(1)
    {
        redirType = NONE_REDIR;
        myFile = NULL;

        printf("[wml @ my_bash path#]");
        fflush(stdout);
        
        //获取输入行内容
        char *s = fgets(lineCommand,sizeof(lineCommand)-1,stdin);
        assert(s != NULL);
        (void)s;

        lineCommand[strlen(lineCommand)-1] = 0;

        //重定向:
        //"ls -a -l > "test.txt""  ---->  "ls -a -l"  >  "test.txt"
        //"ls -a -l >> "test.txt""  ---->  "ls -a -l"  >>  "test.txt"
        //"cat < "test.txt""  ---->  "cat"  <  "test.txt"
    
        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("exit_code-->%d | exit_sign-->%d\n",exit_code,exit_sign);
            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(redirType)
            {
                case NONE_REDIR:
                    break;
                case INPUT_REDIR:
                {
                    int fd  = open(myFile,O_RDONLY);
                    if(fd<0) {perror("open");return 1;}
                    dup2(fd,0);
                }
                     break;
                case OUPUT_REDIR:
                case APPEND_REDIR:
                {
                    umask(0);
                    int flag = O_WRONLY | O_CREAT;
                    if(redirType == APPEND_REDIR) flag |= O_APPEND;
                    else flag |= O_TRUNC;

                    int fd  = open(myFile,flag,0666);
                    if(fd<0){perror("open");return 2;};
                    dup2(fd,1);
                }
                    break;
                default:
                    printf("bug!\n");
                    break;

            }
            execvp(myargv[0],myargv);
           _exit(1);
        }

        int status=0;
        pid_t ret = waitpid(id,&status,0);
        assert(ret!=-1);
        (void)ret;

        exit_sign = (status & 0x7f);
        exit_code = (status>>8) & 0xff;


    }
    return 0;
}

举报

相关推荐

0 条评论