代码改变世界

终端里面的程序是怎样跑起来的

2016-12-21 23:38  summery91  阅读(616)  评论(0编辑  收藏  举报

  比如mac环境下在某个路径下面跑celery的任务,celery -A msg_task worker 对于这一段语句的理解其实就是用后面的这些参数跑起来celery的可执行文件;那具体是怎样跑起来的呢,分为如下的四个步骤。

一:mac的环境变量
  mac 一般使用bash作为默认shell,Mac系统的环境变量,加载顺序为:
  Ⅰ.(系统级别,系统启动就会加载)
    /etc/profile /etc/paths
  Ⅱ.(当前用户级的环境变量,因为~文件夹本身就是登录人的home目录,根据shell的不同,加载的配置文件可能不一样,比如如果用的是zsh的话,那这边的第一个文件就会是~/.zshrc;然后如果其中的~/.bash_profile文件存在,后面的文件就会忽略不读,如果不存在,才会读后面的文件)
    ~/.bash_profile ~/.bash_login ~/.profile
  Ⅲ.(bash shell打开的时候载入的)
    ~/.bashrc
  比如我的电脑上面看见/etc/path文件里面的内容是
    /usr/local/bin
    /usr/bin
    /bin
    /usr/sbin
    /sbin
  然后在~/.zprofile的内容是PATH="/Library/Frameworks/Python.framework/Versions/3.4/bin:${PATH}"
  最后在~/.zshrc的内容是export PATH=/usr/local/sbin:$HOME/bin:/usr/local/bin:$PATH
  所以最后我们在终端里面打印环境变量PATH,使用echo $PATH 得到的信息是/usr/local/sbin:/Users/sunmenghua/bin:/usr/local/bin:/Library/Frameworks/Python.framework/Versions/3.4/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin,这就对应上了

二:找到可执行文件
  在/usr/local/bin【路径1】和/Library/Frameworks/Python.framework/Versions/3.4/bin【路径2】路径下面,我们都发现了celery
  然而执行celery -A msg_task worker的时候,发现,执行有报错,说【路径1】下面的celery是一个错误的编译器(bad interpreter),并且/usr/ocal/opt/python3/bin/python3.5不存在,所以在【路径2】找到对应的可执行文件

三:分析可执行文件

  打开文件,能够定位到入口是在celery模块中__main__.py里面的面函数,看到的内容是: 

1 import re
2 import sys
3 from celery.__main__ import main
4 if __name__ == '__main__':
5   sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) # 正则匹配与替换
6   sys.exit(main())    # 退出时运行从celery模块的__main__.py里面的main函数

  篇外话:发现了一个比较有用处的模块,就是sys模块。之前我写脚本的时候,都是使用的tornado.options模块。使用形式是:  

1 from tornado.options import options, define # 引入模块
2 define("action", default="userinc", help="update static module") # 定义传进来的参数
3 if __name__ == "__main__":
4 options.parse_command_line() # 分析命令行里面传进来的所有参数,默认是sys.argv,然后会丢掉第一个参数sys.argv[0],因为它是程序名
5 action=options.action    然后调用形式就是 python3 test.py --action=test,经过追踪tornado的源码,知道这个模块其实是在sys.args进行了一层封装
四:找到python3模块  
通过import celery ,然后help(celery),  
Ⅰ.找到模块的源码所在地方为/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/site-packages/celery/;  

Ⅱ.用sublime打开对应的文件夹,先找__main__.py,在根据导入模块找到celery/bin/celery.py里面的main函数
Ⅲ.找到执行方法是在class CeleryCommand的execute_from_commandline;
Ⅳ.继续向上追溯,执行方法在celery.bin.base里面的execute_from_commandline;
Ⅴ.打印调试,真正读参数是在setup_app_from_command(argv)
Ⅵ.打印调试,往下走到self.find_app(app);
Ⅶ.调用的是from celery.app.utils import find_app方法传给他的两个参数,一个app是上面传下来的参数,另一个是symbol_by_name,这个参数是一个函数,搞得非常复杂,简直太复杂了,绕的一大圈。
###这里尝试描述一下:
##A.find_app(app, symbol_by_name=self.symbol_by_name)使用的是当前类里面的函数;
##B.from celery.utils import imports
##def symbol_by_name(self, name, imp=imports.import_from_cwd):
##return imports.symbol_by_name(name, imp=imp)
##C0.但是在celery/utils.py里面又有from celery.utils.imports import import_from_cwd,symbol_by_name
##D0.继续去celery/utils/imports.py里面又发现:from kombu.utils.imports import symbol_by_name
##E0.找kombu/utils/imports.py,确实是最后的导入是在这边,可是这里的逻辑是如果没有传导入函数的话,那就用importlib.import_module做导入函数,否则用调用的时候传的导入函数;好吧,那我们使用的时候,其实在步骤B里面有传入一个导入函数,那我们就继续追着B看一下,这个导入函数是哪里传入进来的;
##C1:跟C0一样
##D1:像B的这种形式,那是没有传参数的,所以,这边得到的这个函数也是importlib.import_module

上面的追踪不继续描述了,言语难以表达。
不过在追踪的过程中,还是有几点理解。
  1.函数不调用就不执行。
    写一个简化的调用。

def t1():
 print('t1')

def main(c=t1):
 c()
 print('main')

if __name__ == '__main__':
    main()
def t1():
 print('t1')

def main(c=t1):
 print('main')

if __name__ == '__main__':
    main()

 


    

 

 

 

 

    第一个的打印是t1和main,第二个的打印是main
  2.后面跟别人聊,本来我以为这样子的代码应该算是一种比较糟糕的代码;但是被告知,这种难以理解代码的产生是面向对象编程和产品迭代产生的;调用某个新的模块的时候,不需要知道他的内部实现原理,只需要他的参数和返回,然后如果想拓展他,就直接拓展接口就好了。

但我还是不太理解,后面再想想吧。