shell编程基础(扩展篇:bash启动命令的原理)

前言

    简单聊聊shell,shell这个名称在不同的上下文环境中,表达的含义是不同的,当我们再聊计算机语言时,此时的shell名称即表示一种语言;当我们聊计算机程序时,shell又表示一个解释器程序,比如bash就属于shell中的一种;当我们再聊操作系统的环境中时,shell这个名称又表示与内核(操作系统)通信的接口。

    bash是shell解释器程序中的一种,它是Linux默认使用的shell程序,多数Linux发行版使用的都是bash程序,而bash有一套属于它的标准!

shell启动程序的原理

    当我们按下回车键后,bash解释器开始解释在换行符前的文本信息

$ ls -l

    1、bash读取命令行后,会将自己看到的第一个单词作为命令(备注:这是规定好的标准,比如ls)

    2、接着判断该命令为内部命令还是外部命令?

    3、如果为外部命令,则寻找【单词】指向的可执行文件(备注:PATH环境变量指向的目录,或者绝对路径或者以当前工作目录中寻找)

    4、第一个单词后面的所有单词,都作为第一个命令的命令行参数传进去(备注:采用空白字符进行单词分隔)

    5、bash接着向内核发起系统调用fork( ),此时会建立一个新的子进程,新的子进程会将命令作为程序而执行   

    6、bash进程向内核请求系统调用wait(),所以bash程序会默认一直等待子进程中的程序执行完成,当子进程完成工作后,会向父进程(shell进程)报告,此时bash进程会醒来

    7、如果执行的是后台命令,命令行中加了nohup与&,此时的bash进程不会等待子进程中的程序执行完成,它会继续向下执行程序,bash程序执行结束后会直接返回

ls -l &

    8、终端窗口则继续展示提示符,用户可以继续输入下一条命令;而子进程仍在运行中,运行结束后的标准输出仍然会显示在终端窗口中
   

bash运行程序流程图(感谢作者,有水印)

è¿éåå¾çæè¿°

    图中的2条执行流:排在第一行的是主执行流(bash进程),第二行是子进程的执行流(子进程程序)

当我们在java或者python中调用外部程序的执行流

    我在Java程序(Java进程)中、或者Python程序(Python解释器进程)调用命令行的过程是这样的吗?(备注:结论是错的,中间的shell进程,可以调用,也可以不调用!),比如我们调用的外部程序是grep

第一种

Java进程->shell进程->命令对应的进程

Python进程->shell进程->命令对应的进程

 第二种

Java进程->命令对应的进程

Python进程->命令对应的进程

试验
 

    首先启动两个bash窗口A和B(Mac上,有两个bash进程,它们的pid是83129和83149),此时的两个bash进程可以在ps命令中看到

    接着预先准备好一个python脚本文件

import os
os.system("adb logcat")

    以下的描述如果是真的,那么当启动python进程后,至少会存在4个进程,

第一个:bash进程A,是我用来要启动Python进程

第二个:python解释器进程

第三个:shell进程

第四个:外部命令对应的进程

bash进程->Python进程->shell进程->命令对应的进程

结果会是这样吗?

    在命令行中执行python3 fkme.py

    接着去另外一个bash进程B的窗口中输入ps命令

     发现Python脚本文件中的os.system("adb logcat")的执行,没有去创建一个新的bash进程,而是直接启动一个子进程,我的猜测是错误的,正确的情况是一共只创建了3个进程

第一个:bashA进程是Python解释器进程的父进程

第二个:Python解释器进程是adb logcat的父进程

第三个:adb命令的进程

    再执行一个contro+c,干掉正在执行的python解释器进程

再试验:换一种方案

import os
os.popen("adb logcat")

    使用了python中os模块下的popen()函数,这会在新的子进程执行,特点是python解释器进程不会等待子进程的执行

    让我执行python3 fkme.py ,然后输入ps命令

    此时看到Python解释器进程已经不见了,而adb logcat 进程还在,这就是os.popen()的特点,它不会要求父进程等待它执行完毕

新发现

    再输入ps - l命令,因为python解释器进程已经结束了,因为python解释器进程是它的父进程,这可怎么办?内核不会让进程孤立无关的运行,内核会怎么做呢?

    发现adb logcat的父进程成为1,那么pid为1的进程在mac系统下是谁呢?

    原来mac系统下pid为1的进程是/sbin/launchd进程,卧槽!(Linux下是init进程)

延伸:那么C语言标准库中的system()函数又做了什么?

    在Windows系统上,返回值是系统shell在运行命令后返回的值,shell由Windows环境变量COMSPEC给出:它通常是cmd.exe。返回命令运行的退出状态码,对于使用非本机shell的系统,请参阅您的shell文档

    在Linux/Unix系统中,system()函数会调用fork()函数产生子进程,由子进程执行command,命令执行后即返回原调用的进程,所以adb logcat命令会在Python解释器fork()出来的子进程中执行!

    c标准库中的system()函数

#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
#include <unistd.h>
 
int system(const char * cmdstring)
{
    pid_t pid;
    int status;
    if(cmdstring == NULL){     
         return (1);
    }
    if((pid = fork())<0){
            status = -1;
    }
    else if(pid = 0){
        execl("/bin/sh", "sh", "-c", cmdstring, (char *)0);
        -exit(127); //子进程正常执行则不会执行此语句
        }
    else{
           while(waitpid(pid, &status, 0) < 0){
                if(errno != EINTER){
                    status = -1;
                    break;
                }
            }
        }
        return status;
}

主进程等待子进程执行结束后,才能继续执行的罪魁祸首找到了(这是进程间同步的知识)

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值