分类 语言 下的文章

golang 源码编译安装

在version<=1.4,golang的所有组件都是用c+asm写的,之后的版本是用go+asm写的,所以安装早期的版本只需要gcc即可,但是后期版本的安装需要依赖早期的版本,下面分别从1.4版本和1.8.3两个版本分别介绍其源码安装过程

  • 1.4版本

    cd ~/package
    wget https://storage.googleapis.com/golang/go1.4-bootstrap-20170531.tar.gz
    tar zxvf go1.4-bootstrap-20170531.tar.gz go1.4
    cd go1.4/src/
    CGO_ENABLED=0 ./all.bash

    若只想使用1.4版本,此时可以在~/.bash_profile中添加export PATH=$PATH:~/package/go1.4/bin

  • 1.8.3版本

    cd ~/package
    wget https://github.com/golang/go/archive/go1.8.3.tar.gz
    tar zxvf go1.8.3.tar.gz go1.8.3
    export GOROOT_BOOTSTRAP=~/package/go1.4
    cd go1.8.3/src
    ./all.bash

    若想用1.8.3版本,可以把~/bash.profile文件中的go1.4改成go1.8.3即可

python多进程和多线程的效率

  • python多进程和多线程通过multiprocessing和threading两个模块实现,由于python是c写的,所以和C语言的多进程和多线程基本相同,唯一不同的是python多线程并没有利用多核cpu的并行处理能力,无论开启多少个线程,始终只占用一个核心的cpu利用率,原因在于GIL(全局解释器锁)限制了同一时刻只能有一个线程在运行,GIL有其历史原因,至今依然存在主要在于python本身运行效率远低于c,若需要多线程处理,直接调用c的接口即可,也可以采用python的多进程来处理。下面具体分析一下python多进程和多线程的效率孰优孰劣
  • 单核cpu
    多线程效率高于多进程
  • 多核cpu
    对于cpu密集型任务,多进程的效率高于多线程,对于IO密集型任务,当任务数超出核心一定数量后,多线程效率高于多进程

python pip安装

1. 下载pip源码

https://pypi.org/project/pip/#files

2. 安装setuptools

可参考Python MySQLdb 模块安装

3. 解压运行setup.py

python setup.py install

从c语言层分析python的实现原理

python作为一门开源的、跨平台的脚本语言,其底层全部用c来实现,下面从c语言的角度来分析python的执行过程

1. python解释器

编译python源码后,会在根目录生成python程序,此程序就是python脚本的解释器,python解释器主要由语法解析、编译和执行三个模块组成

2. python语法解析模块

python语法解析模块负责把python字符串或脚本文件根据其语法解析成c语言的数据结构

3. python编译模块

python编译模块负责把解析后的数据结构转换成虚拟机可执行的字节码对象PyCodeObject

4. python执行模块

python执行模块负责执行python字节码,通常也叫做python虚拟机

当执行python file时,python首先会判断file是否为字节码文件,通常字节码文件的扩展名为pyc或pyo,而普通python脚本为py,判断的逻辑如下:

if (maybe_pyc_file(fp, filename, ext, closeit)) {
        /* Try to run a pyc file. First, re-open in binary */
        if (closeit)
            fclose(fp);
        if ((fp = fopen(filename, "rb")) == NULL) {
            fprintf(stderr, "python: Can't reopen .pyc file\n");
            goto done;
        }
        /* Turn on optimization if a .pyo file is given */
        if (strcmp(ext, ".pyo") == 0)
            Py_OptimizeFlag = 1;
        v = run_pyc_file(fp, filename, d, d, flags);
    } else {
        v = PyRun_FileExFlags(fp, filename, Py_file_input, d, d,
                              closeit, flags);
    }

若是字节码文件,则直接调用执行模块来执行,执行函数如下:

PyAPI_FUNC(PyObject *) PyEval_EvalCodeEx(PyCodeObject *co,
                    PyObject *globals,
                    PyObject *locals,
                    PyObject **args, int argc,
                    PyObject **kwds, int kwdc,
                    PyObject **defs, int defc,
                    PyObject *closure);

若是脚本文件,则先调用解析模块再调用编译模块,执行函数如下:

PyAPI_FUNC(struct _mod *) PyParser_ASTFromString(const char *, const char *,
                                                 int, PyCompilerFlags *flags,
                                                 PyArena *);
PyAPI_FUNC(struct _mod *) PyParser_ASTFromFile(FILE *, const char *, int,
                                               char *, char *,
                                               PyCompilerFlags *, int *,
                                               PyArena *);
PyAPI_FUNC(PyCodeObject *) PyAST_Compile(struct _mod *, const char *,
                        PyCompilerFlags *, PyArena *);

通常很多人会认为,编译后的python脚本会执行很快,但事实并非如此,编译后的字节码文件只是省去了语法解析和编译两部分,所以执行效率和普通python脚本一样,只是加载速度相对较快。目前由于个人能力有限,暂时大概分析了python的执行过程,至于语法解析、编译和执行的细节,逻辑比较复杂,待内功修炼到一定层次,再一一揭开python的神秘面纱

c++类函数编译链接问题

1. 缘由

由于前段时间遇到类外定义函数出现链接错误,而类内定义却链接成功,所以花了点时间从汇编角度来分析c++类函数编译链接问题

2. 对函数的理解

可能很多人对类函数和全局函数的理解不够深刻,所以先简单说明一下。从机器语言角度理解,函数本质上就是代码区的地址,所以无论全局还是类函数都仅是一个地址而已。C全局函数用函数名唯一标记这个地址,C++全局函数用函数名和参数类型名来唯一标记,而C++类函数则用类名、函数名、参数类型名来唯一标记,所以C++有面向对象、同名函数可重载等特性。事实上,类静态函数和非静态函数的调用机制本质上都是函数的调用机制,参数进栈->跳转至函数地址->局部变量进栈->函数逻辑->局部变量出栈->参数出栈->返回调用前地址,对于非静态函数,无非把类对象本身作为入栈参数传递给函数,所以类非静态函数至少有一个参数入栈,以下是类非静态函数的反汇编

图一.jpg

3. 函数在类内定义和类外定义区别

很多人甚至很多权威书籍认为类内定义的函数属于内联函数,事实上并非如此,若编译时采用默认优化选项(O=0),则不属于内联函数,只有设置优化选项(O>=1),类内定义的函数才是内联的,而类外定义的函数,除非加上inline关键字,否则始终不是内联函数,以下是类内定义函数的反汇编

图二.jpg

4. 链接器对函数的处理

若a.h中定义一个函数,b.cpp和c.cpp均包含a.h,则链接时会提示函数重复定义。同样若在a.h中定义一个类,并在类外部定义一个函数,则链接同样会提示函数重复定义,但在类内部定义一个函数,则链接不会报错。很多人认为在类内部定义函数被内联了,当然不会报错,但事实不然,若此时采用默认优化选项(O=0),同样也不会报错。学过汇编的都应该知道,代码区是分节的,通常代码区首节是.init用于进程初始化,我们写的代码基本都在.text节,通过对b.o和c.o进行反汇编发现,全局函数和类外定义的函数均在.text节,而类内定义的函数则在.text.fun节,fun为函数名。因此对于全局和类外定义的函数而言,都在.text节,链接器在合并.text内的代码时,当然会报函数重复定义,而对于类内定义的函数而言,在.text.fun节中,链接器首先把b.o中.text.fun节合并到.text,之后在合并c.o中.text.fun节时,发现已经合并过了,则会跳过,当然就不会报函数重复定义。对于模板函数就更特殊了,无论是全局模板函数还是在类内部或外部定义的模板函数,编译器均会给模板函数分配代码节.text.fun,因此模板函数不存在链接时重复定义问题。以下是类内和类外定义函数的反汇编

图三.jpg