精华内容
下载资源
问答
  • 2018-11-30 11:49:21

    如下,Centos里启动 php-fpm 时,控制台总在抛若干警告。

    [root@aerchi] #service php-fpm start
    
    Starting php-fpm daemon is success[28-Nov-2018 17:45:40] NOTICE: PHP message: PHP Warning:  PHP Startup: Unable to load dynamic library 'curl' (tried: /usr/local/php/lib/php/extensions/no-debug-zts-20170718/curl (/usr/local/php/lib/php/extensions/no-debug-zts-20170718/curl: cannot open shared object file: No such file or directory), /usr/local/php/lib/php/extensions/no-debug-zts-20170718/curl.so (/usr/local/php/lib/php/extensions/no-debug-zts-20170718/curl.so: cannot open shared object file: No such file or directory)) in Unknown on line 0
    [28-Nov-2018 17:45:40] NOTICE: PHP message: PHP Warning:  PHP Startup: Unable to load dynamic library 'bcmath.so' (tried: /usr/local/php/lib/php/extensions/no-debug-zts-20170718/bcmath.so (/usr/local/php/lib/php/extensions/no-debug-zts-20170718/bcmath.so: cannot open shared object file: No such file or directory), /usr/local/php/lib/php/extensions/no-debug-zts-20170718/bcmath.so.so (/usr/local/php/lib/php/extensions/no-debug-zts-20170718/bcmath.so.so: cannot open shared object file: No such file or directory)) in Unknown on line 0
    [28-Nov-2018 17:45:40] NOTICE: PHP message: PHP Warning:  PHP Startup: Unable to load dynamic library 'fileinfo' (tried: /usr/local/php/lib/php/extensions/no-debug-zts-20170718/fileinfo (/usr/local/php/lib/php/extensions/no-debug-zts-20170718/fileinfo: cannot open shared object file: No such file or directory), /usr/local/php/lib/php/extensions/no-debug-zts-20170718/fileinfo.so (/usr/local/php/lib/php/extensions/no-debug-zts-20170718/fileinfo.so: cannot open shared object file: No such file or directory)) in Unknown on line 0
    [28-Nov-2018 17:45:40] NOTICE: PHP message: PHP Warning:  PHP Startup: Unable to load dynamic library 'mbstring' (tried: /usr/local/php/lib/php/extensions/no-debug-zts-20170718/mbstring (/usr/local/php/lib/php/extensions/no-debug-zts-20170718/mbstring: cannot open shared object file: No such file or directory), /usr/local/php/lib/php/extensions/no-debug-zts-20170718/mbstring.so (/usr/local/php/lib/php/extensions/no-debug-zts-20170718/mbstring.so: cannot open shared object file: No such file or directory)) in Unknown on line 0
    [28-Nov-2018 17:45:40] NOTICE: PHP message: PHP Warning:  PHP Startup: Unable to load dynamic library 'mysqli' (tried: /usr/local/php/lib/php/extensions/no-debug-zts-20170718/mysqli (/usr/local/php/lib/php/extensions/no-debug-zts-20170718/mysqli: cannot open shared object file: No such file or directory), /usr/local/php/lib/php/extensions/no-debug-zts-20170718/mysqli.so (/usr/local/php/lib/php/extensions/no-debug-zts-20170718/mysqli.so: cannot open shared object file: No such file or directory)) in Unknown on line 0
    [28-Nov-2018 17:45:40] NOTICE: PHP message: PHP Warning:  PHP Startup: Unable to load dynamic library 'openssl' (tried: /usr/local/php/lib/php/extensions/no-debug-zts-20170718/openssl (/usr/local/php/lib/php/extensions/no-debug-zts-20170718/openssl: cannot open shared object file: No such file or directory), /usr/local/php/lib/php/extensions/no-debug-zts-20170718/openssl.so (/usr/local/php/lib/php/extensions/no-debug-zts-20170718/openssl.so: cannot open shared object file: No such file or directory)) in Unknown on line 0
    [28-Nov-2018 17:45:40] NOTICE: PHP message: PHP Warning:  PHP Startup: Unable to load dynamic library 'pdo_mysql' (tried: /usr/local/php/lib/php/extensions/no-debug-zts-20170718/pdo_mysql (/usr/local/php/lib/php/extensions/no-debug-zts-20170718/pdo_mysql: cannot open shared object file: No such file or directory), /usr/local/php/lib/php/extensions/no-debug-zts-20170718/pdo_mysql.so (/usr/local/php/lib/php/extensions/no-debug-zts-20170718/pdo_mysql.so: cannot open shared object file: No such file or directory)) in Unknown on line 0
    [28-Nov-2018 17:45:40] NOTICE: PHP message: PHP Warning:  PHP Startup: Unable to load dynamic library 'bcmath.so' (tried: /usr/local/php/lib/php/extensions/no-debug-zts-20170718/bcmath.so (/usr/local/php/lib/php/extensions/no-debug-zts-20170718/bcmath.so: cannot open shared object file: No such file or directory), /usr/local/php/lib/php/extensions/no-debug-zts-20170718/bcmath.so.so (/usr/local/php/lib/php/extensions/no-debug-zts-20170718/bcmath.so.so: cannot open shared object file: No such file or directory)) in Unknown on line 0
    [28-Nov-2018 17:45:40] NOTICE: PHP message: PHP Warning:  PHP Startup: Unable to load dynamic library 'curl.so' (tried: /usr/local/php/lib/php/extensions/no-debug-zts-20170718/curl.so (/usr/local/php/lib/php/extensions/no-debug-zts-20170718/curl.so: cannot open shared object file: No such file or directory), /usr/local/php/lib/php/extensions/no-debug-zts-20170718/curl.so.so (/usr/local/php/lib/php/extensions/no-debug-zts-20170718/curl.so.so: cannot open shared object file: No such file or directory)) in Unknown on line 0
    [28-Nov-2018 17:45:40] NOTICE: PHP message: PHP Warning:  PHP Startup: Unable to load dynamic library 'fileinfo.so' (tried: /usr/local/php/lib/php/extensions/no-debug-zts-20170718/fileinfo.so (/usr/local/php/lib/php/extensions/no-debug-zts-20170718/fileinfo.so: cannot open shared object file: No such file or directory), /usr/local/php/lib/php/extensions/no-debug-zts-20170718/fileinfo.so.so (/usr/local/php/lib/php/extensions/no-debug-zts-20170718/fileinfo.so.so: cannot open shared object file: No such file or directory)) in Unknown on line 0
    [28-Nov-2018 17:45:40] NOTICE: PHP message: PHP Warning:  PHP Startup: Unable to load dynamic library 'json.so' (tried: /usr/local/php/lib/php/extensions/no-debug-zts-20170718/json.so (/usr/local/php/lib/php/extensions/no-debug-zts-20170718/json.so: cannot open shared object file: No such file or directory), /usr/local/php/lib/php/extensions/no-debug-zts-20170718/json.so.so (/usr/local/php/lib/php/extensions/no-debug-zts-20170718/json.so.so: cannot open shared object file: No such file or directory)) in Unknown on line 0
    [28-Nov-2018 17:45:40] NOTICE: PHP message: PHP Warning:  PHP Startup: Unable to load dynamic library 'mcrypt.so' (tried: /usr/local/php/lib/php/extensions/no-debug-zts-20170718/mcrypt.so (/usr/local/php/lib/php/extensions/no-debug-zts-20170718/mcrypt.so: cannot open shared object file: No such file or directory), /usr/local/php/lib/php/extensions/no-debug-zts-20170718/mcrypt.so.so (/usr/local/php/lib/php/extensions/no-debug-zts-20170718/mcrypt.so.so: cannot open shared object file: No such file or directory)) in Unknown on line 0
    [28-Nov-2018 17:45:40] NOTICE: PHP message: PHP Warning:  PHP Startup: Unable to load dynamic library 'phar.so' (tried: /usr/local/php/lib/php/extensions/no-debug-zts-20170718/phar.so (/usr/local/php/lib/php/extensions/no-debug-zts-20170718/phar.so: cannot open shared object file: No such file or directory), /usr/local/php/lib/php/extensions/no-debug-zts-20170718/phar.so.so (/usr/local/php/lib/php/extensions/no-debug-zts-20170718/phar.so.so: cannot open shared object file: No such file or directory)) in Unknown on line 0
    [28-Nov-2018 17:45:40] NOTICE: PHP message: PHP Warning:  PHP Startup: Unable to load dynamic library 'zip.so' (tried: /usr/local/php/lib/php/extensions/no-debug-zts-20170718/zip.so (/usr/local/php/lib/php/extensions/no-debug-zts-20170718/zip.so: cannot open shared object file: No such file or directory), /usr/local/php/lib/php/extensions/no-debug-zts-20170718/zip.so.so (/usr/local/php/lib/php/extensions/no-debug-zts-20170718/zip.so.so: cannot open shared object file: No such file or directory)) in Unknown on line 0
    [28-Nov-2018 17:45:40] ERROR: unable to bind listening socket for address '127.0.0.1:9000': Address already in use (98)

     由于折腾 Centos主机不是很溜, 安装php时参考了别人的配置,编译php时没有开启若干扩展(很大的一个坑啊).

    在实际使用中抛错了,再想添加扩展,然而不想卸载后再重装,这下就需要用到phpize来帮忙了。

    比如增加bcmath扩展的支持,这是一个支持大整数计算的扩展。

    由于windows系统是自带而且内置,linux“本类函数仅在 PHP 编译时配置了 --enable-bcmath 时可用”(引号内是手册中的话)


    PHP 版本: php-7.2.11, 

    PHP源文件路径: /usr/local/src/php-7.2.11

    PHP的安装后路径: /usr/local/php/
    php.ini 文件路径:  /etc/php.ini

    对应的模块.ini 文件路径: /etc/php.d/

    php-config 路径: /usr/local/php/bin/php-config

    扩展模块安装路径:   /usr/local/php/lib/php/extensions/no-debug-zts-20170718


    解决方法:
    首先,确保抛警告的模块都已经安装。

    1. 进入源文件路径目录:  cd /usr/local/src/php-7.2.11/ext

    2.  进入bcmath目录(安装其它模块,换成其它模块名称即可): cd  bcmath 

    3. 然后执行输入:    /usr/local/php/bin/phpize

    4. 然后再执行:    ./configure --with-php-config=/usr/local/php/bin/php-config

    5. 执行命令:  make && make install

    注:(基于安装bcmatch, 以上命令均在 /usr/local/src/php-7.2.11/ext/bcmath 下执行)


    控制台操作步骤:

    [root@aerchi ext]# cd /usr/local/src/php-7.2.11/ext/bcmath
    [root@aerchi bcmath]# ls
    bcmath.c   config.m4   CREDITS    package.xml   tests
    bcmath.lo  config.w32  libbcmath  php_bcmath.h
    [root@aerchi bcmath]# /usr/local/php/bin/phpize
    Configuring for:
    PHP Api Version:         20170718
    Zend Module Api No:      20170718
    Zend Extension Api No:   320170718
    [root@aerchi bcmath]# ./configure --with-php-config=/usr/local/php/bin/php-config
    ...
    ...
    config.status: creating config.h
    [root@aerchi bcmath]# make && make install
    ...
    ...
    ----------------------------------------------------------------------
    Libraries have been installed in:
       /usr/local/src/php-7.2.11/ext/bcmath/modules
    
    See any operating system documentation about shared libraries for
    more information, such as the ld(1) and ld.so(8) manual pages.
    ----------------------------------------------------------------------
    
    Build complete.
    Don't forget to run 'make test'.
    
    Installing shared extensions:     /usr/local/php/lib/php/extensions/no-debug-zts-20170718/

    以上,提示安装完成,  然后进入  /usr/local/php/lib/php/extensions/no-debug-zts-20170718/  路径 下查看:

    如下,模块安装完成,下面,来检查一下。

    [root@aerchi ~]# /usr/local/php/sbin/php-fpm  -t
    [30-Nov-2018 17:09:32] NOTICE: PHP message: PHP Warning:  PHP Startup: Unable to load dynamic library 'curl' (tried: /usr/local/php/lib/php/extensions/no-debug-                 zts-20170718/curl (/usr/local/php/lib/php/extensions/no-debug-zts-20170718/curl:                  cannot open shared object file: No such file or directory), /usr/local/php/lib/                 php/extensions/no-debug-zts-20170718/curl.so (/usr/local/php/lib/php/extensions/                 no-debug-zts-20170718/curl.so: cannot open shared object file: No such file or d                 irectory)) in Unknown on line 0
    [30-Nov-2018 17:09:32] NOTICE: PHP message: PHP Warning:  PHP Startup: Unable to load dynamic library 'bcmath' (tried: /usr/local/php/lib/php/extensions/no-debu                 g-zts-20170718/bcmath (/usr/local/php/lib/php/extensions/no-debug-zts-20170718/b                 cmath: cannot open shared object file: No such file or directory), /usr/local/ph                 p/lib/php/extensions/no-debug-zts-20170718/bcmath.so (/usr/local/php/lib/php/ext                 ensions/no-debug-zts-20170718/bcmath.so: undefined symbol: _tsrm_ls_cache)) in U                 nknown on line 0
    [30-Nov-2018 17:09:32] NOTICE: PHP message: PHP Warning:  PHP Startup: Unable to load dynamic library 'fileinfo' (tried: /usr/local/php/lib/php/extensions/no-de                 bug-zts-20170718/fileinfo (/usr/local/php/lib/php/extensions/no-debug-zts-201707                 18/fileinfo: cannot open shared object file: No such file or directory), /usr/lo                 cal/php/lib/php/extensions/no-debug-zts-20170718/fileinfo.so (/usr/local/php/lib                 /php/extensions/no-debug-zts-20170718/fileinfo.so: undefined symbol: file_global                 s)) in Unknown on line 0
    [30-Nov-2018 17:09:32] NOTICE: PHP message: PHP Warning:  PHP Startup: Unable to load dynamic library 'mbstring' (tried: /usr/local/php/lib/php/extensions/no-de                 bug-zts-20170718/mbstring (/usr/local/php/lib/php/extensions/no-debug-zts-201707                 18/mbstring: cannot open shared object file: No such file or directory), /usr/lo                 cal/php/lib/php/extensions/no-debug-zts-20170718/mbstring.so (/usr/local/php/lib                 /php/extensions/no-debug-zts-20170718/mbstring.so: cannot open shared object fil                 e: No such file or directory)) in Unknown on line 0
    [30-Nov-2018 17:09:32] NOTICE: PHP message: PHP Warning:  PHP Startup: Unable to load dynamic library 'mysqli' (tried: /usr/local/php/lib/php/extensions/no-debu                 g-zts-20170718/mysqli (/usr/local/php/lib/php/extensions/no-debug-zts-20170718/m                 ysqli: cannot open shared object file: No such file or directory), /usr/local/ph                 p/lib/php/extensions/no-debug-zts-20170718/mysqli.so (/usr/local/php/lib/php/ext                 ensions/no-debug-zts-20170718/mysqli.so: cannot open shared object file: No such                  file or directory)) in Unknown on line 0
    [30-Nov-2018 17:09:32] NOTICE: PHP message: PHP Warning:  PHP Startup: Unable to load dynamic library 'openssl' (tried: /usr/local/php/lib/php/extensions/no-deb                 ug-zts-20170718/openssl (/usr/local/php/lib/php/extensions/no-debug-zts-20170718                 /openssl: cannot open shared object file: No such file or directory), /usr/local                 /php/lib/php/extensions/no-debug-zts-20170718/openssl.so (/usr/local/php/lib/php                 /extensions/no-debug-zts-20170718/openssl.so: cannot open shared object file: No                  such file or directory)) in Unknown on line 0
    [30-Nov-2018 17:09:32] NOTICE: PHP message: PHP Warning:  PHP Startup: Unable to load dynamic library 'pdo_mysql' (tried: /usr/local/php/lib/php/extensions/no-d                 ebug-zts-20170718/pdo_mysql (/usr/local/php/lib/php/extensions/no-debug-zts-2017                 0718/pdo_mysql: cannot open shared object file: No such file or directory), /usr                 /local/php/lib/php/extensions/no-debug-zts-20170718/pdo_mysql.so (/usr/local/php                 /lib/php/extensions/no-debug-zts-20170718/pdo_mysql.so: cannot open shared objec                 t file: No such file or directory)) in Unknown on line 0
    [30-Nov-2018 17:09:32] NOTICE: PHP message: PHP Warning:  PHP Startup: Unable to load dynamic library 'bcmath.so' (tried: /usr/local/php/lib/php/extensions/no-d                 ebug-zts-20170718/bcmath.so (/usr/local/php/lib/php/extensions/no-debug-zts-2017                 0718/bcmath.so: undefined symbol: _tsrm_ls_cache), /usr/local/php/lib/php/extens                 ions/no-debug-zts-20170718/bcmath.so.so (/usr/local/php/lib/php/extensions/no-de                 bug-zts-20170718/bcmath.so.so: cannot open shared object file: No such file or d                 irectory)) in Unknown on line 0
    [30-Nov-2018 17:09:32] NOTICE: PHP message: PHP Warning:  PHP Startup: Unable to load dynamic library 'fileinfo.so' (tried: /usr/local/php/lib/php/extensions/no                 -debug-zts-20170718/fileinfo.so (/usr/local/php/lib/php/extensions/no-debug-zts-                 20170718/fileinfo.so: undefined symbol: file_globals), /usr/local/php/lib/php/ex                 tensions/no-debug-zts-20170718/fileinfo.so.so (/usr/local/php/lib/php/extensions                 /no-debug-zts-20170718/fileinfo.so.so: cannot open shared object file: No such f                 ile or directory)) in Unknown on line 0
    [30-Nov-2018 17:09:32] NOTICE: PHP message: PHP Warning:  PHP Startup: Invalid l                 ibrary (maybe not a PHP library) 'phar.so' in Unknown on line 0
    [30-Nov-2018 17:09:32] NOTICE: PHP message: PHP Warning:  PHP Startup: Unable to load dynamic library 'zip.so' (tried: /usr/local/php/lib/php/extensions/no-debu                 g-zts-20170718/zip.so (/usr/local/php/lib/php/extensions/no-debug-zts-20170718/z                 ip.so: undefined symbol: executor_globals), /usr/local/php/lib/php/extensions/no                 -debug-zts-20170718/zip.so.so (/usr/local/php/lib/php/extensions/no-debug-zts-20                 170718/zip.so.so: cannot open shared object file: No such file or directory)) in                  Unknown on line 0
    [30-Nov-2018 17:09:32] NOTICE: configuration file /usr/local/php/etc/php-fpm.con                 f test is successful

    (注:针对上面检查的问题,极有可能是php.ini 配置文件里没有启用扩展模块路径,见其次)

    进入 php.ini 查看,抛错的正是启用的扩展模块.

    其次, 在php.ini配置文件中,修改 extension_dir的路径(大概 730 行左右)及启用对应模块等。

    如笔者的php 扩展模块路径为: /usr/local/php/lib/php/extensions/no-debug-zts-20170718/
    原始: 

    修改后:

     

    最后,别忘记了重启php-fpm, 并重启nginx 或者apache,reload不行,一定要重启.

    ps -ef|grep nginx # 查看nginx 进程
    
    #php-fpm
    pkill -9 php-fpm
    service php-fpm stop
    service php-fpm start
    
    #nginx
    /usr/local/nginx/sbin/nginx -s stop
    /usr/local/nginx/sbin/nginx -s reload  
    
    /usr/local/nginx/sbin/nginx //启动
    
    #apache
    apachectl stop
    apachectl start
    

    升级 php 7.2 后,使用微信提供的加解密代码时,提示 call to undefined function mcrypt_module_open() ;大脑疯狂运转1秒钟后,得出结论:php 7.2的扩展有变动;查阅相关资料知晓,mcrypt 扩展从 php 7.1.0 开始废弃;自 php 7.2.0 起,会移到 pecl。还好,安装过程不复杂。
    环境:centos 7

    • yum 安装依赖包:

      yum install libmcrypt libmcrypt-devel mcrypt mhash
    • pecl

    apt-get install php-pecl


    安装Libmcrypt

    # cd /usr/local/src
    # wget  https://sourceforge.net/projects/mcrypt/files/Libmcrypt/2.5.8/libmcrypt-2.5.8.tar.gz/download
    # tar -zxvf libmcrypt-2.5.8.tar.gz 
    # cd libmcrypt-2.5.8 
    # /usr/local/php5/bin/phpize #动态编译php模块,使其可以使用configure编译。 
    # ./configure --with-php-config=/usr/local/php/bin/php-config 
    # make && make install
    # libmcript默认安装在/usr/local

    安装mhash

    Mhash是基于离散数学原理的不可逆向的php加密方式扩展库,其在默认情况下不开启。mhash的可以用于创建校验数值,消息摘要,消息认证码,以及无需原文的关键信息保存(如密码)等。

    # cd /usr/local/src
    # wget  https://sourceforge.net/projects/mhash/files/mhash/0.9.9.9/mhash-0.9.9.9.tar.gz/download
    # tar -zxvf mhash-0.9.9.9.tar.gz 
    # cd mhash-0.9.9.9 
    # /usr/local/php5/bin/phpize  
    # ./configure --with-php-config=/usr/local/php/bin/php-config 
    # make  &&  make install

    安装mcrypt
    Mcrypt 扩展库可以实现加密解密功能,就是既能将明文加密,也可以密文还原。PHP加密扩展库Mcrypt的算法和加密模式.
     

    # cd /usr/local/src
    ---------------------
    # wget https://sourceforge.net/projects/mcrypt/files/Libmcrypt/2.5.8/libmcrypt-2.5.8.tar.gz/download 
    # tar -zxvf mcrypt-2.6.8.tar.gz 
    # cd mcrypt-2.6.8 
    或
    # wget http://pecl.php.net/get/mcrypt-1.0.1.tgz
    # tar xf mcrypt-1.0.1.tgz
    # cd mcrypt-1.0.1
    -------------------
    # /usr/local/php/bin/phpize  
    # ./configure --with-php-config=/usr/local/php/bin/php-config 
    # make && make install 

    安装完毕,按照编译安装后的提示


     


    注:比如,zip,  phar,  fileinfo 模块安装也是类似, 只需将 解决方法 (2) 里的模块路径 分别换成 zip, phar, fileinfo, 并执行3、4、5步即可。


    其它方法: 

    1. 打开/usr/local/php/bin/php-config注释掉:extension_dir='/usr/local/php/lib/php/extensions/no-debug-zts-20170718/'解决的

    网上的解决方法是打开/usr/local/php/bin/php-config
    注释掉:extension_dir='/usr/local/php/lib/php/extensions/no-debug-zts-20170718/',
    但是无效,最终解决方式删除  /etc/php.d  下的对应的 .ini 报错文件!!!

     

    2. 将/etc/php.d/ 目录下的.ini文件全部重命名为.so文件, 或删除对应的  *.ini 文件

    如:cp /etc/php.d/zip.ini /etc/php.d/zip.so

    若有扩展被重复加载了,打开/etc/php.ini文件,注释扩展加载的地方即可

    vi /etc/php.ini # 将redis、memcache、swoo加载的地方注释即可
    /etc/init.d/php-fpm restart

    3. 其它:

    PHP 目录结构: 

    1. build 和编译有关的目录。

    2. ext 扩展库代码,例如 mysql、zlib、iconv 等我们熟悉的扩展库。其中/ext/standard/目录下是常用的标准函数集。

    3. main 主目录包含主要的 PHP 宏和定义。

    4. sapi 和各种服务器的接口调用,例如apache、IIS等,也包含一般的fastcgi、cgi等。

    5. win32 和 Windows 下编译 PHP 有关的脚本。

    6. Zend 文件夹核心的引擎,所有的 Zend API 定义与宏等。

    7. scripts Linux 下的脚本目录。

    8. tests 测试脚本目录

    9. sapi 各类 Web 服务器的接口。

    10.TSRM Zend 和 PHP 的 “线程安全资源管理器” (TSRM) 目录。

    11.pear 这个目录就是“PHP 扩展与应用仓库”的目录。包含了PEAR 的核心文件。

    其中几个重要的文件绝对值得你共时间去了解:

    php-src/main/php.h, 位于PHP 主目录。这个文件包含了绝大部分 PHP 宏及 API 定义。

    php-src/Zend/zend.h, 位于 Zend 主目录。这个文件包含了绝大部分 Zend 宏及 API定义。

    php-src/Zend/zend_API.h, 也位于 Zend 主目录,包含了Zend API的定义 


    PHP 安装,详见:   https://blog.csdn.net/aerchi/article/details/72777795


    乐意黎

    2018-11-30

    本文地址: https://blog.csdn.net/aerchi/article/details/84646179

    更多相关内容
  • Apollo6.0规划代码ros移植-路径规划可跑工程分享

    千次阅读 多人点赞 2022-04-19 15:30:48
    安装依赖库:ADOL-C 运行Automatic_driving/src/installers里面的install_adolc.sh 打开install_adolc.sh文件,先修改如下图所表标红色的路径为自己电脑ADOL-C-2.6.3下载的绝对路径。不然会找到lib64。 然后执行:...

    前言

    编译版本终于来了!!!

    已经有群学习交流了!想进群私信!

    之前出了:
    Apollo规划代码ros移植-Lattcie的二次规划.
    Apollo规划代码ros移植-Lattice规划框架.
    规划代码ros移植-POMDP预测规划(一).
    Apollo规划代码ros移植-Em planner.
    Apollo规划代码ros移植-混合A*.
    Apollo规划代码ros移植-动态障碍物处理(一).

    本人小研一枚,这里总结了我半年对Apollo6.0代码的学习成果,打包成一个可以移植编译的规划工程,提供给大家学习使用。大家如果需要下载工程代码文件,私信我。后续还会继续添加功能跟大家分享。
    适合人群:
    1.想移植Apollo等流行规划算法到自己平台上运用,然后跑自己的车。
    2.想快速跑流行规划算法(代码运行起来比Apollo的仿真快速,轻量级),并调参。
    3.想高效地学习算法,没有像Apollo有很多冗余代码,工程代码有比较多的注释,方便理解。
    4.想高效的学习移植,因为Apollo的代码有一些坑,听说也不是全部开源的。
    5.或者想有一个规划算法的学习交流平台。

    工程功能

    1. Lattcie规划(单车道)
    2. EM_planner规划(单车道)
    3. frenet采样规划(s/t,d/t采样)
    4. 倒车规划(根据地图特点解决掉头的麻烦)
    5. 避开静态障碍物
    6. Apollo的参考线平滑(ipopt和osqp二次平滑)
    7. 简单的stanly控制算法

    8. 混合A*
    9. pure_pursuit纯跟踪算法
    10. LQR控制算法(有bug待修复)
    11. Lattice超车跟随
    12. 目前没有换道,需要换道的先勿扰

    这份代码有比较详细的注释,比如:
    在这里插入图片描述

    在这里插入图片描述
    在这里插入图片描述

    作者有话说

    也许有很多吐槽说,Apollo开源,也有系统可跑,但是真正去阅读和把车从0开始整合到车可以规划避障,要运行跑起Apollo的代码,而且是Apollo系统的开发环境,这些是特别困难而且不自在的,用过的人才知道,代码上面有很多坑的,除非是牛鼻大佬,还有,Apollo也不是全部开源。除此之外,很多公司都是借助开源的移植性高的系统去开发无人系统,不是用Apollo系统,而是Ros系统。
    这份工程希望可以帮助你们完成学习开发和算法的应用,学起来更快,更好入门实践,因为有很多坑,如果你是大佬有时间自己踩坑和理解代码就勿扰我这个菜鸡。 网上也有很多是simulink的仿真等等,反正都是可以学习可用的,看大家的目标和目的。想交流学习的,就加入。

    环境配置步骤(18.04和20.04都可以)

    1.更新镜像源
    首先要更新镜像源吧,比如ubuntu20.04的清华源。百度一堆:ubuntu20.04的清华源.
    2.安装gcc,g++
    这里,我们要另外添加一个软件源,以安装gcc,g++。我这里讲的是ubuntu20的gcc,g++安装,18的好像没有9和10的版本,记得是7的,不过你们自己安装好这两个编译器就行,自行百度。

    sudo gedit /etc/apt/sources.list
    

    增加

    deb http://cn.archive.ubuntu.com/ubuntu xenial main
    

    注意这个源是ubuntu20的,18的可能不一样。
    执行

    sudo apt-get update
    

    其次,安装gcc 与 g++:

    sudo apt-get install gcc-9 g++-9
    
    sudo apt-get install gcc-10 g++-10
    

    3.安装ros
    网上教程太多啦,你们可以根据自己的系统安装对应的ros版本。我的是ubuntu20.04,安装的是ros-noetic。建议不要用ubuntu16,因为我没试过,用18或者20,也就是melodicnoetic
    4.安装osqp求解器
    运行Automatic_driving/src/planning/installers里面的install_osqp.sh

    sudo ./install_osqp.sh
    

    5.安装依赖库:ADOL-C
    运行Automatic_driving/src/installers里面的install_adolc.sh

    打开install_adolc.sh文件,先修改如下图所表标红色的路径为自己电脑ADOL-C-2.6.3下载的绝对路径。不然会找不到lib64。在这里插入图片描述 然后执行:

    sudo ./install_adolc.sh
    

    6.安装依赖库:ipopt求解器
    运行Automatic_driving/src/installers里面的install_ipopt.sh

    sudo ./install_ipopt.sh
    

    7.安装基本依赖包
    运行Automatic_driving/src/installers里面的install_apt.sh

    sudo ./install_apt.sh
    

    8.安装其他ros包依赖
    运行Automatic_driving/src/installers里面的install_ros.sh

    sudo ./install_ros.sh
    

    注意,里面有ros-noetic,是针对ros-noetic版本的,melodic版本要改成ros-melodic。
    9.安装xerces-c

    sudo apt-get install libxerces-c-dev 
    

    10.安装Qt5
    参考:https://blog.csdn.net/Joker__123/article/details/122438138

    11.安装qpoases求解器

    sudo ./install_qp_oases.sh
    

    12.安装yaml读取

    sudo ./install_yaml.sh
    

    警告:
    1.特别说明:如果出现输入 sudo: ./xxx.sh:找不到命令,则执行

    chmod o+x ./xxx.sh
    

    再执行

    sudo ./xxx.sh
    

    2.如果出现无法定位包,就是没有更新软件源和安装ros。
    有些人总是会找不到lanelet_core。那说明你都没有执行完成install_ros.sh,因为这个sh里面就有安装这个lanelet_core的apt包。你们不要急,特别是小白,要更加仔细地慢慢来,心急吃不了。。。
    3.
    在这里插入图片描述
    这是github不好下,重新运行或者梯子。

    编译

    在工程文件~/Automatic_driving$ 下执行

    catkin build
    

    因为缺失依赖而没有成功编译的,可屏蔽相关的包catkin config --blacklist <pkg>,取消黑名单catkin config --no-blacklist
    需要屏蔽某个模块的package,可以直接放一个名为CATKIN_IGNORE 的文件到所在目录,则该目录下的包就不会被编译

    编译报错经验

    1.报错信息

    CMake Error at /opt/ros/noetic/share/catkin/cmake/empy.cmake:30 (message):
      Unable to find either executable 'empy' or Python module 'em'...  try
      installing the package 'python3-empy'
    

    注意,因为我安装了conda环境,在base下编译的,所以才可能出现这个问题,解决方法:

    pip install empy
    

    2.报错信息

    ImportError: "from catkin_pkg.package import parse_package" failed: No module named 'catkin_pkg'
    Make sure that you have installed "catkin_pkg", it is up to date and on the PYTHONPATH.
    CMake Error at /opt/ros/noetic/share/catkin/cmake/safe_execute_process.cmake:11 (message):
      execute_process(/usr/local/anaconda3/bin/python3
      "/opt/ros/noetic/share/catkin/cmake/parse_package_xml.py"
      "/opt/ros/noetic/share/catkin/cmake/../package.xml"
      "/home/cg/Automatic_driving/build/catkin_tools_prebuild/catkin/catkin_generated/version/package.cmake")
      returned error code 1
    

    注意,因为我安装了conda环境,在base下编译的,所以才可能出现这个问题,解决方法:

    pip install catkin_pkg
    

    3.报错信息

    Make Error at /opt/ros/noetic/share/catkin/cmake/catkinConfig.cmake:83 (find_package):
      Could not find a package configuration file provided by "object_msgs"
      with any of the following names:
    
        object_msgsConfig.cmake
        object_msgs-config.cmake
    

    解决方法:先编译object_msgs

    catkin build object_msgs
    

    catkin build 
    

    4.报错信息

    CMake Error at /opt/ros/noetic/share/catkin/cmake/catkinConfig.cmake:83 (find_package):
      Could not find a package configuration file provided by
      "lanelet2_extension" with any of the following names:
    
        lanelet2_extensionConfig.cmake
        lanelet2_extension-config.cmake
    

    解决方法:不慌,重新编译

    catkin build lanelet2_extension
    

    catkin build 
    

    5.报错信息

    /usr/bin/ld: /lib/x86_64-linux-gnu/libapr-1.so.0: undefined reference to `uuid_generate@UUID_1.0'
    collect2: error: ld returned 1 exit status
    make[2]: *** [CMakeFiles/ui_console.dir/build.make:150/home/cg/Automatic_driving/devel/.private/ui_console/lib/ui_console/ui_console] 错误 1
    make[1]: *** [CMakeFiles/Makefile2:166:CMakeFiles/ui_console.dir/all] 错误 2
    make: *** [Makefile:141:all] 错误 2
    

    这个错误的原因,还是因为有了conda环境,QT引用的是anaconda里的qt,因为我在ui_console里面用了qt画图,当然,你可以把这个文件夹删除,也可以解决。解决方法:

    到anaconda安装文件夹下的anaconda/lib下:

    sudo mkdir libuuid_bk
    mv libuuid* libuuid_bk/
    

    6.报错信息

    Traceback (most recent call last):
      File "/opt/ros/noetic/lib/joint_state_publisher/joint_state_publisher", line 35, in <module>
        import rospy
      File "/opt/ros/noetic/lib/python3/dist-packages/rospy/__init__.py", line 49, in <module>
        from .client import spin, myargv, init_node, \
      File "/opt/ros/noetic/lib/python3/dist-packages/rospy/client.py", line 52, in <module>
        import roslib
      File "/opt/ros/noetic/lib/python3/dist-packages/roslib/__init__.py", line 50, in <module>
        from roslib.launcher import load_manifest  # noqa: F401
      File "/opt/ros/noetic/lib/python3/dist-packages/roslib/launcher.py", line 42, in <module>
        import rospkg
    ModuleNotFoundError: No module named 'rospkg'
    

    这个错误的原因,还是因为有了conda环境,解决方法:

    pip install rospkg
    

    7.报错信息

      Could not find a package configuration file provided by
      "autoware_lanelet2_msgs" with any of the following names:
    
        autoware_lanelet2_msgsConfig.cmake
        autoware_lanelet2_msgs-config.cmake
    
    

    解决方法:先编译autoware_lanelet2_msgs

    catkin build autoware_lanelet2_msgs
    

    catkin build 
    

    8.报错信息

    在这里插入图片描述

    解决方法:在/Automatic_driving/src/planning/dynamic_routing/package.xml里面加下面msg的类型包:

    object_msgs
    

    catkin build 
    

    9.找不到链接库,不管是ladolclibqpOASES还是其他l开头或者lib开头的。解决办法:
    a.打开**/etc/ld.so.conf**文件:

    sudo gedit /etc/ld.so.conf
    

    b.加入动态库文件所在的目录:在include ld.so.conf.d/*.conf下方增加

    /usr/local/lib
    

    c.保存后,在命令行终端执行:

    sudo ldconfig
    

    10.找不到ipopt相关的头文件或者lanelet_core,那就是你没有运行完成我前面写的sh文件。

    11.编译报Eigen的错误。比如下面这种,因为我的sh文件里面有用apt安装Eigen库,如果你之前系统已经有Eigen库,可能会冲突,删除所有Eigen,再次apt安装Eigen库。

    代码架构

    ├── Automatic_driving ----> 存放无人车架构模块  
            ├── src     ---> 源代码
    		│    ├── common      ---->  公共包
    		│    ├── messages     ----> 自定义消息类型  ,包括autoware的自定义消息类型
    		│    ├── planning      ---->  规划模块  
    				  │    ├── dynamic_routing      ---->  局部规划
    				  │    ├── global_routing     ---->  全局规划
    							│    ├── hdmap      ---->  地图文件
    				  │    ├── installers     ---->  环境配置安装文件与说明文件
    				  │    ├── planning_vehicle_simulator      ---->  ui界面(qt编写的)与车辆模型加载
    				  │    ├── routing_msgs      ---->  局部规划的自定义消息类型
    

    注意,这里的 全局规划和局部规划和紧挨的,全局规划的路线是作为局部规划的参考线。

    代码配置

    我是使用vscode进行代码编辑的。
    在.vscode/c_cpp_properties.json,把路径加载进去,目的是在vscode里面没有红波兰线,虽然编译不会报错。

    在这里插入图片描述

    代码运行

    第一步:

    source ./devel/setup.bash 
    

    第二步:

    roslaunch global_routing global_routing.launch 
    

    说明:src/planning/global_routing/launch/global_routing.launch是最外层的launch,有关参数设置都有注释。

    运行报错经验

    1.运行时可能来不及加载模拟的障碍物(有延时,可能线程没优化好),重新roslaunch就可以了。
    2.报错如下:
    在这里插入图片描述
    就是找不到链接库:libqpOASES.so,解决方法很多,例如:
    a.打开**/etc/ld.so.conf**文件:

    sudo gedit /etc/ld.so.conf
    

    b.加入动态库文件所在的目录:在include ld.so.conf.d/*.conf下方增加

    /usr/local/lib
    

    c.保存后,在命令行终端执行:

    sudo ldconfig
    

    运行效果

    1.rviz加载的地图
    在这里插入图片描述

    2.rviz加载的主题
    在这里插入图片描述

    3.仿真车辆和模拟的障碍物显示
    在这里插入图片描述

    4.定义起点和终点的方法
    起点的定义需要点击如下图的标红位置,注意箭头的方向就是你放置车的方向,这个地图遵循靠右行使,所以代码我写了判别方向的,方向对了才能放车,终点也一样。
    在这里插入图片描述

    终点定义,点这里,一样要注意方向。
    在这里插入图片描述

    点完路径就出来了。
    入门和该注意的地方看我这篇:
    Apollo6.0规划代码ros移植-小白领进门

    5.全局路径切割,即利用Lanelet的特点,进行切割获取,蓝色带为起点到终点的全局路径
    在这里插入图片描述

    6.跑Apollo-Lattice的采样规划,蓝色线为全局路径,也就是参考线啦。红色为局部轨迹,车可以完美绕开障碍物。

    在这里插入图片描述

    7.跑Apollo-Lattice的二次规划,蓝色线为全局路径,也就是参考线啦。红色为局部轨迹,车可以完美绕开障碍物。
    在这里插入图片描述

    8.frenet坐标系的规划,就是分为s-t规划和d-t规划,然后合成进行筛选。代码直观注释。

    在这里插入图片描述

    9.到达终点停止。
    在这里插入图片描述

    10.Apollo参考线平滑算法,有两个平滑方案选择,CosThetaSmoother 和FemPosSmooth,原理自行百度啦,不懂再在交流群交流。效果我不显示地图给你们看会比较清楚
    平滑前:表红的地方都是有明显皱折
    在这里插入图片描述

    CosThetaSmoother平滑后:超级平滑
    在这里插入图片描述

    FemPosSmooth平滑后:超级平滑
    在这里插入图片描述
    11.还有一个很有趣的效果,就是可以倒车到达终点,如果终点在车后脑勺不远的地方,就可以倒车规划。
    在这里插入图片描述
    12.EM:
    Apollo规划代码ros移植-Em planner

    13.混合A*
    Apollo规划代码ros移植-混合A*

    举不完了,看我开头挂的其他子博客链接吧。

    如何应用于实际车辆?(纯属个人见解,不存在暴露其他工作)

    1.将所有坐标值,都换成订阅你们车上定位模块的坐标系下的坐标值。
    2.将模拟测试的障碍物信息,换成订阅你们感知模块的信息,记得,我们规划的障碍物需要什么信息,就对接好感知模块那一边,要发布什么信息给你获取。
    3.发布轨迹点数据给控制模块跟踪。

    须知

    1.如果终点定义很远,插值时间会很大,你们可以自行进行分段处理,这里我就不透露我的做法。
    2.由于我还不需要超车,所以ST图我还没用,但是框架代码也写好了。
    3.二次规划的前探距离要比较长,轨迹也要长一些,但是我在测试的时候没有设置很长,你们可以自己调参。

    结束语

    各位在校想学习这个方向的同学,刚工作这方面的同事,公司创业者,要毕业要毕业设计,课程设计的同学等,都可以拿来学习。由于我也是C++菜鸟,所以代码写得不好请大家见谅。需要下载工程代码文件,可以先私信我,感谢大家的支持,大家一起学习。代码会不断更新迭代加功能。

    算法移植后不是很完美,很多细节需要慢慢调整,如果对要求很高的,勿扰,仅供学习使用。

    算法移植后不是很完美,很多细节需要慢慢调整,如果对要求很高的,勿扰,仅供学习使用。

    算法移植后不是很完美,很多细节需要慢慢调整,如果对要求很高的,勿扰,仅供学习使用。

    后续有空的更新内容:
    1.优化动态障碍物的避障。
    2.carla与我的工程联合仿真。(如果carla跑不起来就换成commonroad)
    3.换道与超车。

    展开全文
  • 第二种是模块内部的数据访问,比如模块中定义的全局变量、静态变量:指令中能直接包含数据的绝对地址,那么唯一的办法就是相对寻址。我们知道,一个模块前面一般是若干个页的代码,后面紧跟着若干个页的数据,这些...

    1. 为什么要动态链接

    静态链接诸多缺点,比如浪费内存和磁盘空间、模块更新困难等

    内存和磁盘空间:静态链接的方式对于计算机内存和磁盘的空间浪费非常严重,特别是在多进程操作系统情况下。

    程序开发和发布:空间浪费是静态链接的一个问题,另一个问题是静态链接对程序的更新、部署和发布也会带来很多麻烦。一旦程序中有任何模块更新,整个程序就要重新链接、发布给用户。如果程序都使用静态链接,那么通过网络来更新程序将会非常不便,因为一旦程序任何位置的一个小改动,都会导致整个程序重新下载。

    动态链接:要解决空间浪费和更新困难这两个问题最简单的办法就是把程序的模块相互分割开来,形成独立的问题,而不再将它们静态地链接在一起。简单地讲,就是不对那些组成程序的目标文件进行链接,等到程序要运行时才进行链接。也就是说,把链接这个过程推迟到了运行时再进行,这就是动态链接(Dynamic Linking)的基本思想。动态链接解决了共享的目标文件多个副本浪费磁盘和内存空间的问题。另外在内存中共享一个目标文件模块的好处不仅仅是节省内存,它还可以减少物理页面的换入换出,也可以增加CPU缓存的命中率,因为不同进程间的数据和指令访问都集中在了同一个共享模块上。动态链接方案也可以使程序的升级变得更加容易,当我们要升级程序库或程序共享的某个模块时,理论上只要简单地将旧的目标文件覆盖掉,而无须将所有的程序再重新链接一遍。当程序下一次运行的时候,新版本的目标文件会被自动装载到内存并且链接起来,程序就完成了升级的目标。动态链接的方式使得开发过程中各个模块更加独立,耦合度更小,便于不同的开发者和开发组织之间独立进行开发和测试。

    程序可扩展性和兼容性:动态链接还有一个特点就是程序在运行时可以动态地选择加载各种程序模块,这个优点就是后来被人们用来制作程序的插件(Plug-in)。比如某个公司开发完成了某个产品,它按照一定的规则制定好程序的接口,其它公司或开发者可以按照这种接口来编写符合要求的动态链接文件。该产品程序可以动态地载入各种由第三方开发的模块,在程序运行时动态地链接,实现程序功能的扩展。

    动态链接还可以加强程序的兼容性。一个程序在不同的平台运行时可以动态地链接到由操作系统提供的动态链接库,这些动态链接库相当于在程序和操作系统之间增加了一个中间层,从而消除了程序对不同平台之间依赖的差异性。

    动态链接的基本实现:动态链接的基本思想是把程序按照模块拆分成各个相对独立部分,在程序运行时才将它们链接在一起形成一个完整的程序,而不是像静态链接一样把所有的程序模块都链接成一个单独的可执行文件。动态链接涉及运行时的链接及多个文件的装载,必需要有操作系统的支持,因为动态链接的情况下,进程的虚拟地址空间的分布会比静态链接情况下更为复杂,还有一些存储管理、内存共享、进程线程等机制在动态链接下也会有一些微妙的变化。目前主流的操作系统几乎都支持动态链接这种方式,Linux系统中,ELF动态链接文件被称为动态共享对象(DSO, Dynamic Shared Objects),简称共享对象,它们一般都是以”.so”为扩展名的一些文件;而在Windows系统中,动态链接文件被称为动态链接库(Dynamical Linking Library),它们通常就是很常见的以”.dll”为扩展名的文件

    在Linux中,常用的C语言库的运行库glibc,文件名叫做”libc.so”。整个系统只保留一份C语言库的动态链接文件”libc.so”,而所有的C语言编写的、动态链接的程序都可以在运行时使用它。当程序被装载的时候,系统的动态链接器会将程序所需要的所有动态链接库(最基本的就是libc.so)装载到进程的地址空间,并且将程序中所有未决议的符号绑定到相应的动态链接库中,并进行重定位工作。程序与libc.so之间真正的链接工作是由动态链接器完成的,而不是静态链接器ld完成的。也就是说,动态链接是把链接这个过程从本来的程序装载前被推迟到了装载的时候。

    2. 简单的动态链接例子

    测试代码如下:Program1.c、Program2.c、Lib.c、Lib.h

    Program1.c:

    #include "Lib.h"
    
    int main()
    {
    	foobar(1);
    	return 0;
    }

    Program2.c:

    #include "Lib.h"
    
    int main()
    {
    	foobar(2);
    	return 0;
    }

    Lib.h:

    #ifndef LIB_H
    #define LIB_H
    
    void foobar(int i);
    
    #endif

    Lib.c:

    #include <stdio.h>
    
    void foobar(int i)
    {
    	printf("Printing from Lib.so %d\n", i);
    	sleep(-1);
    }

    脚本build.sh内容如下:执行此脚本后可生成动态库Lib.so,可执行文件Program1和Program2。

    gcc -fPIC -shared -o Lib.so Lib.c
    gcc -o Program1 Program1.c ./Lib.so
    gcc -o Program2 Program2.c ./Lib.so

    在静态链接时,整个程序最终只有一个可执行文件,它是一个不可以分割的整体;但是在动态链接下,一个程序被分成了若干个文件,有程序的主要部分,即可执行文件(Program1)和程序所依赖的共享对象(Lib.so),很多时候我们也把这些部分称为模块,即动态链接下可执行文件和共享对象都可以看作是程序的一个模块

    当程序模块Program1.c被编译成为Program1.o时,编译器还不知道foobar()函数的地址。但链接器将Program1.o链接成可执行文件时,这时候链接器必须确定Program1.o中所引用的foobar()函数的性质。如果foobar()是一个定义与其它静态目标模块中的函数,那么链接器将会按照静态链接的规则,将Program1.o中的foobar地址引用重定位;如果foobar()是一个定义在某个动态共享对象中的函数,那么链接器就会将这个符号的引用标记为一个动态链接的符号,不对它进行地址重定位,把这个过程留到装载时再进行。链接器如何知道foobar的引用是一个静态符号还是一个动态符号?这实际上就是我们要用到Lib.so的原因。Lib.so中保存了完整的符号信息(因为运行时进行动态链接还须使用符号信息),把Lib.so也作为链接的输入文件之一,链接器在解析符号时就可以知道:foobar是一个定义在Lib.so的动态符号。这样链接器就可以对foobar的引用做特殊的处理,使它成为一个对动态符号的引用。

    动态链接程序运行时地址空间分布:对于静态链接的可执行文件来说,整个进程只有一个文件要被映射,那就是可执行文件本身。但是对于动态链接来说,除了可执行文件本身之外,还有它所依赖的共享目标文件。

    查看进程的虚拟地址空间分布,结果如下图所示,可以看到,整个进程虚拟地址空间中,多出了几个文件的映射。Lib.so与Program1一样,它们都是被操作系统用同样的方法映射至进程的虚拟地址空间,只是它们占据的虚拟地址和长度不同。Program1除了使用Lib.so以外,它还用到了动态链接形式的C语言运行库libc-2.19.so。另外还有一个很值得关注的共享对象就是ld-2.19.so,它实际上是Linux下的动态链接器。动态链接器与普通共享对象一样被映射到了进程的地址空间,在系统开始运行Program1之前,首先会把控制权交给动态链接器,由它完成所有的动态链接工作以后再把控制权交给Program1,然后开始执行。

    通过readelf工具来查看Lib.so的装载属性,结果如下图所示,除了文件的类型与普通程序不同以外,其它几乎与普通程序一样。还有有一点比较不同的是,动态链接模块的装载地址是从地址0x0000000000000000开始的。我们知道这个地址是无效地址,并且从上面的进程虚拟空间分布看到,Lib.so的最终装载地址并不是0x0000000000000000。从这一点我们可以推断,共享对象的最终装载地址在编译时是不确定的而是在装载时,装载器根据当前地址空间的空闲情况,动态分配一块足够大小的虚拟地址空间给相应的共享对象

    3. 地址无关代码

    装载时重定位:为了能够使共享对象在任意地址装载,我们首先想到的方法就是静态链接中的重定位。这个想法的基本思路就是,在链接时,对所有绝对地址的引用不作重定位,而把这一步推迟到装载时再完成。一旦模块装载地址确定,即目标地址确定,那么系统就对程序中所有的绝对地址引用进行重定位。我们前面在静态链接时提到过重定位,那时的重定位叫做链接时重定位(Link Time Relocation),而现在这种情况经常被称为装载时重定位(Load Time Relocation),在Windows中,这种装载时重定位又被叫做基址重置(Rebasing)。但是装载时重定位的方法并不适合用来解决上面的共享对象中所存在的问题。可以想象,动态链接模块被装载映射至虚拟空间后,指令部分是在多个进程之间共享的,由于装载时重定位的方法需要修改指令,所以没有办法做到同一份指令被多个进程共享,因为指令被重定位后对于每个进程来讲是不同的。当然,动态链接库中的可修改数据部分对于不同的进程来说有多个副本,所以它们可以采用装载时重定位的方法来解决。Linux和GCC支持这种装载时重定位的方法,我们前面在产生共享对象时,使用了两个GCC参数”-shared”和”-fPIC”,如果只使用”-shared”,那么输出的共享对象就是使用装载时重定位的方法

    地址无关代码:装载时重定位是解决动态模块中有绝对地址引用的办法之一,但是它有一个很大的缺点是指令部分无法在多个进程之间共享,这样就失去了动态链接节省内存的一大优势。我们还需要有一种更好的方法解决共享对象指令中对绝对地址的重定位问题。其实我们的目的很简单,希望程序模块中共享的指令部分在装载时不需要因为装载地址的改变而改变,所以实现的基本想法就是把指令中那些需要被修改的部分分离出来,跟数据部分放在一起,这样指令部分就可以保持不变,而数据部分可以在每个进程中拥有一个副本。这种方案就是目前被称为地址无关代码(PIC, Position-independent Code)的技术。

    对于现代的机器来说,产生地址无关的代码并不麻烦。我们先来分析模块中各种类型的地址引用方式:这里把共享对象模块中的地址引用按照是否为跨模块分成两类:模块内部引用和模块外部引用;按照不同的引用方式又可以分为指令引用和数据访问,这样就得到了四种情况,如下图所示:

    第一种是模块内部的函数调用、跳转等:被调用的函数与调用者都处于同一个模块,它们之间的相对位置是固定的,所以这种情况比较简单。对于现代的系统来讲,模块内部的跳转、函数调用都可以是相对地址调用,或者是基于寄存器的相对调用,所以对于这种指令是不需要重定位的。

    第二种是模块内部的数据访问,比如模块中定义的全局变量、静态变量:指令中不能直接包含数据的绝对地址,那么唯一的办法就是相对寻址。我们知道,一个模块前面一般是若干个页的代码,后面紧跟着若干个页的数据,这些页之间的相对位置是固定的,也就是说,任何一条指令与它需要访问的模块内部数据之间的相对位置是固定的,那么只需要相对于当前指令加上固定的偏移量就可以访问模块内部数据了。现代的体系结构中,数据的相对寻址往往没有相对于当前指令地址(PC)的寻址方式,所以ELF用了一个很巧妙的办法来得到当前的PC值,然后再加上一个偏移量就可以达到访问相应变量的目的了。得到PC值的方法很多。

    第三种是模块外部的数据访问,比如其它模块中定义的全局变量:模块间的数据访问目标地址要等到装载时才决定,比如上面例子中的变量b,它被定义在其它模块中,并且该地址在装载时才能确定。要使得代码地址无关,基本的思想就是把跟地址相关的部分放到数据段里面,很明显,这些其它模块的全局变量的地址是跟模块装载地址有关的。ELF的做法是在数据段里面建立一个指向这些变量的指针数组,也被称为全局偏移表(Global Offset Table, GOT),当代码需要引用该全局变量时,可以通过GOT中相对应的项间接引用。当指令中需要访问变量b时,程序会先找到GOT,然后根据GOT中变量所对应的项找到变量的目标地址。每个变量都对应一个4个字节的地址,链接器在装载模块的时候会查找每个变量所在的地址,然后填充GOT中的各个项,以确保每个指针所指向的地址正确。由于GOT本身是放在数据段的,所以它可以在模块装载时被修改,并且每个进程都可以有独立的副本,相互不受影响。GOT如何做到指令的地址无关性?从第二中类型的数据访问我们了解到,模块在编译时可以确定模块内部变量相对于当前指令的偏移,那么我们也可以在编译时确定GOT相对于当前指令的偏移。确定GOT的位置跟上面的访问变量a的方法基本一样,通过得到PC值然后加上一个偏移量,就可以得到GOT的位置。然后我们根据变量地址在GOT中的偏移就可以得到变量的地址,当然GOT中每个地址对应于哪个变量是由编译器决定的。

    第四种是模块外部的函数调用、跳转等: 也可以采用上面类型三的方法来解决,与上面的类型有所不同的是,GOT中相应的项保存的是目标函数的地址,当模块需要调用目标函数时,可以通过GOT中的项进行间接跳转。

    使用GCC产生地址无关代码很简单,我们只需要使用”-fPIC”参数接口。实际上GCC还提供了另外一个类似的参数叫做”-fpic”,即”PIC”3个字母小写,这两个参数从功能上来讲完全一样,都是指示GCC产生地址无关代码。唯一的区别是,”-fPIC”产生的代码要大,而”-fpic”产生的代码相对较小,而且较快。那么我们为什么不使用”-fpic”而要使用”-fPIC”呢?原因是,由于地址无关代码都是跟硬件平台相关的,不同的平台有着不同的实现”-fpic”在某些平台上会有一些限制,比如全局符号的数量或者代码的长度等,而”-fPIC”则没有这样的限制。所以为了方便起见,绝大部分情况下,我们都使用”-fPIC”参数来产生地址无关代码。

    如何区分一个DSO是否为PIC:执行命令:$ readelf -d Lib.so | grep TEXTREL

    如果上面的命令有任何输出,那么Lib.so就不是PIC的,否则就是PIC的。PIC的DSO是不会包含任何代码段重定位表的,TEXTREL表示代码段重定位表地址。

    PIC与PIE:地址无关代码技术除了可以用在共享对象上面,它也可以用于可执行文件,一个以地址无关方式编译的可执行文件被称作地址无关可执行文件(PIE, Position-Independent Executable)GCC的”-fPIC”与”-fpic”参数类似,产生PIE的参数为”-fPIE”或”-fpie”

    共享模块的全局变量问题:ELF共享库在编译时,默认都把定义在模块内部的全局变量当作定义在其它模块的全局变量,也就是说当作前面的类型三,通过GOT来实现变量的访问。当共享模块被装载时,如果某个全局变量在可执行文件中拥有副本,那么动态链接器就会把GOT中的相应地址指向该副本,这样该变量在运行时实际上最终就只有一个实例。如果变量在共享模块中被初始化,那么动态链接器还需要将该初始化值复制到程序主模块中的变量副本;如果该全局变量在程序主模块中没有副本,那么GOT中的相应地址就指向模块内部的该变量副本。

     数据段地址无关性对于数据段来说,它在每个进程都有一份独立的副本,所以并不担心被进程改变。从这点来看,我们可以选择装载时重定位的方法来解决数据段中绝对地址引用问题。对于共享对象来说,如果数据段中有绝对地址引用,那么编译器和链接器就会产生一个重定位表,这个重定位表里面包含了”R_386_RELATIVE”类型的重定位入口,用于解决上述问题。当动态链接器装载共享对象时,如果发现该共享对象有这样的重定位入口,那么动态链接器就会对该共享对象进行重定位。实际上,我们甚至可以让代码段也使用这种装载时重定位的方法,而不使用地址无关代码。但是,如果代码不是地址无关的,它就不能被多个进程之间共享,于是也就失去了节省内存的优点。但是装载时重定位的共享对象的运行速度要比使用地址无关代码的共享对象快,因为它省去了地址无关代码中每次访问全局数据和函数时需要做一次计算当前地址以及间接地址寻址的过程。

    对于可执行文件来说,默认情况下,如果可执行文件是动态链接的,那么GCC会使用PIC的方法来产生可执行文件的代码段部分,以便于不同的进程能够共享代码段,节省内存。所以我们可以看到,动态链接的可执行文件中存在”.got”这样的段。

    4. 延迟绑定(PLT)

    动态链接的确有很多优势,比静态链接要灵活得多,但它是以牺牲一部分性能为代价的。据统计ELF程序在静态链接下要比动态库稍微快点,当然这取决于程序本身的特性及运行环境等。我们知道动态链接比静态链接慢的主要原因是动态链接下对于全局和静态的数据访问都要进行复杂的GOT定位,然后间接寻址;对于模块间的调用也要先定位GOT,然后再进行间接跳转,如此一来,程序的运行速度必定会减慢。另外一个减慢运行速度的原因是动态链接的链接工作在运行时完成,即程序开始执行时,动态链接器都需要进行一次链接工作,动态链接器会寻找并装载所需要的共享对象,然后进行符号查找地址重定位等工作,这些工作势必减慢程序的启动速度。这是影响动态链接性能的两个主要问题。

    延迟绑定实现:在动态链接下,程序模块之间包含了大量的函数引用(全局变量往往比较少,因为大量的全局变量会导致模块之间耦合度变大),所以在程序开始执行前,动态链接会耗费不少时间用于解决模块之间的函数引用的符号查找以及重定位。不过可以想象,在一个程序运行过程中,可能很多函数在程序执行完时都不会被用到,比如一些错误处理函数或者是一些用户很少用到的功能模块等,如果一开始就把所有函数都链接好实际上是一种浪费。所以ELF采用了一种延迟绑定(Lazy Bingding)的做法,基本的思想就是当函数第一次被用到时才进行绑定(符号查找、重定位等),如果没有用到则不进行绑定。所以程序开始执行时,模块间的函数调用都没有进行绑定,而是需要用到时才由动态链接器来负责绑定。这样的做法可以大大加快程序的启动速度,特别有利于一些有大量函数引用和大量模块的程序。ELF使用PLT(Procedure Linkage Table)的方法来实现延迟绑定,这种方法使用了一些很精巧的指令序列来完成。当我们调用某个外部模块的函数时,如果按照通常的做法应该是通过GOT中相应的项进行间接跳转。PLT为了实现延迟绑定,在这个过程中间又增加了一层间接跳转。调用函数并不直接通过GOT跳转,而是通过一个叫做PLT项的结构来进行跳转。每个外部函数在PLT中都有一个相应的项。

    ELF将GOT拆分成了两个表叫做”.got”和”.got.plt”。其中”.got”用来保存全局变量引用的地址,”.got.plt”用来保存函数引用的地址,也就是说,所有对于外部函数的引用全部被分离出来放到了”.got.plt”中。另外”.got.plt”还有一个特殊的地方是它的前三项是有特殊意义的,分别含义如下:第一项保存的是”.dynamic”段的地址,这个段描述了本模块动态链接相关的信息;第二项保存的是本模块的ID;第三项保存的是_dl_runtime_resolve()的地址。其中第二项和第三项由动态链接器在装载共享模块的时候负责将它们初始化。”.got.plt”的其余项分别对应每个外部函数的引用。PLT在ELF文件中以独立的段存放,段名通常叫做”.plt”,因为它本身是一些地址无关的代码,所以可以跟代码段等一起合并成同一个可读可执行的”Segment”被装载入内存。

    5. 动态链接相关结构

    动态链接在不同的系统上有不同的实现方式,ELF的动态链接实现方式比PE稍微简单一点。动态链接情况下,可执行文件的装载与静态链接情况基本一样。首先操作系统会读取可执行文件的头部,检查文件的合法性,然后从头部中的”Program Header”中读取每个”Segment”的虚拟地址、文件地址和属性,并将它们映射到进程虚拟空间的相应位置,这些步骤跟前面的静态链接情况下的装载基本无异。在静态链接情况下,操作系统接着就可以把控制权转交给可执行文件的入口地址,然后程序开始执行,一切看起来非常直观。但是在动态链接情况下,操作系统还不能在装载完可执行文件之后就把控制权交给可执行文件,因为我们知道可执行文件依赖于很多共享对象。这时候,可执行文件里对于很多外部符号的引用还处于无效地址的状态,即还没有跟相应的共享对象中的实际位置链接起来。所以在映射完可执行文件之后,操作系统会先启动一个动态链接器(Dynamic Linker)

    在Linux下,动态链接器ld.so实际上是一个共享对象,操作系统同样通过映射的方式将它加载到进程的地址空间中。操作系统在加载完动态链接器之后,就将控制权交给动态链接器的入口地址(与可执行文件一样,共享对象也有入口地址)。当动态链接器得到控制权之后,它开始执行一系列自身的初始化操作,然后根据当前的环境参数,开始对可执行文件进行动态链接工作。当所有动态链接工作完成以后,动态链接器会将控制权转交给可执行文件的入口地址,程序开始正式执行。

    “.interp”:动态链接器的位置既不是由系统配置指定,也不是由环境参数决定,而是由ELF可执行文件决定。在动态链接的ELF可执行文件中,有一个专门的段叫做”.interp”段(“interp”是”interpreter”(解释器)的缩写)。如果我们使用objdump工具来查看,可以看到”.interp”内容,如下图所示。”.interp”的内容很简单,里面保存的就是一个字符串,这个字符串就是可执行文件所需要的动态链接器的路径,Linux下,可执行文件所需要的动态链接器的路径几乎都是”/lib64/ld-linux-x86-64.so.2”,其它的*nix操作系统可能会有不同的路径。在Linux的系统中,/lib64/ld-linux-x86-64.so.2通常是一个软链接,比如在我的机器上,它指向/lib/x86_64-gnu/ld-2.19.so,这个才是真正的动态链接器。在Linux中,操作系统在对可执行文件进行加载的时候,它会去寻找装载该可执行文件所需要相应的动态链接器,即”.interp”段指定的路径的共享对象。

    动态链接器在Linux下是Glibc的一部分,也就是属于系统库级别的,它的版本号往往跟系统中的Glibc库版本号是一样的。当系统中的Glibc库更新或者安装其它版本的时候,/lib64/ld-linux-x86-64.so.2这个软链接就会指向到新的动态链接器,而可执行文件本身不需要修改”.interp”中的动态链接器路径来适应系统的升级。

    我们也可以使用”readelf -l Program1 | grep interpreter”命令来查看一个可执行文件所需要的动态链接器的路径。

    “.dynamic”:动态链接ELF中最重要的结构应该是”.dynamic”段,这个段里面保存了动态链接器所需要的基本信息,比如依赖于哪些共享对象、动态链接符号表的位置、动态链接重定位表的位置、共享对象初始化代码的地址等。”.dynamic”段的结构Elf64_Dyn或Elf32_Dyn定义在/usr/include/elf.h文件中,如下:

    /* Dynamic section entry.  */
    typedef struct {
      Elf32_Sword   d_tag;                  /* Dynamic entry type */
      union {
          Elf32_Word d_val;                 /* Integer value */
          Elf32_Addr d_ptr;                 /* Address value */
        } d_un;
    } Elf32_Dyn;
    
    typedef struct {
      Elf64_Sxword  d_tag;                  /* Dynamic entry type */
      union {
          Elf64_Xword d_val;                /* Integer value */
          Elf64_Addr d_ptr;                 /* Address value */
        } d_un;
    } Elf64_Dyn;

    Elf64_Dyn或Elf32_Dyn结构由一个类型值加上一个附加的数值或指针,对于不同的类型,后面附加的数值或者指针有着不同的含义。这里列举几个比较常见的类型值(这些值都是定义在”elf.h”里面的宏),如下图所示:

    使用readelf工具可以查看”.dynamic”段的内容,另外,Linux还提供了一个ldd命令用来查看程序主模块或一个共享库依赖于哪些共享库,如下图所示:可以看到有个linux-vdso.so.1的共享对象很特殊,你在整个文件系统中都搜索不到这个文件,因为它根本不存在于文件系统中,它实际上是一个内核虚拟共享对象(Kernel Virtual DSO)。

    动态符号表:在静态链接中,有一个专门的段叫做符号表”.symtab”(Symbol Table),里面保存了所有关于该目标文件的符号的定义和引用。动态链接的符号表示实际上它跟静态链接十分相似。比如前面例子中的Program1程序依赖于Lib.so,引用到了里面的foobar()函数。那么对于Program1来说,我们往往称Program1导入(Import)了foobar函数,foobar是Program1的导入函数(Import Function);而站在Lib.so的角度来看,它实际上定义了foobar()函数,并且提供给其它模块使用,我们往往称Lib.so导出(Export)了foobar()函数,foobar是Lib.so的导出函数(Export Function)。把这种导入导出关系放到静态链接的情形下,我们可以把它们看作普通的函数定义和引用。为了表示动态链接这些模块之间的符号导入导出关系,ELF专门有一个叫做动态符号表(Dynamic Symbol Table)的段来保存这些信息,这个段的段名通常叫做”.dynsym”(Dynamic Symbol)。与”.symtab”不同的是,”.dynsym”只保存了与动态链接相关的符号,对于那些模块内部的符号,比如模块私有变量则不保存。很多时候动态链接的模块同时拥有”.dynsym”和”.symtab”两个表,”.symtab”中往往保存了所有符号,包括”.dynsym”中的符号。

     与”.symtab”类似,动态符号表也需要一些辅助的表,比如用于保存符号名的字符串表。静态链接时叫做符号字符串表”.strtab”(String Table),在这里就是动态符号字符串表”.dynstr”(String Table),在这里就是动态符号字符串表”.dynstr”(Dynamic String Table);由于动态链接下,我们需要在程序运行时查找符号,为了加快符号的查找过程,往往还有辅助的符号哈希表(“.hash”)。我们可以用readelf工具来查看ELF文件的动态符号表及它的哈希表,如下图所示:

    动态链接符号表的结构与静态链接的符号表几乎一样,我们可以简单地将导入函数看作是对其它目标文件中函数的引用;把导出函数看作是在本目标文件定义的函数就可以了

    动态链接重定位表共享对象需要重定位的主要原因是导入符号的存在。动态链接下,无论是可执行文件或共享对象,一旦它依赖于其它共享对象,也就是说有导入的符号时,那么它的代码或数据中就会有对于导入符号的引用在编译时这些导入符号的地址未知,在静态链接中,这些未知的地址引用在最终链接时被修正。但是在动态链接中,导入符号的地址在运行时才确定,所以需要在运行时将这些导入符号的引用修正,即需要重定位。动态链接的可执行文件使用的是PIC方法,但这不能改变它需要重定位的本质。对于动态链接来说,如果一个共享对象不是以PIC模式编译的,那么毫无疑问,它是需要在装载时被重定位的;如果一个共享对象是PIC模式编译的,也需要重定位。对于使用PIC技术的可执行文件或共享对象来说,虽然它们的代码段不需要重定位(因为地址无关),但是数据段还包含了绝对地址的引用,因为代码段中绝对地址相关的部分被分离了出来,变成了GOT,而GOT实际上是数据段的一部分。除了GOT以外,数据段还可能包含绝对地址引用。

    动态链接重定位相关结构:共享对象的重定位与前面”静态链接”中分析的目标文件的重定位是否类似,唯一有区别的是目标文件的重定位是在静态链接时完成的,而共享对象的重定位是在装载时完成的。在静态链接中,目标文件里面包含有专门用于表示重定位信息的重定位表,比如”.rel.text”表示是代码段的重定位表,”.rel.data”是数据段的重定位表。动态链接的文件中,也有类似的重定位表分别叫做”.rel.dyn”(“.rela.dyn”)和”.rel.plt”(“.rela.plt”),它们分别相当于”.rel.text”和”.rel.data”。”.rel.dyn”实际上是对数据引用的修正,它所修正的位置位于”.got”以及数据段;而”.rel.plt”是对函数引用的修正,它所修正的位置位于”.got.plt”。我们可以使用readelf来查看一个动态链接的文件的重定位表,如下图所示:

    动态链接时进程堆栈初始化信息:站在动态链接器的角度看,当操作系统把控制权交给它的时候,它将开始做链接工作,那么至少它需要知道关于可执行文件和本进程的一些信息,比如可执行文件有几个段(“Segment”)、每个段的属性、程序的入口地址(因为动态链接器到时候需要把控制权交给可执行文件)等。这些信息往往由操作系统传递给动态链接器,保存在进程的堆栈里面。进程初始化的时候,堆栈里面保存了关于进程执行环境和命令行参数等信息。事实上,堆栈里面还保存了动态链接器所需要的一些辅助信息数组(Auxiliary Vector)。辅助信息的格式也是一个结构数组,它的结构定义在” /usr/include/elf.h”:

    /* Auxiliary vector.  */
    
    /* This vector is normally only used by the program interpreter.  The
       usual definition in an ABI supplement uses the name auxv_t.  The
       vector is not usually defined in a standard <elf.h> file, but it
       can't hurt.  We rename it to avoid conflicts.  The sizes of these
       types are an arrangement between the exec server and the program
       interpreter, so we don't fully specify them here.  */
    
    typedef struct {
      uint32_t a_type;              /* Entry type */
      union {
          uint32_t a_val;           /* Integer value */
          /* We use to have pointer elements added here.  We cannot do that,
             though, since it does not work when using 32-bit definitions
             on 64-bit platforms and vice versa.  */
        } a_un;
    } Elf32_auxv_t;
    
    typedef struct {
      uint64_t a_type;              /* Entry type */
      union {
          uint64_t a_val;           /* Integer value */
          /* We use to have pointer elements added here.  We cannot do that,
             though, since it does not work when using 32-bit definitions
             on 64-bit platforms and vice versa.  */
        } a_un;
    } Elf64_auxv_t;

    跟前面的”.dynamic”段里面的结构如出一辙。先是一个32位/64位的类型值,后面是一个32位/64位的数值部分。几个比较重要的类型值,如下图所示:

    6. 动态链接的步骤和实现

    动态链接的步骤基本上分为3步:先是启动动态链接器本身,然后装载所有需要的共享对象,最后是重定位和初始化

    动态链接器自举:动态链接器本身也是一个共享对象,但是事实上它有一些特殊性。对于普通共享对象文件来说,它的重定位工作由动态链接器来完成;它也可以依赖于其它共享对象,其中的被依赖的共享对象由动态链接器负责链接和装载。动态链接器的特殊性:首先是,动态链接器本身不可以依赖于其它任何共享对象;其次是动态链接器本身所需要的全局和静态变量的重定位工作由它本身完成。对于第一个条件我们可以人为地控制,在编写动态链接器时保证不使用任何系统库、运行库;对于第二个条件,动态链接器必须在启动时有一段非常精巧的代码可以完成这项工作而同时又不能用到全局和静态变量。这种具有一定限制条件的启动代码往往被称为自举(Bootstrap)。

    动态链接器入口地址即是自举代码的入口,当操作系统将进程控制权交给动态链接器时,动态链接器的自举代码即开始执行。自举代码首先会找到它自己的GOT。而GOT的第一个入口保存的即是”.dynamic”段的偏移地址,由此找到了动态链接器本身的”.dynamic”段。通过”.dynamic”中的信息,自举代码便可以获得动态链接器本身的重定位表和符号表等,从而得到动态链接器本身的重定位入口,先将它们全部重定位。从这一步开始,动态链接器代码中才可以开始使用自己的全局变量和静态变量。实际上在动态链接器的自举代码中,除了不可以使用全局变量和静态变量之外,甚至不能调用函数,即动态链接器本身的函数也不能调用。

     装载共享对象完成基本自举以后,动态链接器将可执行文件和链接器本身的符号表都合并到一个符号表当中,我们可以称它为全局符号表(Global Symbol Table)。然后链接器开始寻找可执行文件所依赖的共享对象,”.dynamic”段中,有一种类型的入口是DT_NEEDED,它所指出的是该可执行文件(或共享对象)所依赖的共享对象。由此,链接器可以列出可执行文件所需要的所有共享对象,并将这些共享对象的名字放入到一个装载集合中。然后链接器开始从集合里取一个所需要的共享对象的名字,找到相应的文件后打开该文件,读取相应的ELF文件头和”.dynamic”段,然后将它相应的代码段和数据段映射到进程空间中。如果这个ELF共享对象还依赖于其它共享对象,那么将所依赖的共享对象的名字放到装载集合中。如此循环直到所有依赖的共享对象都被装载进来为止,当然链接器可以有不同的装载顺序,如果我们把依赖关系看做一个图的话,那么这个装载过程就是一个图的遍历过程,链接器可能会使用深度优先或者广度优先或者其它的顺序来遍历整个图,这取决于链接器,比较常见的算法一般都是广度优先的。当一个新的共享对象被装载进来的时候,它的符号表会被合并到全局符号表中,所以当所有的共享对象都被装载进来的时候,全局符号表里面将包含进程中所有的动态链接所需要的符号。

    符号的优先级:一个共享对象里面的全局符号被另一个共享对象的同名全局符号覆盖的现象又被称为共享对象的全局符号介入(Global Symbol Interpose)。关于全局符号介入这个问题,实际上Linux下的动态链接器是这样处理的:它定义了一个规则,那就是当一个符号需要被加入全局符号表时,如果相同的符号名已经存在,则后加入的符号被忽略。由于存在这种重名符号被直接忽略的问题,当程序使用大量共享对象时应该非常小心符号的重名问题,如果两个符号重名又执行不同的功能,那么程序运行时可能会将所有该符号名的引用解析到第一个被加入全局符号表的使用该符号名的符号,从而导致程序莫名其妙的错误。

    重定位和初始化:当所有依赖的共享对象被装载进来以后,链接器开始重新遍历可执行文件和每个共享对象的重定位表,将它们的GOT/PLT中的每个须要重定位的位置进行修正。因为此时动态链接器已经拥有了进程的全局符号表,所以这个修正过程也显得比较容易,跟前面提到的地址重定位的原理基本相同。重定位完成之后,如果某个共享对象有”.init”段,那么动态链接器会执行”.init”段中的代码,用以实现共享对象特有的初始化过程,比如最常见的,共享对象中的C++的全局/静态对象的构造就需要通过”.init”来初始化。相应地,共享对象中还可能有”.finit”段,当进程退出时会执行”.finit”段中的代码,可以用来实现类似C++全局对象析构之类的操作。如果进行的可执行文件也有”.init”段,那么动态链接器不会执行它,因为可执行文件中的”.init”段和”.finit”段由程序初始化部分代码负责执行。当完成了重定位和初始化之后,所有的准备工作就宣告完成了,所需要的共享对象也都已经装载并且链接完成了,这时候动态链接器就如释重负,将进程的控制权转交给程序的入口并且开始执行。

    Linux动态链接器实现:内核在装载完ELF可执行文件以后就返回到用户空间,将控制权交给程序的入口。对于不同链接形式的ELF可执行文件,这个程序的入口是有区别的。对于静态链接的可执行文件来说,程序的入口就是ELF文件头里面的e_entry指定的入口;对于动态链接的可执行文件来说,如果这时候把控制权交给e_entry指定的入口地址,那么肯定是不行的,因为可执行文件所依赖的共享库还没有被装载,也没有进行动态链接。所以对于动态链接的可执行文件,内核会分析它的动态链接器地址(在”.interp”段),将动态链接器映射至进程地址空间,然后把控制权交给动态链接器。

    在Linux下,可执行文件所需要的动态链接器的路径几乎都是”/lib64/ld-linux-x86-64.so.2”,其它的*nix操作系统可能会有不同的路径。在Linux的系统中,/lib64/ld-linux-x86-64.so.2通常是一个软链接。动态链接器是个非常特殊的共享对象,它不仅是个共享对象,还是个可执行的程序,可以直接在命令行下面运行共享库和可执行文件实际上没什么区别,除了文件头的标志位和扩展名有所不同之外,其它都是一样的。Windows系统中的EXE和DLL也是类似的区别,DLL也可以被当作程序来运行,Windows提供了一个叫做rundll32.exe的工具可以把一个DLL当作可执行文件运行。

    动态链接器本身应该是静态链接的,它不能依赖于其它共享对象,动态链接器本身是用来帮助其它ELF文件解决共享对象依赖问题的,如果它也依赖于其它共享对象,那么谁来帮它解决依赖问题?所以它本身必须不依赖于其它共享对象。这一点可以使用ldd来判断,如下图所示:

    是不是PIC对于动态链接器来说并不关键,动态链接器可以是PIC的也可以不是,但往往使用PIC会更加简单一些。一方面,如果不是PIC的话,会使得代码段无法共享,浪费内存;另一方面也会使/lib64/ld-linux-x86-64.so.2本身初始化更加复杂,因为自举时还需要对代码段进行重定位。实际上的/lib64/ld-linux-x86-64.so.2是PIC的。

    /lib64/ld-linux-x86-64.so.2的装载地址跟一般的共享对象没区别,即为0x000000000。这个装载地址是一个无效的装载地址,作为一个共享库,内核在装载它是会为其选择一个合适的装载地址。

    7. 显式运行时链接

    支持动态链接的系统往往都支持一种更加灵活的模块加载方式,叫做显式运行时链接(Explicit Run-time Linking),有时候也叫做运行时加载。也就是让程序自己在运行时控制加载指定的模块,并且可以在不需要该模块时将其卸载。如果动态链接器可以在运行时将共享模块装载进内存并且可以进行重定位等操作,那么这种运行时加载在理论上也是很容易实现的。而且一般的共享对象不需要进行任何修改就可以进行运行时加载,这种共享对象往往被叫做动态装载库(Dynamic Loading Library),其实本质上它跟一般的共享对象没什么区别,只是程序开发者使用它的角度不同。这种运行时加载使得程序的模块组织变得很灵活,可以用来实现一些诸如插件、驱动等功能。当程序需要用到某个插件或者驱动的时候,才将相应的模块加载进来,而不需要从一开始就将它们全部加载进来,从而减少了程序启动时间和内存使用。并且程序可以在运行的时候重新加载某个模块,这样使得程序本身不必重新启动而实现模块的增加、删除、更新等,这对于很多需要长期运行的程序来说是很大的优势。

    在Linux中,从文件本身的格式上来看,动态库实际上跟一般的共享对象没有区别。主要的区别是共享对象是由动态链接器在程序启动之前负责装载和链接的,这一系列步骤都由动态链接器自动完成,对于程序本身是透明的;而动态库的装载则是通过一系列由动态链接器提供的API,具体地讲共有4个函数:打开动态库(dlopen)、查找符号(dlsym)、错误处理(dlerror)以及关闭动态库(dlclose),程序可以通过这几个API对动态库进行操作。这几个API的实现实在/lib/x86_64-linux-gnu/libdl.so.2里面,它们的声明和相关常量被定义在系统标准头文件/usr/include/dlfcn.h,这几个函数的声明如下:

    /* Open the shared object FILE and map it in; return a handle that can be
       passed to `dlsym' to get symbol values from it.  */
    void *dlopen (const char *__file, int __mode);
    /* Find the run-time address in the shared object HANDLE refers to
       of the symbol called NAME.  */
    void *dlsym (void * __handle, const char * __name);
    /* When any of the above functions fails, call this function
       to return a string describing the error.  Each call resets
       the error string so that a following call returns null.  */
    char *dlerror (void);
    /* Unmap and close a shared object opened by `dlopen'.
       The handle cannot be used again after calling `dlclose'.  */
    int dlclose (void *__handle);

    dlopen():此函数用来打开一个动态库,并将其加载到进程的地址空间,完成初始化过程。第一个参数是被加载动态库的路径,如果这个路径是绝对路径(以”/”开始的路径),则该函数将会尝试直接打开该动态库;如果是相对路径,那么该函数会尝试在以一定的顺序去查找该动态库文件:(1). 查找有环境变量LD_LIBRARY_PATH指定的一系列目录;(2). 查找由/etc/ld.so.cache里面所指定的共享库路径;(3). /lib、/usr/lib,注意:这个查找顺序与旧的a.out装载器的顺序刚好相反,旧的a.out的装载器在装载共享库的时候会先查找/usr/lib,然后是/lib。如果我们将__file这个参数设置为0,那么dlopen返回的将是全局符号表的句柄,也就是说我们可以在运行时找到全局符号表里面的任何一个符号,并且可以执行它们,这有些类似高级语言反射(Reflection)的特性。全局符号表包括了程序的可执行文件本身、被动态链接器加载到进程中的所有共享模块以及在运行时通过dlopen打开并且使用了RTLD_GLOBAL方式的模块中的符号。

    第二个参数__mode表示函数符号的解析方式,常量RTLD_LAZY表示使用延迟绑定,当函数第一次被用到时才进行绑定,即PLT机制;而RTLD_NOW表示当模块被加载时即完成所有的函数绑定工作,如果有任何未定义的符号引用的绑定工作没法完成,那么dlopen就返回错误。上面的两种绑定方式必须选其一。另外还有一个常量RTLD_GLOBAL可以跟上面的两者中任意一个一起使用(通过常量的”或”操纵),它表示将被加载的模块的全局符号合并到进程的全局符号表中,使得以后加载的模块可以使用这些符号。在调试程序的时候我们可以使用RTLD_NOW作为加载参数,因为如果模块加载时有任何符号未被绑定的话,我们可以使用dlerror立即捕获到相应的错误信息;而如果使用RTLD_LAZY的话,这种符号未绑定的错误会在加载后发生,则难以捕获。当然,使用RTLD_NOW会导致加载动态库的速度变慢。

    dlopen的返回值是被加载的模块的句柄,这个句柄在后面使用dlsym或者dlclose时需要用到。如果加载模块失败,则返回NULL。如果模块已经通过dlopen被加载过了,那么返回的是同一个句柄。另外如果被加载的模块之间有依赖关系,比如模块A依赖于模块B,那么程序员需要手工加载被依赖的模块,比如先加载B,再加载A。

    事实上dlopen还会在加载模块时执行模块中初始化部分的代码。动态链接器在加载模块时,会执行”.init”段的代码,用以完成模块的初始化工作,dlopen的加载过程基本跟动态链接器一致,在完成装载、映射和重定位以后,就会执行”.init”段的代码,然后返回。

    dlsym():此函数基本上是运行时加载的核心部分,我们可以通过这个函数找到所需要的符号。第一个参数是由dlopen返回的动态库的句柄;第二个参数即所要查找的符号的名字,一个以”\0”结尾的C字符串。如果dlsym找到了相应的符号,则返回该符号的值;没有找到相应的符号,则返回NULL。dlsym返回的值对于不同类型的符号,意义是不同的。如果查找的符号是个函数,那么它返回函数的地址;如果是个变量,它返回变量的值;如果这个符号是个常量,那么它返回的是该常量的值。如果符号找到了,那么dlerror返回NULL,如果没找到,dlerror就会返回相应的错误信息。

    符号不仅仅是函数和变量,有时还是常量,比如表示编译单元文件名的符号等,这一般由编译器和链接器产生,而且对外不可见,但它们的确存在于模块的符号表中。dlsym是可以查找到这些符号的,我们也可以通过”objdump -t”来查看符号表,常量在符号表里面的类型是”*ABS*”。

    符号优先级:动态链接实现时,共享模块中符号名冲突时,先装入的符号优先,我们把这种优先级方式称为装载序列(Load Ordering)。那么当我们的进程中有模块是通过dlopen装入的共享对象时,这些后装入的模块中的符号可能会跟先前已经装入的模块之间的符号重复时,也都是采用装载序列。当使用dlsym进行符号的地址查找工作时,这个函数也是按照装载序列的优先级进行符号的查找。

    dlerror():每次我们调用dlopen、dlsym、dlclose以后,我们都可以调用dlerror函数来判断上一次调用是否成功。dlerror的返回值类型是char*,如果返回NULL,则表示上一次调用成功;如果不是,则返回相应的错误信息。

    dlclose():此函数的作用跟dlopen刚好相反,它的作用是将一个已经加载的模块卸载。系统会维持一个加载引用计数器,每次使用dlopen加载某模块时,相应的计数器加一;每次使用dlclose卸载某模块时,相应计数器减一。只有当计数器减到0时,模块才被真正地卸载掉。卸载的过程跟加载刚好相反,先执行”.finit”段的代码,然后将相应的符号从符号表中去除,取消进程空间跟模块的映射关系,然后关闭模块文件。

    GitHubhttps://github.com/fengbingchun/Messy_Test

    展开全文
  • 程序员必知的 89 个操作系统核心概念

    万次阅读 多人点赞 2020-03-31 19:13:39
    系统调用(system call): 指运行在用户空间的程序向操作系统内核请求需要更高权限运行的服务。系统调用提供用户程序与操作系统之间的接口。大多数系统交互式操作需求在内核态运行。如设备 IO 操作或者进程间通信。 ...
    1. 操作系统(Operating System,OS):是管理计算机硬件与软件资源的系统软件,同时也是计算机系统的内核与基石。操作系统需要处理管理与配置内存、决定系统资源供需的优先次序、控制输入与输出设备、操作网络与管理文件系统等基本事务。操作系统也提供一个让用户与系统交互的操作界面。

    1. shell:它是一个程序,可从键盘获取命令并将其提供给操作系统以执行。 在过去,它是类似 Unix 的系统上唯一可用的用户界面。 如今,除了命令行界面(CLI)外,我们还具有图形用户界面(GUI)。

    1. GUI (Graphical User Interface):是一种用户界面,允许用户通过图形图标和音频指示符与电子设备进行交互。

    1. 内核模式(kernel mode): 通常也被称为 超级模式(supervisor mode),在内核模式下,正在执行的代码具有对底层硬件的完整且不受限制的访问。 它可以执行任何 CPU 指令并引用任何内存地址。 内核模式通常保留给操作系统的最低级别,最受信任的功能。 内核模式下的崩溃是灾难性的; 他们将停止整个计算机。 超级用户模式是计算机开机时选择的自动模式。

    2. 用户模式(user node):当操作系统运行用户应用程序(例如处理文本编辑器)时,系统处于用户模式。 当应用程序请求操作系统的帮助或发生中断或系统调用时,就会发生从用户模式到内核模式的转换。在用户模式下,模式位设置为1。 从用户模式切换到内核模式时,它从1更改为0。

    3. 计算机架构(computer architecture) : 在计算机工程中,计算机体系结构是描述计算机系统功能,组织和实现的一组规则和方法。它主要包括指令集、内存管理、I/O 和总线结构

    1. SATA(Serial ATA):串行 ATA (Serial Advanced Technology Attachment),它是一种电脑总线,负责主板和大容量存储设备(如硬盘及光盘驱动器)之间的数据传输,主要用于个人电脑。

    2. 复用(multiplexing):也称为共享,在操作系统中主要指示了时间和空间的管理。对资源进行复用时,不同的程序或用户轮流使用它。 他们中的第一个开始使用资源,然后再使用另一个,依此类推。

    3. 大型机(mainframes):大型机是一类计算机,通常以其大尺寸,存储量,处理能力和高度的可靠性而著称。它们主要由大型组织用于需要大量数据处理的关键任务应用程序。

    1. 批处理(batch system): 批处理操作系统的用户不直接与计算机进行交互。 每个用户都在打孔卡等脱机设备上准备工作,并将其提交给计算机操作员。 为了加快处理速度,将具有类似需求的作业一起批处理并成组运行。 程序员将程序留给操作员,然后操作员将具有类似要求的程序分批处理。

    2. OS/360: OS/360,正式称为IBM System / 360操作系统,是由 IBM 为 1964 年发布的其当时新的System/360 大型机开发的已停产的批处理操作系统。

    3. 多处理系统(Computer multitasking):是指计算机同时运行多个程序的能力。多任务的一般方法是运行第一个程序的一段代码,保存工作环境;再运行第二个程序的一段代码,保存环境;……恢复第一个程序的工作环境,执行第一个程序的下一段代码。

    4. 分时系统(Time-sharing):在计算中,分时是通过多程序和多任务同时在许多用户之间共享计算资源的一种系统

    5. 相容分时系统(Compatible Time-Sharing System):最早的分时操作系统,由美国麻省理工学院计算机中心设计与实作。

    6. 云计算(cloud computing):云计算是计算机系统资源(尤其是数据存储和计算能力)的按需可用性,而无需用户直接进行主动管理。这个术语通常用于描述 Internet 上可供许多用户使用的数据中心。 如今占主导地位的大型云通常具有从中央服务器分布在多个位置的功能。 如果与用户的连接相对较近,则可以将其指定为边缘服务器。

    1. UNIX 操作系统:UNIX 操作系统,是一个强大的多用户、多任务操作系统,支持多种处理器架构,按照操作系统的分类,属于分时操作系统。

    2. UNIX System V:是 UNIX 操作系统的一个分支。

    3. BSD(Berkeley Software Distribution):UNIX 的衍生系统。

    4. POSIX:可移植操作系统接口,是 IEEE 为要在各种 UNIX 操作系统上运行软件,而定义API的一系列互相关联的标准的总称。

    5. MINIX:Minix,是一个迷你版本的类 UNIX 操作系统。

    6. Linux:终于到了大名鼎鼎的 Linux 操作系统了,太强大了,不予以解释了,大家都懂。

    1. DOS (Disk Operating System):磁盘操作系统(缩写为DOS)是可以使用磁盘存储设备(例如软盘,硬盘驱动器或光盘)的计算机操作系统。

    2. MS-DOS(MicroSoft Disk Operating System) :一个由美国微软公司发展的操作系统,运行在Intel x86个人电脑上。它是DOS操作系统家族中最著名的一个,在Windows 95以前,DOS是IBM PC及兼容机中的最基本配备,而MS-DOS则是个人电脑中最普遍使用的DOS操作系统。

    1. MacOS X,怎能少的了苹果操作系统?macOS 是苹果公司推出的基于图形用户界面操作系统,为 Macintosh 的主操作系统

    1. Windows NT(Windows New Technology):是美国微软公司 1993 年推出的纯 32 位操作系统核心。

    2. Service Pack(SP):是程序的更新、修复和(或)增强的集合,以一个独立的安装包的形式发布。许多公司,如微软或Autodesk,通常在为某一程序而做的修补程序达到一定数量时,就发布一个Service Pack。

    3. 数字版权管理(DRM):他是工具或技术保护措施(TPM)是一组访问控制技术,用于限制对专有硬件和受版权保护的作品的使用。

    4. x86:x86是一整套指令集体系结构,由 Intel 最初基于 Intel 8086 微处理器及其 8088 变体开发。采用内存分段作为解决方案,用于处理比普通 16 位地址可以覆盖的更多内存。32 位是 x86 默认的位数,除此之外,还有一个 x86-64 位,是x86架构的 64 位拓展,向后兼容于 16 位及 32 位的 x86架构。

    5. FreeBSD:FreeBSD 是一个类 UNIX 的操作系统,也是 FreeBSD 项目的发展成果。

    6. X Window System:X 窗口系统(X11,或简称X)是用于位图显示的窗口系统,在类 UNIX 操作系统上很常见。

    1. Gnome:GNOME 是一个完全由自由软件组成的桌面环境。它的目标操作系统是Linux,但是大部分的 BSD 系统亦支持 GNOME。

    1. 网络操作系统(network operating systems):网络操作系统是用于网络设备(如路由器,交换机或防火墙)的专用操作系统。

    1. 分布式网络系统(distributed operating systems):分布式操作系统是在独立,网络,通信和物理上独立计算节点的集合上的软件。 它们处理由多个CPU服务的作业。每个单独的节点都拥有全局集合操作系统的特定软件的一部分。

    1. 程序计数器(Program counter):程序计数器 是一个 CPU 中的寄存器,用于指示计算机在其程序序列中的位置

    2. 堆栈寄存器(stack pointer): 堆栈寄存器是计算机 CPU 中的寄存器,其目的是跟踪调用堆栈

    3. 程序状态字(Program Status Word): 它是由操作系统维护的8个字节(或64位)长的数据的集合。它跟踪系统的当前状态。

    4. 流水线(Pipeline): 在计算世界中,管道是一组串联连接的数据处理元素,其中一个元素的输出是下一个元素的输入。 流水线的元素通常以并行或按时间分割的方式执行。 通常在元素之间插入一定数量的缓冲区存储。

    1. 超标量(superscalar): 超标量 CPU 架构是指在一颗处理器内核中实行了指令级并发的一类并发运算。这种技术能够在相同的CPU主频下实现更高的 CPU 流量。
    2. 系统调用(system call): 指运行在用户空间的程序向操作系统内核请求需要更高权限运行的服务。系统调用提供用户程序与操作系统之间的接口。大多数系统交互式操作需求在内核态运行。如设备 IO 操作或者进程间通信。
    3. 多线程(multithreading):是指从软件或者硬件上实现多个线程并发执行的技术。具有多线程能力的计算机因为有硬件支持而能够在同一时间执行多个线程,进而提升整体处理性能。
    4. CPU 核心(core):它是 CPU 的大脑,它接收指令,并执行计算或运算以满足这些指令。一个 CPU 可以有多个内核。
    5. 图形处理器(Graphics Processing Unit):又称显示核心、视觉处理器、显示芯片或绘图芯片;它是一种专门在个人电脑、工作站、游戏机和一些移动设备(如平板电脑、智能手机等)上运行绘图运算工作的微处理器。

    1. 存储体系结构:顶层的存储器速度最高,但是容量最小,成本非常高,层级结构越向下,其访问效率越慢,容量越大,但是造价也就越便宜。

    1. 高速缓存行(cache lines):其实就是把高速缓存分割成了固定大小的块,其大小是以突发读或者突发写周期的大小为基础的。
    2. 缓存命中(cache hit):当应用程序或软件请求数据时,会首先发生缓存命中。 首先,中央处理单元(CPU)在其最近的内存位置(通常是主缓存)中查找数据。 如果在缓存中找到请求的数据,则将其视为缓存命中。

    1. L1 cache:一级缓存是 CPU 芯片中内置的存储库。 L1缓存也称为主缓存,是计算机中最快的内存,并且最接近处理器。

    2. L2 cache: 二级缓存存储库,内置在 CPU 芯片中,包装在同一模块中,或者建在主板上。 L2 高速缓存提供给 L1 高速缓存,后者提供给处理器。 L2 内存比 L1 内存慢。

    3. L3 cache: 三级缓存内置在主板上或CPU模块内的存储库。 L3 高速缓存为 L2 高速缓存提供数据,其内存通常比 L2 内存慢,但比主内存快。 L3 高速缓存提供给 L2 高速缓存,后者又提供给 L1 高速缓存,后者又提供给处理器。

    4. RAM((Random Access Memory):随机存取存储器,也叫主存,是与 CPU 直接交换数据的内部存储器。它可以随时读写,而且速度很快,通常作为操作系统或其他正在运行中的程序的临时数据存储介质。RAM工作时可以随时从任何一个指定的地址写入(存入)或读出(取出)信息。它与 ROM 的最大区别是数据的易失性,即一旦断电所存储的数据将随之丢失。RAM 在计算机和数字系统中用来暂时存储程序、数据和中间结果。

    5. ROM (Read Only Memory):只读存储器是一种半导体存储器,其特性是一旦存储数据就无法改变或删除,且内容不会因为电源关闭而消失。在电子或电脑系统中,通常用以存储不需经常变更的程序或数据。

    6. EEPROM (Electrically Erasable PROM):电可擦除可编程只读存储器,是一种可以通过电子方式多次复写的半导体存储设备。

    7. 闪存(flash memory): 是一种电子式可清除程序化只读存储器的形式,允许在操作中被多次擦或写的存储器。这种科技主要用于一般性数据存储,以及在电脑与其他数字产品间交换传输数据,如储存卡与U盘。

    8. SSD(Solid State Disks):固态硬盘,是一种主要以闪存作为永久性存储器的电脑存储设备。

    1. 虚拟地址(virtual memory): 虚拟内存是计算机系统内存管理的一种机制。它使得应用程序认为它拥有连续可用的内存(一个连续完整的地址空间),而实际上,它通常是被分隔成多个物理内存碎片,还有部分暂时存储在外部磁盘存储器上,在需要时进行数据交换。与没有使用虚拟内存技术的系统相比,使用这种技术的系统使得大型程序的编写变得更容易,对真正的物理内存(例如RAM)的使用也更有效率。

    2. MMU (Memory Management Unit):内存管理单元,有时称作分页内存管理单元。它是一种负责处理中央处理器(CPU)的内存访问请求的计算机硬件。它的功能包括虚拟地址到物理地址的转换(即虚拟内存管理)、内存保护、中央处理器高速缓存的控制等

    1. context switch:上下文切换,又称环境切换。是一个存储和重建 CPU 状态的机制。要交换 CPU 上的进程时,必需先行存储当前进程的状态,然后再将进程状态读回 CPU 中。

    2. 驱动程序(device driver):设备驱动程序,简称驱动程序(driver),是一个允许高级别电脑软件与硬件交互的程序,这种程序创建了一个硬件与硬件,或硬件与软件沟通的接口,经由主板上的总线或其它沟通子系统与硬件形成连接的机制,这样使得硬件设备上的数据交换成为可能。

    1. 忙等(busy waiting):在软件工程中,忙碌等待也称自旋,是一种以进程反复检查一个条件是否为真的条件,这种机制可能为检查键盘输入或某个锁是否可用。

    2. 中断(Interrupt):通常,在接收到来自外围硬件(相对于中央处理器和内存)的异步信号,或来自软件的同步信号之后,处理器将会进行相应的硬件/软件处理。发出这样的信号称为进行中断请求(interrupt request,IRQ)。硬件中断导致处理器通过一个运行信息切换(context switch)来保存执行状态(以程序计数器和程序状态字等寄存器信息为主);软件中断则通常作为 CPU 指令集中的一个指令,以可编程的方式直接指示这种运行信息切换,并将处理导向一段中断处理代码。中断在计算机多任务处理,尤其是即时系统中尤为有用。

    3. 中断向量(interrupt vector):中断向量位于中断向量表中。中断向量表(IVT)是将中断处理程序列表与中断向量表中的中断请求列表相关联的数据结构。 中断向量表的每个条目(称为中断向量)都是中断处理程序的地址。

    1. DMA (Direct Memory Access):直接内存访问,直接内存访问是计算机科学中的一种内存访问技术。它允许某些电脑内部的硬件子系统(电脑外设),可以独立地直接读写系统内存,而不需中央处理器(CPU)介入处理 。

    2. 总线(Bus):总线(Bus)是指计算机组件间规范化的交换数据的方式,即以一种通用的方式为各组件提供数据传送和控制逻辑。

    3. PCIe (Peripheral Component Interconnect Express):官方简称PCIe,是计算机总线的一个重要分支,它沿用现有的PCI编程概念及信号标准,并且构建了更加高速的串行通信系统标准。

    4. DMI (Direct Media Interface):直接媒体接口,是英特尔专用的总线,用于电脑主板上南桥芯片和北桥芯片之间的连接。

    5. USB(Universal Serial Bus):是连接计算机系统与外部设备的一种串口总线标准,也是一种输入输出接口的技术规范,被广泛地应用于个人电脑和移动设备等信息通讯产品,并扩展至摄影器材、数字电视(机顶盒)、游戏机等其它相关领域。

    1. BIOS(Basic Input Output System):是在通电引导阶段运行硬件初始化,以及为操作系统提供运行时服务的固件。它是开机时运行的第一个软件。

    1. 硬实时系统(hard real-time system):硬实时性意味着你必须绝对在每个截止日期前完成任务。 很少有系统有此要求。 例如核系统,一些医疗应用(例如起搏器),大量国防应用,航空电子设备等。

    2. 软实时系统(soft real-time system):软实时系统可能会错过某些截止日期,但是如果错过太多,最终性能将下降。 一个很好的例子是计算机中的声音系统。

    3. 进程(Process):程序本身只是指令、数据及其组织形式的描述,进程才是程序(那些指令和数据)的真正运行实例。若进程有可能与同一个程序相关系,且每个进程皆可以同步(循序)或异步的方式独立运行。

    4. 地址空间(address space):地址空间是内存中可供程序或进程使用的有效地址范围。 也就是说,它是程序或进程可以访问的内存。 存储器可以是物理的也可以是虚拟的,用于执行指令和存储数据。

    5. 进程表(process table):进程表是操作系统维护的数据结构,该表中的每个条目(通常称为上下文块)均包含有关进程的信息,例如进程名称和状态,优先级,寄存器以及它可能正在等待的信号灯。

    6. 命令行界面(command-line interpreter):是在图形用户界面得到普及之前使用最为广泛的用户界面,它通常不支持鼠标,用户通过键盘输入指令,计算机接收到指令后,予以执行。

    1. 进程间通信(interprocess communication): 指至少两个进程或线程间传送数据或信号的一些技术或方法。
    2. 超级用户(superuser): 也被称为管理员帐户,在计算机操作系统领域中指一种用于进行系统管理的特殊用户,其在系统中的实际名称也因系统而异,如 root、administrator 与supervisor。
    3. 目录(directory): 在计算机或相关设备中,一个目录或文件夹就是一个装有数字文件系统的虚拟容器。在它里面保存着一组文件和其它一些目录。
    4. 路径(path name): 路径是一种电脑文件或目录的名称的通用表现形式,它指向文件系统上的一个唯一位置。
    5. 根目录(root directory):根目录指的就是计算机系统中的顶层目录,比如 Windows 中的 C 盘和 D 盘,Linux 中的 /
    6. 工作目录(Working directory):它是一个计算机用语。用户在操作系统内所在的目录,用户可在此目录之下,用相对文件名访问文件。
    7. 文件描述符(file descriptor): 文件描述符是计算机科学中的一个术语,是一个用于表述指向文件的引用的抽象化概念。
    8. inode:索引节点的缩写,索引节点是 UNIX 系统中包含的信息,其中包含有关每个文件的详细信息,例如节点,所有者,文件,文件位置等。
    9. 共享库(shared library):共享库是一个包含目标代码的文件,执行过程中多个 a.out 文件可能会同时使用该目标代码。
    10. DLLs (Dynamic-Link Libraries):动态链接库,它是微软公司在操作系统中实现共享函数库概念的一种实现方式。这些库函数的扩展名是 .DLL、.OCX(包含ActiveX控制的库)或者.DRV(旧式的系统驱动程序)。
    11. 客户端(clients):客户端是访问服务器提供的服务的计算机硬件或软件。
    12. 服务端(servers): 在计算中,服务器是为其他程序或设备提供功能的计算机程序或设备,称为服务端
    13. 主从架构(client-server): 主从式架构也称客户端/服务器架构、C/S 架构,是一种网络架构,它把客户端与服务器区分开来。每一个客户端软件的实例都可以向一个服务器或应用程序服务器发出请求。有很多不同类型的服务器,例如文件服务器、游戏服务器等。

    1. 虚拟机(Virtual Machines):在计算机科学中的体系结构里,是指一种特殊的软件,可以在计算机平台和终端用户之间创建一种环境,而终端用户则是基于虚拟机这个软件所创建的环境来操作其它软件。

    1. Java 虚拟机(Java virtual Machines):Java虚拟机有自己完善的硬体架构,如处理器、堆栈、寄存器等,还具有相应的指令系统。JVM屏蔽了与具体操作系统平台相关的信息,使得Java程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。
    2. 目标文件(object file):目标文件是包含目标代码的文件,这意味着通常无法直接执行的可重定位格式的机器代码。 目标文件有多种格式,相同的目标代码可以打包在不同的目标文件中。 目标文件也可以像共享库一样工作。
    3. C preprocessor: C 预处理å器是 C 语言、C++ 语言的预处理器。用于在编译器处理程序之前预扫描源代码,完成头文件的包含, 宏扩展, 条件编译, 行控制等操作。

    文章参考:

    https://blog.csdn.net/zhangjg_blog/article/details/20380971

    https://www.techopedia.com/definition/4763/address-space

    https://en.wikipedia.org/wiki/Direct_Media_Interface

    https://en.wikipedia.org/wiki/Bus_(computing)

    https://en.wikipedia.org/wiki/Interrupt_vector_table

    https://en.wikipedia.org/wiki/Busy_waiting

    https://en.wikipedia.org/wiki/Context_switch

    https://en.wikipedia.org/wiki/Read-only_memory

    https://www.techopedia.com/definition/6306/cache-hit

    https://zhuanlan.zhihu.com/p/37749443

    https://en.wikipedia.org/wiki/Pipeline_(computing)

    https://en.wikipedia.org/wiki/Stack_register

    https://en.wikipedia.org/wiki/Distributed_operating_system

    https://en.wikipedia.org/wiki/Time-sharing

    https://zh.wikipedia.org/wiki/UNIX

    https://zh.wikipedia.org/wiki/UNIX_System_V

    https://en.wikipedia.org/wiki/Network_operating_system

    https://zh.wikipedia.org/zh/X86-64

    https://zh.wikipedia.org/zh/X86

    https://en.wikipedia.org/wiki/Cloud_computing

    https://www.techopedia.com/definition/24356/mainframe

    https://zh.wikipedia.org/wiki/SATA

    https://blog.codinghorror.com/understanding-user-and-kernel-mode/

    https://en.wikipedia.org/wiki/Protection_ring

    展开全文
  • 原帖地址: https://www.cnblogs.com/LittleHann/p/4244863.htmlLinux Dynamic Shared Library &amp;&amp; LD Linker目录1. 动态链接的意义 2. 地址无关代码: PIC 3. 延迟版定(PLT Procedure Linkage Table...
  • 高通msm8996平台的ASOC音频路径分析(基于androidN及linux3.1x) tags : msm8996 sound linux android 目录: 前言 0 ASOC音频子系统模型 1 关于高通平台 2 音频数据流视角的音频链路 2.1 音频数据流工作过程...
  • Java类的初始化和清理

    千次阅读 2021-10-18 14:18:51
    1 安全的编程是造成编程代价昂贵的主因之一 两个安全性问题 1.1 初始化 C 语言中很多的 bug 都是因为程序员忘记初始化导致的。尤其是很多类库的使用者知道如何初始化类库组件,甚至当侠客们必须得初始化这些三方...
  • DLL搜索路径和DLL劫持

    2019-04-22 14:19:00
    为什么要把DLL搜索路径(DLL ORDER)和DLL劫持(DLL Hajack)拿到一起讲呢?呵呵,其实没啥深意,仅仅是二者有因果关系而已。可以讲正是因为Windows系统下面DLL的搜索路径存在的漏洞才有了后来的一段时间的DLL劫持...
  • 2020年小程序开发-云开发技术总结

    千次阅读 2020-12-18 18:50:16
    2020年注定是平凡的一年,一场冠状疫情的爆发,让人们突然认识到生命的可贵,人们对生命重新有了新的认识。谱写了太多的悲伤,太多难过,太多的眼泪和辛酸。珍惜当下,敬畏生命,敬畏自然。 下面围绕这些规范写...
  • 高通msm8996平台的ASOC音频路径分析(基于androidN及linux3.1x) tags : msm8996 sound linux android 高通msm8996平台的ASOC音频路径分析(基于androidN及linux3.1x) 前言 0 ASOC音频子系统模型 1 关于高通...
  • 关于DLL搜索路径顺序的一个问题
  • babel@7.x核心概念

    2020-08-16 16:31:24
    但也不绝对,比如前面babel-plugin-transform-es2015-arrow-functions这个包,在babel7里面,还去掉了es2015的字符。这也是babel7中对于plugin做出另外一个变化之一: babel7去掉了plugin包名称里面跟es版本有关的...
  •  系统调用(system call): 指运行在用户空间的程序向操作系统内核请求需要更高权限运行的服务。系统调用提供用户程序与操作系统之间的接口。大多数系统交互式操作需求在内核态运行。如设备 IO 操作或者进程间通信。 ...
  • 这些操作系统的概念,保你没听过!

    千次阅读 多人点赞 2020-02-10 12:40:45
    绝对路径名包含了从根目录到该文件的所有目录清单,它们之间用斜杠分隔符分开,在上面的大学院系文件系统中,文件 CS101 的路径名是 /Faculty/Prof.Brown/Courses/CS101 。最开始的斜杠分隔符代表的是 根目录 / ,...
  • 前一阵子,项目里需要导出一个DLL,但是导出之后输出一直怎么对,改了半天才算改对。。。读了一些DLL教程,感觉之后要把现在的代码导出,应该还要花不少功夫。。。下面教程参照我读的3个教程写成,所以内容比较多...
  • 绝对路径名包含了从根目录到该文件的所有目录清单,它们之间用斜杠分隔符分开,在上面的大学院系文件系统中,文件 CS101 的路径名是 /Faculty/Prof.Brown/Courses/CS101 。最开始的斜杠分隔符代表的是 根目录 / ,...
  • 我写了一本操作系统词典送你了

    千次阅读 多人点赞 2020-05-08 11:10:17
    YndF2T.md.png 硬实时系统(hard real-time system):硬实时性意味着你必须绝对在每个截止日期前完成任务。 很少有系统有此要求。 例如核系统,一些医疗应用(例如起搏器),大量国防应用,航空电子设备等。 软实时...
  • 这样的场路径名转换成的文件所在磁盘地址的结果也可以保存缓存中,以避免重复寻址。另外,当一个 Web 页面(URL) 的地址转换为网络地址(IP 地址)后,这个转换结果也可以缓存起来供将来使用。 在任何缓存系统中,都会...
  • Automated System Call Filtering for Commodity Software 翻译
  • 然而,程序员不会直接和这些硬件打交道,而且每位程序员可能会掌握所有计算机系统的细节,这样我们就不用再编写代码了,所以在硬件的基础之上,计算机安装了一层软件,这层软件能够通过响应...
  • Python的运行速度相较于C,绝对是很慢了。Python的CPython解释器是C语言编写的。   2. 简述解释型和编译型编程语言? 请看上一条   3.Python 的解释器种类以及相关特点? 摘自 廖雪峰 教程 ...
  • ##绝对路径_ flutter_application_path = '/Users/suning/Desktop/flutterdemos/Flutter_OC/flutter_module' eval(File.read(File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')), binding...
  • 的优点在于支持多种指标的 Profiling(StackTrace、CPUBusy、Memory、I/O、Method),且支持将 Profiling 结果通过 Kafka 上报回中心 Server 进行分析,也即支持集群诊断。 基于 JVMTI + GetStackTrace 实现 ...
  • WDM在不同Windows版本上的音频支持

    千次阅读 2019-10-13 09:49:07
    这导致私有APO在UWP上是可用(在之前的配置应用程序上可用)。 从Windows 10版本1703开始,音频模块UWP API允许 配置应用程序和用户模式组件 与 内核和硬件层中的模块 (可通过新 KS 属性集发现 ) 进行通信。音频...
  • From: ... ...理解vim的启动过程对于增强使用vim的信心非常重要,本文...除了CentOS平台的vim使用绝对路径指定/etc/vimrc为其系统配置文件以外,其他的平台及配置文件均依赖于环境变量$VIM和$HOME。 2.2 环境变量的...
  • Flutter基础(三)Dart快速入门

    千次阅读 2019-07-10 01:51:03
    如果对象限于单一类型,可以指定Object或dynamic类型。 Object name = 'liuwangshu' ; 如果定义的变量不会变化,可以使用final或const来代替var,final变量只能设置一次。 final name = '...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 4,128
精华内容 1,651
关键字:

dynamiccall不支持绝对路径么