什么是PDCA模型和SMART原则

什么是 PDCA 模型?PDCA 循环是提高工作质量的理论方法,广泛应用在企业管理和个人工作中。P-D-C-A 这四个字母,分别代表:Plan(计划),Do(行动),Check(检查),Act(处理)。美国的质量管理大师戴明认为,高质量,不是来自基于结果的产品...
继续阅读 »

什么是 PDCA 模型?

PDCA 循环是提高工作质量的理论方法,广泛应用在企业管理和个人工作中。P-D-C-A 这四个字母,分别代表:Plan(计划),Do(行动),Check(检查),Act(处理)。美国的质量管理大师戴明认为,高质量,不是来自基于结果的产品检验,而是来自于基于过程的不断改善。


PDCA循环的四个过程


第一,Plan(计划)
一个 PDCA 循环式的「计划」,一定要有「Who do what by when」,也就是:「谁在什么时间之前做什么」,发给大家,而不是口头布置。

第二,Do(行动)


行动,是最占用时间,也是最重要的部分。因为有了计划,以及基于计划分解的,分配到每个人任务栏里的,有时间限制的具体任务,执行就变得责任明确、优先级清晰。


第三,Check(检查)
每一件交代出去的任务,就像一个扔出去的回行镖,最终必须回到你的手上。这是 PDCA 的关键。

第四,Act(处理)
处理不是行动,而是总结成功经验,制定相应标准,或者把未解决或新出现的问题转入下一个 PDCA 循环。

什么是 SMART 原则?

SMART原则是一套制定目标的原则。S-M-A-R-T这5个字母,代表Specific(具体的),Measurable(可衡量的),Attainable(可实现的),Relevant(相关的),和Time-Based(有时间限制的)。


它的本质,是砍掉模棱两可,砍掉标准争议,砍掉不切实际,砍掉无关目标,砍掉无限拖延,让目标从“一千个人心中的一千个哈姆雷特”,变成同一个。


五个字母的含义


第一,Specific(具体的)
绩效考核要切中特定的工作指标,不能笼统。

第二,Measurable(可衡量的)
绩效指标是数量化或者行为化的,验证这些绩效指标的数据或者信息是可以获得的。

第三,Attainable(可实现的)
绩效指标在付出努力的情况下可以实现,避免设立过高或过低的目标。

第四,Relevant(相关性的)
绩效指标是与工作的其它目标是相关联的;绩效指标是与本职工作相关联的。

第五,Time-bound(有时间限制的)
注重完成绩效指标的特定期限。

收起阅读 »

Python的发展趋势

一、Python发展历史Python是一种计算机程序设计语言。你可能在之前听说过很多编程语言,比如难学的C语言(语法和实现难度),非常流行的JAVA语言(尤其是现在分布式存储和服务),非常有争议的PHP(常见 WordPress 大多网站),前端HTML、Ja...
继续阅读 »

一、Python发展历史

Python是一种计算机程序设计语言。你可能在之前听说过很多编程语言,比如难学的C语言(语法和实现难度),非常流行的JAVA语言(尤其是现在分布式存储和服务),非常有争议的PHP(常见 WordPress 大多网站),前端HTML、JavaScripts、Node.JS、还有最近随着容器风行的Golang等等。那Python是What?


  • 1989年,Python的创始人为吉多·范罗苏姆(Guido van Rossum)。1989年的圣诞节期间,吉多·范罗苏姆为了在阿姆斯特丹打发时间,决心开发一个新的脚本解释程序,作为ABC语言的一种继承。
  • 1991年,第一个Python编译器诞生。它是用C语言实现的,并能够调用C语言的库文件。从一出生,Python已经具有了:类,函数,异常处理,包含表和词典在内的核心数据类型,以及模块为基础的拓展系统。
  • 1992年,Python之父发布了Python的web框架Zope1.
  • Python 1.0 - January 1994 增加了 lambda, map, filter and reduce.
  • Python 2.0 - October 16, 2000,加入了内存回收机制,构成了现在Python语言框架的基础
  • Python 2.4 - November 30, 2004, 同年目前最流行的WEB框架Django 诞生
  • Python 2.5 - September 19, 2006
  • Python 2.6 - October 1, 2008
  • Python 2.7 - July 3, 2010
  • In November 2014, it was announced that Python 2.7 would be supported until 2020, and reaffirmed that there would be no 2.8 release as users were expected to move to Python 3.4+ as soon as possible
  • Python 3.0 - December 3, 2008
  • Python 3.1 - June 27, 2009
  • Python 3.2 - February 20, 2011
  • Python 3.3 - September 29, 2012
  • Python 3.4 - March 16, 2014
  • Python 3.5 - September 13, 2015

最新参考:https://www.python.org/downloads/release


二、Python的前景

最新的TIOBE( https://www.tiobe.com/tiobe-index/ )排行榜,Python赶超JAVA占据第二名了, Python崇尚优美、清晰、简单,是一个优秀并广泛使用的语言。

我们看看17年Python的排名:


由上图17年预测可见,Python整体呈上升趋势,反映出Python应用越来越广泛并且也逐渐得到大家的认知和认可,影响度也越来越大,在国内Python开发招聘的岗位也越来越多,我们来看看2017年100offer统计情况:

从上图我们可以看出Python的人均面邀数为6,整体年薪在34w左右,在职位招聘排行榜前十名,应该还算不错的表现哦。


三、Python的应用领域

Python可以应用于众多领域,如:数据分析、组件集成、网络服务、图像处理、数值计算和科学计算等众多领域。


目前业内几乎所有大中型互联网企业都在使用Python,如:Youtube、Dropbox、BT、Quora(中国知乎)、豆瓣、知乎、Google、Yahoo!、Facebook、NASA、阿里、百度、腾讯、汽车之家、美团等。


目前Python主要的应用领域

  • 云计算: 在云计算领域Python可谓有一席之地, 典型应用OpenStack这个大体量的开源云计算产品就是居于Python开发的。


  • WEB开发: 已有众多大型网站均为Python开发,Youtube, Dropbox, 豆瓣, 知乎等…., Python也有许多Web开发框架,典型WEB框架有Django、Pylons,还有Tornado、Bottle、Flask等。


  • 系统运维: 从国内的趋势来看,掌握一门编程语言已经成为了必然的结果,Python在国内已经成为了首选,不管是做自动化运维还是业务运维现在Python在运维领域已经应用极广。


  • 金融:量化交易,金融分析,在金融工程领域,Python不但在用,且用的最多,而且重要性逐年提高。原因:作为动态语言的Python,语言结构清晰简单,库丰富,成熟稳定,科学计算和统计分析都很牛逼,生产效率远远高于c,c++,java,尤其擅长策略回测


  • 图形GUI: PyQT, WxPython, TkInter, PySide等在图形用户接口领域都有广泛被应用。


哪些公司在用Python

  • 谷歌:Google App Engine 、code.google.com 、Google earth 、谷歌爬虫、Google广告等项目都在大量使用Python开发。


  • CIA: 美国中情局网站就是用Python开发的。


  • NASA: 美国航天局(NASA)大量使用Python进行数据分析和运算。


  • YouTube:世界上最大的视频网站YouTube就是用Python开发的。


  • Dropbox:美国最大的在线云存储网站,全部用Python实现,每天网站处理10亿个文件的上传和下载。


  • Instagram:美国最大的图片分享社交网站,每天超过3千万张照片被分享,全部用python开发。


  • Facebook:大量的基础库均通过Python实现的


  • Redhat: 世界上最流行的Linux发行版本中的yum包管理工具就是用python开发的


  • 豆瓣: 公司几乎所有的业务均是通过Python开发完成的。


  • 知乎: 国内最大的问答社区,通过Python开发(国外Quora)


  • 春雨医生:国内知名的在线医疗网站是用Python开发的


除上面之外,还有搜狐、金山、腾讯、盛大、网易、百度、阿里、淘宝 、土豆、新浪、果壳等公司都在使用Python完成各种各样的任务, 互联网公司广泛使用Python来做的事一般有:自动化运维、自动化测试、大数据分析、爬虫、Web 等


为什么是Python而不是其他语言呢?

C 和 Python、Java、C#等

C语言: 代码编译得到 机器码 ,机器码在处理器上直接执行,每一条指令控制CPU工作


其他语言: 代码编译得到 字节码 ,虚拟机执行字节码并转换成机器码再后在处理器上执行


Python和C Python这门语言是由C开发而来 

对于使用:Python的类库齐全并且使用简洁,如果要实现同样的功能,Python 10行代码可以解决,C可能就需要100行甚至更多.
对于速度:Python的运行速度相较与C,绝逼是慢了


Python 和 Java、C#等

对于使用:Linux原装Python,其他语言没有;以上几门语言都有非常丰富的类库支持


对于速度:Python在速度上可能稍显逊色


Python和PHP相比

Python提供了丰富的数据结构,非常容易和c集成。相比较而言,php集中专注在web上。 php大多只提供了系统api的简单封装,但是python标准包却直接提供了很多实用的工具。python的适用性更为广泛,php在web更加专业,php的简单数据类型,完全是为web量身定做。


所以,Python和其他语言没有什么本质区别,其他区别在于:擅长某领域、人才丰富、先入为主。语言是死的,每个语言的诞生都有它的道理,所以选择你喜欢的,开心的玩起来。

收起阅读 »

yum和apt-get命令对比

说明 Redhat系 Debian系 更新缓存 yum makecache apt-get update 更新包 yum update apt-get upgrade 检索包 yum search apt-cache search 检索包内文件 yum pro...
继续阅读 »






















































说明 Redhat Debian
更新缓存 yum makecache apt-get update
更新包 yum update apt-get upgrade
检索包 yum search apt-cache search
检索包内文件 yum provides apt-file search
安装指定的包 yum install apt-get install
删除指定的包 yum remove apt-get remove
显示指定包的信息 yum info apt-cache show
显示包所在组的一览 yum grouplist -
显示指定包所在组的信息 yum groupinfo -
安装指定的包组 yum groupinstall -
删除指定的包组 yum groupremove -
参考库的设定文件 /etc/yum.repos.d/* /etc/apt/sources.list
安装完的包的列表 rpm -qa dpkg-query -l
显示安装完的指定包的信息 rpm -qi apt-cache show
安装完的指定包内的文件列表 rpm -ql dpkg-query -L
安装完的包的信赖包的列表 rpm -qR apt-cache depends
安装完的文件信赖的包 rpm -qf dpkg -S
收起阅读 »

什么是CICD

传统的应用发布模式如果你经历体验过传统的应用发布,你可能就会觉得CICD有足够吸引你的地方,反之亦然。一般一个研发体系中都会存在多个角色:开发、测试、运维。当时我们的应用发布模式可以能是这样的: 开发团队在开发环境中完成软件开发,单元测试,测试通过,提交到代码...
继续阅读 »

传统的应用发布模式


如果你经历体验过传统的应用发布,你可能就会觉得CICD有足够吸引你的地方,反之亦然。一般一个研发体系中都会存在多个角色:开发、测试、运维。当时我们的应用发布模式可以能是这样的:

  • 开发团队在开发环境中完成软件开发,单元测试,测试通过,提交到代码版本管理库;
  • 开发同学通知运维同学项目可以发布了,然后运维同学下载代码进行打包和构建,生成应用制品;
  • 运维同学使用部署脚本将生成的制品部署到测试环境,并提示测试同学可以进行产品的测试;
  • 测试同学开始进行手动、自动化测试,测试完成后提醒运维同学可以进行预生产环境部署;
  • 运维同学开始进行预生产环境部署,然后测试同学进行测试,测试完成后,开始部署生产环境。

当然我描述的可能只是其中的一部分,手动操作很多、出现的问题很多。上面看似很流畅的过程,其实每次构建或发布都可能会出现问题。未对每次提交验证、构建环境不一致:开发人员本地测试成功后提交代码,运维同学下载代码进行编译却出现了错误。


存在的问题:


  1. 错误发现不及时: 很多错误在项目的早期可能就存在,到最后集成的时候才发现问题;
  2. 人工低级错误发生: 产品和服务交付中的关键活动全都需要手动操作;
  3. 团队工作效率低: 需要等待他人的工作完成后才能进行自己的工作;
  4. 开发运维对立: 开发人员想要快速更新,运维人员追求稳定,各自的针对的方向不同。

经过上述问题我们需要作出改变,如何改变?


什么是CICD


软件开发的连续方法基于自动执行脚本,以最大程度地减少在开发应用程序时引入错误的机会。从开发新代码到部署新代码,他们几乎不需要人工干预,甚至根本不需要干预。

它涉及到在每次小的迭代中就不断地构建,测试和部署代码更改,从而减少了基于错误或失败的先前版本开发新代码的机会。


此方法有三种主要方法,每种方法都将根据最适合您的策略的方式进行应用。


持续集成 CI(Continuous Integration)


在传统软件开发过程中,集成通常发生在每个人都完成了各自的工作之后。在项目尾声阶段,通常集成还要痛苦的花费数周或者数月的时间来完成。持续集成是一个将集成提前至开发周期的早期阶段的实践方式,让构建、测试和集成代码更经常反复地发生。

开发人员通常使用一种叫做CI Server的工具来做构建和集成。持续集成要求史蒂夫和安妮能够自测代码。分别测试各自代码来保证它能够正常工作,这些测试通常被称为单元测试(Unit tests)。


代码集成以后,当所有的单元测试通过,史蒂夫和安妮就得到了一个绿色构建(Green Build)。这表明他们已经成功地集成在一起,代码正按照测试预期地在工作。然而,尽管集成代码能够成功地一起工作了,它仍未为生产做好准备,因为它没有在类似生产的环境中测试和工作。


CI是需要对开发人员每次的代码提交进行构建测试验证。确定每次提交的代码都是可以正常编译测试通过的。在没有持续集成服务器的时候,我们可以写一个程序来监听版本控制系统的状态,当出现了push动作则触发相应的脚本运行编译构建等步骤。现在有了专业的持续集成服务器后,我们借助持续集成服务器来实现版本控制系统中代码提交触发构建测试等验证步骤。


持续合并开发人员正在开发编写的所有代码的一种做法。通常一天内进行多次合并和提交代码,从存储库或生产环境中进行构建和自动化测试,以确保没有集成问题并及早发现任何问题。


开发人员提交代码的时候一般先在本地测试验证,只要开发人员提交代码到版本控制系统就会触发一条提交流水线,对本次提交进行验证。


持续交付 CD (Continuous Delivery)


Continuous Delivery (CD) 持续交付是持续集成的延伸,将集成后的代码部署到类生产环境,确保可以以可持续的方式快速向客户发布新的更改。如果代码没有问题,可以继续手工部署到生产环境中。

持续交付CD:是基于持续集成的基础上,将集成后的代码自动化的发布到各个环境中测试(DEV TEST UAT STAG),确定可以发布生产版本。这里我们可以借用制品库实现制品的管理,根据环境类型创建对应的制品库。一次构建,到处运行


  • 开发环境发布:我们可以将开发环境产出的制品部署进行测试,没有问题后上传到测试环境的制品库中。
  • 测试环境发布:此时通知测试人员可以进行测试环境发布测试,获取测试环境制品库中的制品,发布到测试环境验证。验证通过将制品上传到预生产环境制品库。
  • 预生产环境发布:获取预生产环境制品,进行部署测试。测试成功后可以将制品上传到生产库中。
  • 手动部署生产环境。

持续交付是超越持续集成的一步。不仅会在推送到代码库的每次代码更改时都进行构建和测试,而且,作为附加步骤,即使部署是手动触发的,它也可以连续部署。此方法可确保自动检查代码,但需要人工干预才能从策略上手动触发更改的部署。


持续部署 CD(Continuous Deploy)


如果真的想获得持续交付的好处,应该尽早部署到生产环境,以确保可以小批次发布,在发生问题时可以轻松排除故障。于是有了持续部署。

通常可以通过将更改自动推送到发布系统来随时将软件发布到生产环境中。持续部署 会更进一步,并自动将更改推送到生产中。类似于持续交付,持续部署也是超越持续集成的进一步。不同之处在于,您无需将其手动部署,而是将其设置为自动部署。部署您的应用程序完全不需要人工干预。


持续部署CD:是基于持续交付的基础上,将在各个环境经过测试的应用自动化部署到生产环境。其实各个环境的发布过程都是一样的。应用发布到生产环境后,我们需要对应用进行健康检查、添加应用的监控项、 应用日志管理。


我们通常将这个在不同环境发布和测试的过程叫做部署流水线, 持续部署是在持续交付的基础上,把部署到生产环境的过程自动化。


参考:



https://blog.csdn.net/weixin_40046357/article/details/107478696


http://www.idevops.site/gitlabci/chapter01/01/


https://www.jianshu.com/p/654505d42180


收起阅读 »

最全医疗细分赛道龙头

今天周末,股大夫一直满仓,最近账户增值不错,我敢于满仓持股不动主要得益于我对医疗的三大逻辑思考:人口老龄化,这个话题前面我已经表格说明过,最近七普人口调查数据大家应该也看了。国产替代,我之前也专题研说过,未来国产替代将是大势所趋,无法阻挡的历史洪流。刚需,医疗...
继续阅读 »
今天周末,股大夫一直满仓,最近账户增值不错,我敢于满仓持股不动主要得益于我对医疗的三大逻辑思考:人口老龄化,这个话题前面我已经表格说明过,最近七普人口调查数据大家应该也看了。国产替代,我之前也专题研说过,未来国产替代将是大势所趋,无法阻挡的历史洪流。刚需,医疗作为一种特殊的消费品,中国人口数量仍然世界第一,居民人均可支配收入不断提高,这也是很自然的大趋势客观存在,无法改变。

今天花时间总结下医疗各赛道龙头公司,每个公司的产品都是自己独特的护城河,或者有的公司有自己的祖传秘方,我们投资的目标就是找寻市场最牛的,最有需求的公司,今天我列举出来,每个人的理解不同,估计还不是很全面。

恒瑞医药:肿瘤创新药龙头,医药总龙头

爱尔眼科:眼科医疗服务龙头

迈瑞医疗:医疗器械总龙头

药明康德:医药CXO龙头

长春高新:生长激素龙头


片仔癀:中药龙头

美年健康:体检医疗服务龙头

云南白药:传统中医药龙头

复星医药:创新药龙头老二,医药综合龙头

乐普医疗:心脏支架系统龙头


新和成:维生素龙头

华兰生物:血液制品龙头

智飞生物:疫苗代理销售龙头

人福医药:麻醉药龙头

科伦药业:大输液龙头


康泰生物:乙肝、肺炎疫苗龙头

我武生物:脱敏药龙头

鱼跃医疗:家用医疗器械龙头

健帆生物:血液灌流器械龙头

健友股份:肝素原料药龙头


华大基因:基因检测龙头

汤臣倍健:保健品龙头

欧普康视:角膜塑形镜龙头

三诺生物:血糖监测仪龙头

安图生物:化学发光试剂龙头


南微医学:微创手术器械龙头

大博医疗:骨科龙头

透景生命:体外试剂诊断龙头

通化东宝:糖尿病药龙头之一

甘李药业:糖尿病药龙头之一


山大华特:新生儿补充剂龙头

司太立:    造影剂龙头

戴维医疗:新生儿医疗器械龙头

沃森生物:肺炎疫苗和HPV疫苗龙头

凯利泰:椎体成形微创介入手术、骨科植入、骨科手术器械龙头。


三友医疗:脊椎类植入耗材、创伤类植入耗材龙头。

楚天科技:医药装备龙头。

华海药业:特色原料药龙头。全球最大的普利类和沙坦类药物提供商。

康美药业:国内中药饮品龙头。

卫宁健康:医疗信息龙头。


英科医疗:专精医用手套世界级龙头。

康龙化成:国内第二、全球第三的临床前CRO企业,次于药明康德。

昭衍新药:临床前CRO安全性评价龙头。

凯莱英:小分子化学制药CDMO龙头

药石科技:药物分子砌块领域CRO龙头


泰格医药:专注定位CRO这个细分市场并成为临床CRO行业龙头

通策医疗:国内口腔医疗龙头,从医生+口碑两个方面加深自己的护城河。

国瓷材料:全资子公司爱尔创在国内高端陶瓷牙用纳米氧化锆市占率第一。

爱美客:注射用玻尿酸龙头,聚焦于医美终端产品。公司研发能力很强,多款产品属于国内首款。

华熙生物:玻尿酸原料龙头,是世界最大的透明质酸生产及销售企业。全产业链布局,在医美和化妆品赛道持续发力。


金域医学:国内第三方医学检验龙头企业,行业核心在于数据积累,数据库越大,检验效率、准确率越高。

万孚生物:即时检测龙头,在POCT领域惟精惟一。

艾德生物:聚焦肿瘤检测。

大参林:国内连锁药店的龙头之一,四大药房中毛利率净利润率排名第一,盈利能力较强,稳扎稳打型。

益丰药房:国内连锁药店的龙头之一。


医疗行业是非常复杂的行业,很多公司都具备自己独特的立身之本,很多小公司靠一两个产品都能稳定于市场之中长期活下去,跟随医疗的上面三大逻辑不断壮大。各位朋友,今天的文章,建议大家收藏,有个大体了解就好。

分享阅读原文: https://henduan.com/Flkgz 

收起阅读 »

敏捷为什么要使用Scrum而不是瀑布?

Scrum方法需要改变传统方法的思维方式。中心焦点已经从瀑布方法的范围转变为在Scrum中实现最大的商业价值。 在瀑布中,改变成本和进度以确保达到预期的范围,在Scrum中,可以改变质量和约束以实现获得最大商业价值的主要目标。瀑布模型适用于有序和可预测的项目,...
继续阅读 »

Scrum方法需要改变传统方法的思维方式。中心焦点已经从瀑布方法的范围转变为在Scrum中实现最大的商业价值。


在瀑布中,改变成本和进度以确保达到预期的范围,在Scrum中,可以改变质量和约束以实现获得最大商业价值的主要目标。

瀑布模型适用于有序和可预测的项目,其中所有要求都明确定义并且可以准确估计,并且在大多数行业中,此类项目正在减少。客户需求的变化导致企业适应和改变其交付方式的压力增大。

Scrum方法在当前市场中更为成功,其特点是不可预测性和波动性。Scrum方法基于inspect-adapt循环,而不是Waterfall方法的命令和控制结构。


Scrum项目以迭代方式完成,其中首先完成具有最高业务价值的功能。各个跨职能团队在Sprint中并行工作,以便在每个Sprint结束时提供潜在的可交付解决方案。


因为每次迭代都会产生可交付的解决方案(这是整个产品的一部分),所以团队必须实现可衡量的目标。这可确保团队正在进行,项目将按时完成。传统方法没有提供这种及时的检查,因此导致团队可能会下班并最终完成大量工作。


当客户定期与团队互动时,定期审查完成的工作; 因此,可以确保进度符合客户的要求。然而,在瀑布中没有这样的交互,因为工作是在筒仓中进行的,并且在项目结束之前没有可用的功能。


在复杂的项目中,客户不清楚他们在最终产品中需要什么,并且功能需求不断变化,迭代模型可以更灵活地确保在项目完成之前可以包含这些更改。


但是,当完成具有明确定义的功能的简单项目,并且当团队具有完成此类项目的先前经验(因此,估计将是准确的)时,瀑布方法可以是成功的。


敏捷 Vs 瀑布

下面是一个表格,可以更好地了解Scrum和瀑布的差异。

敏捷还是瀑布?

Standish Group的最新报告涵盖了他们在2013年至2017年期间研究的项目。在这段时间内,敏捷和瀑布的成功,挑战和失败的整体突破如下所示,敏捷项目成功的可能性大约是后者的2倍,失败的可能性降低1/3

来源:vitalitychicago.com - 比较瀑布和敏捷项目成功率


分享阅读: https://henduan.com/Aynya

收起阅读 »

cmake编译程序设置动态链接库加载路径

编译运行的程序需要链接到程序所在路径下的某些个动态库,为方便移植,必须设置链接库的相对路径,比如./lib等等。默认在Linux系统下动态库的搜寻路径如下: 使用选项-Wl,-rpath在编译时指定;通过配置LD_LIBRARY_PATH来指定;在/lib和/...
继续阅读 »

编译运行的程序需要链接到程序所在路径下的某些个动态库,为方便移植,必须设置链接库的相对路径,比如./lib等等。默认在Linux系统下动态库的搜寻路径如下:


  1. 使用选项-Wl,-rpath在编译时指定;
  2. 通过配置LD_LIBRARY_PATH来指定;
  3. /lib/usr/lib中查找;

其中第一个在gcc编译选项中添加:-Wl,rpath=xxx会将rpath路径写入到程序中保存起来。


为了方便移植运行一些编译安装的应用程序,在编译的时候需要设置链接库读取的相对路径目录, 比如../lib 或者./lib


默认在Linux系统下动态库的搜寻路径如下:


  1. 使用选项-Wl,-rpath在编译时指定rpath;
  2. 通过配置LD_LIBRARY_PATH来指定,运行加载;
  3. /lib/usr/lib等系统默认动态库路径中查找。

其中第一个在gcc编译选项中添加:-Wl,rpath=xxx会将rpath路径写入到程序中保存起来。
以下两种方式都可以用来配置rpath路径。

1、使用gcc编译选项:


add_definitions(-std=c++11)
SET(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -g -ggdb -Wl,-rpath=./:./lib") #-Wl,-rpath=./
SET(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -O3 -Wl,-rpath=./:./lib") #-Wall

2、使用cmake配置


set(CMAKE_SKIP_BUILD_RPATH FALSE)
set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
set(CMAKE_INSTALL_RPATH "./lib")

或者


SET(CMAKE_SKIP_BUILD_RPATH FALSE)
SET(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE)
SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib:$ORIGIN/lib")
SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)

其中RPATH可以使用"./lib""./"配置,有可以使用"$ORIGIN/lib""\${ORIGIN}/lib",这里必须加上\符号,否则无法识别。


还可以同时定义多个RPATH,比如:"$ORIGIN:$ORIGIN/lib",中间使用:分割。


参考:https://blog.csdn.net/wh8_2011/article/details/79519293
CMAKE和RPATH:https://blog.csdn.net/zhangzq86/article/details/80718559
CMAKE中RPATH的用法:https://blog.csdn.net/z296671124/article/details/86699720
Linux C编程使用相对路径加载动态库: https://blog.csdn.net/dreamcs/article/details/52138229

收起阅读 »

每个伟大的产品需要一个伟大的ScrumMaster

摘要产品负责人和ScrumMaster是两个相互补充的独立敏捷角色。 为了出色地完成工作,产品所有者需要在他们身边强大的ScrumMaster。 不幸的是,我发现通常缺少可以支持产品所有者的ScrumMaster。 有时角色之间会混淆,或者根本没有Scrum...
继续阅读 »

摘要

产品负责人和ScrumMaster是两个相互补充的独立敏捷角色。 为了出色地完成工作,产品所有者需要在他们身边强大的ScrumMaster


不幸的是,我发现通常缺少可以支持产品所有者的ScrumMaster。 有时角色之间会混淆,或者根本没有ScrumMaster


这篇文章解释了这两个角色之间的区别,产品所有者应该从他们的ScrumMaster中获得什么,以及ScrumMasters从他们中可以期待什么。


产品负责人与ScrumMaster

产品负责人和ScrumMaster是相互补充的两个不同角色。 如果其中一个位置不正确,则另一个会受到影响。 作为产品负责人 ,您应对产品的成功负责-创造一种对用户和客户的工作都非常出色并满足其业务目标的产品。 因此,您可以与用户和客户以及内部利益相关者,开发团队和ScrumMaster进行交互,如下图所示。

上图中的灰色圆圈描述了由产品所有者,ScrumMaster和跨功能开发团队组成的Scrum团队。

ScrumMaster负责流程的成功 -帮助产品负责人和团队使用正确的流程来创建成功的产品,并促进组织变革和建立敏捷的工作方式。 因此,ScrumMaster与产品所有者和开发团队以及受Scrum影响的高级管理层,人力资源(HR)和业务组合作,如下图所示:

要成功成为产品负责人,需要正确的技能,时间,精力和重点。 扮演ScrumMaster角色也是如此。 将这两个角色(甚至是部分角色)组合在一起不仅非常具有挑战性,而且意味着忽略了某些职责。 如果您是产品所有者,请不要承担ScrumMaster的职责!

产品负责人对ScrumMaster的期望

作为产品所有者,您应该从以下几种方面受益于ScrumMaster的工作。 ScrumMaster应该指导团队,以便团队成员可以构建出色的产品,促进组织变革,以便组织利用Scrum并帮助您完成出色的工作:

下表详细说明了您应该从ScrumMaster获得的支持:

ScrumMaster支持您作为产品所有者,因此您可以专注于自己的工作-确保创建具有正确用户体验(UX)和正确功能的正确产品。 如果您的ScrumMaster不提供或无法提供此支持,请与个人联系,并找出问题所在。 不要介入并接管ScrumMaster的工作。 如果您没有ScrumMaster,请将上面的列表显示给您的高级管理层赞助商或老板,以解释为什么您需要身边有合格的ScrumMaster。

ScrumMaster应该对产品负责人有什么期望

Tango花了两个时间,ScrumMaster对您作为产品所有者的工作抱有期望,这是公平的。 下图说明了其中的一些:

下表更详细地描述了ScrumMaster的期望:

英文原文: https://henduan.com/1pjvW

收起阅读 »

Scrum是一个用于开发和维护复杂产品的框架

Scrum是一个用于开发和维护复杂产品的框架 ,是一个增量的、迭代的开发过程。在这个框架中,整个开发过程由若干个短的迭代周期组成,一个短的迭代周期称为一个Sprint,每个Sprint的建议长度是2到4周(互联网产品研发可以使用1周的Sprint)。 在Scr...
继续阅读 »

Scrum是一个用于开发和维护复杂产品的框架 ,是一个增量的、迭代的开发过程。在这个框架中,整个开发过程由若干个短的迭代周期组成,一个短的迭代周期称为一个Sprint,每个Sprint的建议长度是2到4周(互联网产品研发可以使用1周的Sprint)。


在Scrum中,使用产品Backlog来管理产品的需求,产品backlog是一个按照商业价值排序的需求列表,列表条目的体现形式通常为用户故事。Scrum团队总是先开发对客户具有较高价值的需求。在Sprint中,Scrum团队从产品Backlog中挑选最高优先级的需求进行开发。


挑选的需求在Sprint计划会议上经过讨论、分析和估算得到相应的任务列表,我们称它为Sprint backlog。在每个迭代结束时,Scrum团队将递交潜在可交付的产品增量。 Scrum起源于软件开发项目,但它适用于任何复杂的或是创新性的项目。

SCRUM框架包括3个角色、3个工件、5个事件、5个价值

3个角色


  • 产品负责人(Product Owner)
  • Scrum Master
  • 开发团队

3个工件


  • 产品Backlog(Product Backlog)
  • SprintBacklog
  • 产品增量(Increment)

5个事件


  • Sprint(Sprint本身是一个事件,包括了如下4个事件)
  • Sprint计划会议(Sprint Planning Meeting)
  • 每日站会(Daily Scrum Meeting)
  • Sprint评审会议(Sprint Review Meeting)
  • Sprint回顾会议(Sprint Retrospective Meeting)

5个价值


  • 承诺 – 愿意对目标做出承诺
  • 专注– 把你的心思和能力都用到你承诺的工作上去
  • 开放– Scrum 把项目中的一切开放给每个人看
  • 尊重– 每个人都有他独特的背景和经验
  • 勇气– 有勇气做出承诺,履行承诺,接受别人的尊重


SCRUM理论基础

Scrum以经验性过程控制理论(经验主义)做为理论基础的过程。经验主义主张知识源于经验, 以及基于已知的东西做决定。Scrum 采用迭代、增量的方法来优化可预见性并控制风险。


Scrum 的三大支柱支撑起每个经验性过程控制的实现:透明性、检验和适应。Scrum的三大支柱如下:


第一:透明性(Transparency)


透明度是指,在软件开发过程的各个环节保持高度的可见性,影响交付成果的各个方面对于参与交付的所有人、管理生产结果的人保持透明。管理生产成果的人不仅要能够看到过程的这些方面,而且必须理解他们看到的内容。也就是说,当某个人在检验一个过程,并确信某一个任务已经完成时,这个完成必须等同于他们对完成的定义。


第二:检验(Inspection)


开发过程中的各方面必须做到足够频繁地检验,确保能够及时发现过程中的重大偏差。在确定检验频率时,需要考虑到检验会引起所有过程发生变化。当规定的检验频率超出了过程检验所能容许的程度,那么就会出现问题。幸运的是,软件开发并不会出现这种情况。另一个因素就是检验工作成果人员的技能水平和积极性。


第三:适应(Adaptation)


如果检验人员检验的时候发现过程中的一个或多个方面不满足验收标准,并且最终产品是不合格的,那么便需要对过程或是材料进行调整。调整工作必须尽快实施,以减少进一步的偏差。


Scrum中通过三个活动进行检验和适应:每日例会检验Sprint目标的进展,做出调整,从而优化次日的工作价值;Sprint评审和计划会议检验发布目标的进展,做出调整,从而优化下一个Sprint的工作价值;Sprint回顾会议是用来回顾已经完成的Sprint,并且确定做出什么样的改善可以使接下来的Sprint更加高效、更加令人满意,并且工作更快乐。

全文阅读:https://www.scrumcn.com/agile/scrum-knowledge-library/scrum.html

收起阅读 »

了解共享库动态加载

在本文中,我将尝试解释在Linux系统中动态加载共享库的内部工作原理。 这边文章不是一个如何引导,尽管它确实展示了如何编译和调试共享库和可执行文件。为了解动态加载的内部工作方式进行了优化。写这篇文章是为了消除我在该主题上的知识欠缺,以便成为一名更好的程序员。我...
继续阅读 »

在本文中,我将尝试解释在Linux系统中动态加载共享库的内部工作原理。


这边文章不是一个如何引导,尽管它确实展示了如何编译和调试共享库和可执行文件。为了解动态加载的内部工作方式进行了优化。写这篇文章是为了消除我在该主题上的知识欠缺,以便成为一名更好的程序员。我希望它也能帮助您变得更好。


什么是共享库

库是一个包含编译后的代码和数据的文件。一般来说,库非常有用,因为它们可以缩短编译时间(在编译应用程序时不必编译依赖关系的所有源代码)和模块化开发过程。


静态库链接到已编译的可执行文件(或另一个库)中。编译后,新组件将包含静态库的内容。


共享库在运行时由可执行文件(或其他共享库)加载。这让它们变得更加复杂,通常大家对这个领域可能存在认知障碍,我们将在这篇文章中讨论。


示例设置

为了探索共享库的世界,我们将在本文中使用一个示例。我们将从三个源文件开始:


main.cpp是我们定义的可执行文件的主文件, 它不会做太多, 只是从我们将要编译的随机库random调用一个函数:


$ vi main.cpp

#include "random.h"

int main() {
return get_random_number();
}

头文件random.h将定义一个简单的函数:


$ vi random.h

int get_random_number();

它将在其源文件中提供一个简单的实现, random.cpp


$ vi random.cpp

#include "random.h"

int get_random_number(void) {
return 4;
}

Note: 所有示例均在Ubuntu 14.04系统上运行



编译共享库

在编译实际库之前,我们将从random.cpp创建一个目标文件:


$ clang++ -o random.o -c random.cpp

通常,一切正常后,构建工具不会打印到标准输出。以下是所有解释的参数:


  • -o random.o: 将输出文件名定义为random.
  • -c: 不尝试任何链接(只编译)
  • random.cpp: 输入文件

接下来,我们将目标文件编译到共享库中:


$ clang++ -shared -o librandom.so random.o

参数-shared用于指定应该构建共享库的标志。



注意: librandom.so称为共享库。这不是随心所欲的, 呗调用的共享库应该以lib<name>.so使它们以后正确链接(如我们在下面的链接部分中所见)。



编译和链接动态可执行文件

首先,我们将为main.cpp创建一个共享对象:


$ clang++ -o main.o -c main.cpp

与之前完全相同random.o


现在,我们将尝试创建一个可执行文件:


$ clang++ -o main main.o
main.o: In function `main':
main.cpp:(.text+0x10): undefined reference to `get_random_number()'
clang: error: linker command failed with exit code 1 (use -v to see invocation)

好吧,看来我们需要告诉clang我们要使用librandom.so:


$ clang++ -o main main.o -lrandom
/usr/bin/ld: cannot find -lrandom
clang: error: linker command failed with exit code 1 (use -v to see invocation)

注意: 我们选择动态链接librandom.so到main。可以静态地执行此操作-并将random库中的所有符号直接加载到main可执行文件中。



我们告诉编译器我们要使用librandom文件。由于它是动态加载的,为什么我们在编译时需要它?好吧,原因是我们需要确保依赖的库包含可执行文件所需的所有符号。还要注意,我们指定random的是库的名称,而不是librandom.so。还记得关于库文件命名的约定吗?这是使用它的地方。


因此,我们需要让我们clang知道在哪里搜索共享库。我们用-L参数来做到这一点。请注意,由指定的路径-L仅在链接时影响搜索路径,而不会在运行时影响。我们将指定当前目录:


$ clang++ -o main main.o -lrandom -L.

现在它可以运行了,但是:


$ ./main 
./main: error while loading shared libraries: librandom.so: cannot open shared object file: No such file or directory

当找不到依赖项时,这是我们得到的错误。这将在我们的应用程序甚至运行一行代码之前发生,因为共享库是在可执行文件中的符号之前加载的。


到这就需要面对如下几个问题:


  1. main它怎么知道依赖librandom.so?
  2. main在哪里查找librandom.so?
  3. 要这么告诉main在当前目录查找librandom.so?

要回答这些问题,我们将不得不更深入地研究这些文件的结构。


ELF - 可执行和可链接的格式

共享库和可执行文件格式称为ELF(可执行和可链接格式)。如果您查看Wikipedia文章,您会发现它是一团糟,因此我们不会一一列举。总之,ELF文件包含:


  • ELF Header
  • 文件数据,可能包含:
    1. 程序头表(段头列表)
    2. 段头表(列表章节标题)
    3. 以上两个标题指向的数据

ELF标头指定程序标头表中段的大小和数量,以及节标头表中段的大小和数量。每个这样的表都由固定大小的条目组成(我使用该条目在适当的表中描述段标题或节标题)。条目是标题,并且包含指向该段或节的实际主体位置的指针(文件中的偏移量)。该主体存在于文件的数据部分中。更复杂的是-每个部分都是一个段的一部分,一个段可以包含许多段。


实际上,相同的数据要么作为段的一部分引用,要么作为段的一部分引用,这取决于当前上下文。链接时使用分段,执行时使用分段。

我们将使用readelf命令读取ELF。让我们从查看以下内容的ELF标头开始分析main


$ readelf -h main
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: Advanced Micro Devices X86-64
Version: 0x1
Entry point address: 0x4005e0
Start of program headers: 64 (bytes into file)
Start of section headers: 4584 (bytes into file)
Flags: 0x0
Size of this header: 64 (bytes)
Size of program headers: 56 (bytes)
Number of program headers: 9
Size of section headers: 64 (bytes)
Number of section headers: 30
Section header string table index: 27

我们可以看到,这是Unix上的ELF文件(64位), 其类型为EXEC,这是一个可执行文件-符合预期。它有9个程序标头(意味着有9个segment)和30个节标头(即section)。


下一步-程序头(program headers):


$ readelf -l main

Elf file type is EXEC (Executable file)
Entry point 0x4005e0
There are 9 program headers, starting at offset 64

Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
PHDR 0x0000000000000040 0x0000000000400040 0x0000000000400040
0x00000000000001f8 0x00000000000001f8 R E 8
INTERP 0x0000000000000238 0x0000000000400238 0x0000000000400238
0x000000000000001c 0x000000000000001c R 1
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
LOAD 0x0000000000000000 0x0000000000400000 0x0000000000400000
0x000000000000089c 0x000000000000089c R E 200000
LOAD 0x0000000000000dd0 0x0000000000600dd0 0x0000000000600dd0
0x0000000000000270 0x0000000000000278 RW 200000
DYNAMIC 0x0000000000000de8 0x0000000000600de8 0x0000000000600de8
0x0000000000000210 0x0000000000000210 RW 8
NOTE 0x0000000000000254 0x0000000000400254 0x0000000000400254
0x0000000000000044 0x0000000000000044 R 4
GNU_EH_FRAME 0x0000000000000774 0x0000000000400774 0x0000000000400774
0x0000000000000034 0x0000000000000034 R 4
GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000 RW 10
GNU_RELRO 0x0000000000000dd0 0x0000000000600dd0 0x0000000000600dd0
0x0000000000000230 0x0000000000000230 R 1

Section to Segment mapping:
Segment Sections...
00
01 .interp
02 .interp .note.ABI-tag .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt .init .plt .text .fini .rodata .eh_frame_hdr .eh_frame
03 .init_array .fini_array .jcr .dynamic .got .got.plt .data .bss
04 .dynamic
05 .note.ABI-tag .note.gnu.build-id
06 .eh_frame_hdr
07
08 .init_array .fini_array .jcr .dynamic .got

同样,我们看到我们有9个程序标头。它们的类型LOAD(有2个),DYNAMIC,NOTE等等。我们也可以看到各段的部分所有权。


最后-节标题(section headers):


$ readelf -S main
There are 30 section headers, starting at offset 0x11e8:

Section Headers:
[Nr] Name Type Address Offset
Size EntSize Flags Link Info Align
[ 0] NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[ 1] .interp PROGBITS 0000000000400238 00000238
000000000000001c 0000000000000000 A 0 0 1
[ 2] .note.ABI-tag NOTE 0000000000400254 00000254
0000000000000020 0000000000000000 A 0 0 4

[..]

[21] .dynamic DYNAMIC 0000000000600de8 00000de8
0000000000000210 0000000000000010 WA 6 0 8

[..]

[28] .symtab SYMTAB 0000000000000000 00001968
0000000000000618 0000000000000018 29 45 8
[29] .strtab STRTAB 0000000000000000 00001f80
000000000000023d 0000000000000000 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), l (large)
I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
O (extra OS processing required) o (OS specific), p (processor specific)

为了简洁起见,我对此进行了修剪。我们看到列出的30个部分带有各种名称(例如.note.ABI-tag)和类型(例如SYMTAB)。


您现在可能会感到困惑, 不用担心一般不会考这方面的东西。在他们的:因为我们感兴趣的是这个文件的特定部分,我解释这个程序头表,ELF文件可以有(和共享特别库必须具有)段头一个描述段型的PT_DYNAMIC。该部分拥有一个名为的部分.dynamic,其中包含有用的信息以了解动态依赖性。


直接依赖

我们可以使用readelf实用工具来进一步探索.dynamic可执行文件的部分。


特别是,本节包含我们ELF文件的所有动态依赖项。我们仅将其指定librandom.so为依赖项,因此我们希望列出main的依赖项:


$ readelf -d main | grep NEEDED
0x0000000000000001 (NEEDED) Shared library: [librandom.so]
0x0000000000000001 (NEEDED) Shared library: [libstdc++.so.6]
0x0000000000000001 (NEEDED) Shared library: [libm.so.6]
0x0000000000000001 (NEEDED) Shared library: [libgcc_s.so.1]
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]

objdump可执行文件可以提供类似的结果。在这种情况下,例如:objdump -p librandom.so | grep NEEDED将打印非常相似的输出。



我们可以看到librandom.so我们指定的,但是我们还得到了四个我们没有想到的额外依赖项。这些依赖性似乎出现在所有已编译的共享库中。这些是什么呢?


  • libstdc++: 标准C++库
  • libm: 包含基本数学函数的库
  • libgcc_s: GCC(GNU编译器集合)运行时库
  • libc: C库:它定义了系统调用和其他基础设施如库open,malloc,printf,exit等。

好的, 我们已经知道main依赖于librandom.so, 那么,为什么在运行时main找不到librandom.so


运行时搜索路径

ldd是一个工具,使我们可以查看递归共享库的依赖关系。这意味着我们可以看到程序在运行时需要的所有共享库的完整列表。这也让我们看到了在那里这些依赖所在。让我们继续运行main,看看会发生什么:


$ ldd main
linux-vdso.so.1 => (0x00007fff889bd000)
librandom.so => not found
libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f07c55c5000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f07c52bf000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f07c50a9000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f07c4ce4000)
/lib64/ld-linux-x86-64.so.2 (0x00007f07c58c9000)

如上,我们看到了文件librandom.so依赖的动态链接库文件,但是提示是not found


我们还可以看到,我们还有两个附加的库(vdsold-linux-x86-64)。它们是间接依赖关系, 更重要的是,我们看到ldd报告了库的位置。比如libstdc++ldd报告其位置为/usr/lib/x86_64-linux-gnu/libstdc++.so.6, 这是怎么知道的呢?


我们的依赖项中的每个共享库都按顺序在以下位置进行搜索:


  1. 可执行文件rpath中列出的目录;
  2. LD_LIBRARY_PATH环境变量中的目录,该变量包含以冒号分隔的目录列表(例如:/path/to/libdir:/another/path);
  3. 可执行文件runpath中列出的目录;
  4. 文件/etc/ld.so.conf中包含的文件目录列表;
  5. 默认系统库-通常为/lib/usr/lib (设置-z nodefaultlib参数编译时可跳过)

修复我们的可执行文件

好的, 我们验证了librandom.so是列出的依赖项,但找不到。我们知道在哪里搜索依赖项,ldd再次使用以下命令,确保目录实际上不在搜索路径中:


$ LD_DEBUG=libs ldd main
[..]

3650: find library=librandom.so [0]; searching
3650: search cache=/etc/ld.so.cache
3650: search path=/lib/x86_64-linux-gnu/tls/x86_64:/lib/x86_64-linux-gnu/tls:/lib/x86_64-linux-gnu/x86_64:/lib/x86_64-linux-gnu:/usr/lib/x86_64-linux-gnu/tls/x86_64:/usr/lib/x86_64-linux-gnu/tls:/usr/lib/x86_64-linux-gnu/x86_64:/usr/lib/x86_64-linux-gnu:/lib/tls/x86_64:/lib/tls:/lib/x86_64:/lib:/usr/lib/tls/x86_64:/usr/lib/tls:/usr/lib/x86_64:/usr/lib (system search path)
3650: trying file=/lib/x86_64-linux-gnu/tls/x86_64/librandom.so
3650: trying file=/lib/x86_64-linux-gnu/tls/librandom.so
3650: trying file=/lib/x86_64-linux-gnu/x86_64/librandom.so
3650: trying file=/lib/x86_64-linux-gnu/librandom.so
3650: trying file=/usr/lib/x86_64-linux-gnu/tls/x86_64/librandom.so
3650: trying file=/usr/lib/x86_64-linux-gnu/tls/librandom.so
3650: trying file=/usr/lib/x86_64-linux-gnu/x86_64/librandom.so
3650: trying file=/usr/lib/x86_64-linux-gnu/librandom.so
3650: trying file=/lib/tls/x86_64/librandom.so
3650: trying file=/lib/tls/librandom.so
3650: trying file=/lib/x86_64/librandom.so
3650: trying file=/lib/librandom.so
3650: trying file=/usr/lib/tls/x86_64/librandom.so
3650: trying file=/usr/lib/tls/librandom.so
3650: trying file=/usr/lib/x86_64/librandom.so
3650: trying file=/usr/lib/librandom.so

[..]

我剪裁了输出。难怪找不到我们的共享库-所在目录librandom.so不在搜索路径中!解决此问题的最特别的方法是使用LD_LIBRARY_PATH


$ LD_LIBRARY_PATH=. ./main

它可以工作,但不是很轻便。我们不想每次运行程序时都指定lib目录。更好的方法是将依赖项放入文件中, 这就需要设置rpathrunpath


rpath和runpath

rpath并且runpath是我们的运行时搜索路径“清单”中最复杂的项目。可执行文件或共享库的rpath和runpath在.dynamic我们前面介绍的部分中是可选条目。它们都是要搜索的目录列表。



rpath的类型为DT_RPATH, runpath的类型为DT_RUNPATH



rpathrunpath之间的唯一区别是搜索它们的顺序。具体来说,它们与LD_LIBRARY_PATH的顺序: rpath在LD_LIBRARY_PATH之前搜索,而runpath在LD_LIBRARY_PATH之后搜索。这意味着rpath不能用环境变量动态改变,而runpath可以。


设置rpath,看看是否可以让main工作:


$ clang++ -o main main.o -lrandom -L. -Wl,-rpath,.

参数-Wl-rpath逗号分隔将.标志传递给链接器。要进行设置runpath,我们还必须通过--enable-new-dtags参数设置(-Wl,--enable-new-dtags,-rpath,.)。让我们检查一下结果:


$ readelf -d main | grep path
0x000000000000000f (RPATH) Library rpath: [.]

$ ./main

可执行文件可以运行,但是已将其添加.rpath当前的工作目录中。这意味着它将无法从其他目录运行:


$ cd /tmp
$ ~/code/shared_lib_demo/main
/home/nurdok/code/shared_lib_demo/main: error while loading shared libraries: librandom.so: cannot open shared object file: No such file or directory

我们有几种解决方法。最简单的方法是复制librandom.so到搜索路径中的目录(例如/lib)。显然,更复杂的方法是我们要执行的操作-指定rpath相对于可执行文件的位置。


$ORIGIN

rpath和runpath中的路径可以是相对于当前工作目录的绝对路径(例如/path/to/my/libs/),但它们也可以是相对于可执行文件的。这是通过使用rpath定义中的$ORIGIN变量来实现的:


$ clang++ -o main main.o -lrandom -L. -Wl,-rpath,"\$ORIGIN"

注意,$ORIGIN不是一个环境变量。如果你设置ORIGIN=/path,它将不起作用。它总是放置可执行文件的目录。



请注意,我们需要对美元符号进行转义(或使用单引号),以便我们的shell不会尝试对其进行扩展。结果是main可以在每个目录下工作并librandom.so正确找到:


$ ./main
$ cd /tmp
$ ~/code/shared_lib_demo/main

让我们使用我们的工具包来确保:


$ readelf -d main | grep path
0x000000000000000f (RPATH) Library rpath: [$ORIGIN]

$ ldd main
linux-vdso.so.1 => (0x00007ffe13dfe000)
librandom.so => /home/nurdok/code/shared_lib_demo/./librandom.so (0x00007fbd0ce06000)
[..]

运行时搜索目录之安全性

如果您从命令行更改了Linux用户密码,则可能使用了该passwd实用程序:


$ passwd
Changing password for nurdok.
(current) UNIX password:
Enter new UNIX password:
Retype new UNIX password:
passwd: password updated successfully

密码被哈希之后存储在受root保护的文件/etc/shadow中,所以问题来了,非root用户如何更改此文件?


答案是passwd程序设置了setuid位,你可以通过ls看到:


$ ls -l `which passwd`
-rwsr-xr-x 1 root root 39104 2009-12-06 05:35 /usr/bin/passwd
# ^--- This means that the "setuid" bit is set for user execution.

这是s(该行的第四个字符)。设置了此权限位的所有程序均以该程序的所有者身份运行。在此示例中,用户是root(该行的第三个单词)。


这与共享库有什么关系? 我们举个例子.


现在我们在libs目录下有了librandom.so,并且我们将main程序的rpath设置为$ORIGIN/libs:


$ ls
libs main
$ ls libs
librandom.so
$ readelf -d main | grep path
0x000000000000000f (RPATH) Library rpath: [$ORIGIN/libs]

正常我们是可以运行main的,但是我们给它设置setuid位,并设置属主为root:


$ sudo chown root main
$ sudo chmod a+s main
$ ./main
./main: error while loading shared libraries: librandom.so: cannot open shared object file: No such file or directory

好吧,rpath行不通。让我们尝试设置LD_LIBRARY_PATH


$ LD_LIBRARY_PATH=./libs ./main
./main: error while loading shared libraries: librandom.so: cannot open shared object file: No such file or directory

还是不行,这里发生了什么?


出于安全考虑,使用提升的权限运行可执行文件(例如,当setuidsetgid特殊功能等)的搜索路径不同于正常:LD_LIBRARY_PATH被忽略,以及任何路径rpathrunpath包含$ORIGIN


原因是使用这些搜索路径允许利用提升的特权可执行文件以as身份运行root。有关此漏洞利用的详细信息,请参见此处


基本上,它允许您使提升特权的可执行文件加载您自己的库,该库将以root用户(或其他用户)身份运行。以root身份运行自己的代码几乎可以使您完全控制所使用的计算机。


如果您的可执行文件需要提升的特权,则需要在绝对路径中指定依赖项,或将其放置在默认位置(例如/lib)。


这里要注意的重要行为是,对于此类应用程序,ldd我们必须面对:


$ ldd main
linux-vdso.so.1 => (0x00007ffc2afd2000)
librandom.so => /home/nurdok/code/shared_lib_demo/libs/librandom.so (0x00007f1f666ca000)
libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f1f663c6000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f1f660c0000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f1f65eaa000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f1f65ae5000)
/lib64/ld-linux-x86-64.so.2 (0x00007f1f668cc000)

ldd不在乎setuid,它会$ORIGIN在搜索我们的依赖项时扩展。在调试对setuid应用程序的依赖项时,这可能是一个陷阱。


调试备忘单

如果在运行可执行文件时遇到此错误:


$ ./main
./main: error while loading shared libraries: librandom.so: cannot open shared object file: No such file or directory

您可以尝试执行以下操作:


  1. 找出缺少哪些依赖项ldd <executable>;
  2. 如果您不能识别它们,则可以通过运行来检查它们是否是直接依赖项readelf -d <executable> | grep NEEDED;
  3. 确保依赖项确实存在。也许您忘了编译它们或将它们移动到libs目录中?
  4. 找出使用来搜索依赖项的位置LD_DEBUG=libs ldd <executable>;
  5. 如果您需要在搜索中添加目录:

临时:将目录添加到LD_LIBRARY_PATH环境变量
嵌入文件中:将目录添加到可执行文件或共享库的目录中,rpath或runpath通过传递-Wl,-rpath,<dir>(for rpath)或-Wl,--enable-new-dtags,-rpath,<dir>(for runpath)。使用$ORIGIN相对于可执行文件的路径。



  1. 如果ldd显示没有依赖项丢失,请查看您的应用程序是否具有提升的特权。如果是这样,ldd可能会撒谎。请参阅上面的安全问题。

原文: https://amir.rachum.com/blog/2016/09/17/shared-libraries/#debugging-cheat-sheet
参考:
https://en.wikipedia.org/wiki/Executable_and_Linkable_Format
https://docs.oracle.com/cd/E23824_01/html/819-0690/chapter6-42444.html
https://www.gnu.org/software/libc/
http://tldp.org/HOWTO/Program-Library-HOWTO/shared-libraries.html
http://unix.stackexchange.com/questions/22926/where-do-executables-look-for-shared-objects-at-runtime
http://www.sco.com/developers/gabi/latest/ch5.pheader.html
https://greek0.net/elf.html
https://en.wikipedia.org/wiki/Rpath
http://blog.lxgcc.net/?tag=dt_rpath
https://cs.nyu.edu/~xiaojian/bookmark/linux/ld_so%20%20Dynamic-Link%20Library%20support.htm
http://unix.stackexchange.com/questions/101467/how-does-the-passwd-command-gain-root-user-permissions
http://nairobi-embedded.org/004_elf_format.html

收起阅读 »