静态库与动态库

编译流程 编译生成可重定位目标文件,经链接器将其和依赖的库组合起来,生成可执行文件。 可执行文件运行时,经过加载器(loader)将其中的代码和和数据拷贝到内存,然后跳转到程序起始地址开始执行。 链接 连接器主要完成2个任务 符号解析:每个符号对应一个函数,全局变量或静态变量。符号解析将符号引用与符号定义关联起来。 重定位:编译器生成的可重定位目标文件中地址从0开始。链接器把多个库合并,将符号与内存地址关联,重定位这些节,并修改符号引用地址。 ELF文件 可执行可链接格式ELF(Executable and Linkable Format) 静态库与静态链接 linux上静态库以存档(archive, .a后缀)文件格式存储,是一组可重定位目标文件的集合。 静态链接时,会将.a中对应的内容拷贝,最终生成的可执行文件中包含静态库内容。程序运行时不再依赖静态库。 动态共享库与动态链接 静态库内容需要拷贝到可执行文件中,生成目标文件比较大,占用空间(减少了运行时依赖,现在看也许是优势) 静态库不支持热更新 相比静态库,以上就是动态库的特点:允许多个进程在内存中共享同一份库代码。 位置无关代码 位置无关代码PIC(Position-Independent Code), 编译时需要指定-fpic参数 显示调用 显示使用共享库时不需要依赖头文件,通过dlfcn.h中提供的API进行调用: // 将动态库加载到内存 // 指定名称,在LD_LIBRARY_PATH、/lib、/usr/lib目录下查找 // 或指定全路径 void *dlopen(const char *libname,int flag); // 在动态库中查找对应函数 void *dlsym(void *handle,const char *symbol); // dlclose() // 获取最近一次error信息,调用后会清空最近的错误信息 char *dlerror(void); 显示调用,可以在运行时打开库函数进行调用,有点儿类似动态语言的效果;而且,在代码执行到dlopen之前,so可以不存在。 隐式调用 隐式调用时需要引用库对应的头文件,代码中直接调用头文件中声明的函数即可;编译时,通过-l指定需要链接的库。 但隐式调用,在可执行文件开始执行时,所有依赖的库必须存在,否则无法运行。 后记 又水一篇。 链接中ELF结构与《深入理解Java虚拟机》第6章 类文件结构简直神似,看这种内容就和看模电或高频里一大坨电路图一样,全是记都记不住的细节。果然还是脑袋缓存太小了,处理太慢了。……

阅读全文

工具篇

曾经信心满满的立过很多flag,要学习并掌握某一领域或坚持某件事儿,最终都无疾而终,只剩下零星概念和一堆遗憾,原因大都类似: 计划被其它事情打断,中止了 进行到一半,发现意义不大 进行中遇到了困难,放弃了 当时掌握了,但没有及时总结,后续没有练习,忘记了 包括不限于协议栈、数据库、JVM、正则表达式、音律、写总结……也包括设计模式。结果一回头,猛然发现时间已经过去了这么久,却什么都没有留下来。 在此再立一个flag,每周写一篇总结。周积跬步,以致千里。 UML 统一建模语言 (Unified Modeling Language, UML) 所有介绍设计模式的资料都会用到uml图。之前总以为UML图就是类图,每次都是被继承,实现里面的实线、虚线、箭头等给整懵了。然后就放弃看图,只看代码实现。 现在才知道,类图只是UML中的一小部分。UML图还包括用例图,时序图等。和SE等大佬交流时,设计文档里全是各种UML图。 PlantUML 各种工具都可以画UML图,包括visio,Visual Paradigm等。我们使用PlantUML, 像Markdown一样,通过简单的语句写UML图,使用方便。当然,这一套工具也都是免费的。 windows安装 PlantUML PlantUML有IntelliJ IDEA的插件版,可以直接在IDEA的plugins中下载安装。我们也使用这个版本。 插件装好之后,新建puml文件,直接打开就可以看到对应的编辑器了。 Graphviz 安装插件之后,也可能无法看到右边的预览图,而是提示报错,这是因为缺少Graphviz环境变量。Graphviz是一个开源的绘图软件,很多开源软件生成可视化图时都使用Graphviz。 下载Graphviz并安装后,配置如下环境变量之后就好了。 GRAPHVIZ_DOT=D:\Program Files (x86)\Graphviz2.38\bin\dot.exe PlantUML基本语法 PlantUML类图 泛化,Generalization 关联,Association 组合,Composition 聚合,Aggregation 实现,Realization 依赖,Dependency @startuml Class01 <|-- Class02:泛化 Class03 <-- Class04:关联 Class05 *-- Class06:组合 Class07 o-- Class08:聚合 Class09 <|.. Class10:实现 Class11 <.. Class12:依赖 @enduml ……

阅读全文

策略模式 Strategy Pattern

策略模式定义了算法族,分别封装起来,让它们之间可以相互替换,此模式让算法的变化独立于使用算法的客户。 类图 继承 vs 组合 上面的描述有些抽象。策略模式想要表达的是,如果一个类需要某种行为,不要通过继承来实现(is-a),可以使用组合(has-a)来实现。这样做更加灵活,当业务类(duck)的行为(fly)需要改变时,只需要注入不同的算法实现类即可。 这也就是 鸭子继承, 如果一个类通过组合获取了鸭子的行为,那它就可以当作鸭子使用,不需要关注它是否继承自鸭子基类,是否真正的是一只鸭子。 Java中Spring的依赖注入(DI)也是这种思路。Golang中甚至取消了继承,从语法层面避免继承所带来的耦合。这里甚至可以推测,OOP在提出之初,只是基于对真实世界中对象建模的思路,提出了继承,实现等概念。但后续工程实践发现继承并没有组合好用。 很多协议设计也是这样,基于最初的构想设计了很多特性,结果后续使用中发现一部分设计很鸡肋或很容易出错。于是又通过各种约定,最佳实践,框架等来解决这些问题。 代码 public interface FlyBehavior { void fly(); } class FlyWithWings implements FlyBehavior { @Override public void fly() { // todo } } class FlyNoWay implements FlyBehavior { @Override public void fly() { // do nothing } } public interface QuackBehavior { void quack(); } class Quack implements QuackBehavior { @Override public void quack() { // todo } } class Squeak implements QuackBehavior { @Override public void quack() { // todo } } class MuteQuack implements QuackBehavior { @Override public void quack() { // todo } } public abstract class Duck { FlyBehavior flyBehavior; QuackBehavior quackBehavior; abstract void display(); void performFly() { flyBehavior.……

阅读全文

程序员的浪漫

程序员的浪漫 之前学习TLS时向一个做安全的朋友请教,过程中他给我讲了下面一个故事。听完后甚是感动,分享给大家。 为了方便描述,以我朋友的视角进行描述。 背景 公司开发的产品涉及安全时都要求使用TLS通信,TLS通信时需要使用证书。申请证书时需要预先制作好私钥,私钥与证书里的公钥形成密钥对。非对称加密可以粗略的理解为,通信前将你的证书发给对端,对端使用你证书中的公钥进行加密。加密后的密文只有对应的私钥才可以解密。只要私钥不泄密,就可以保证安全。 但通信时是需要使用私钥进行解密的,所以私钥也需要存储在服务器上。如果服务器被攻破,私钥被窃取,这样也很危险。所以使用openssl制作私钥时一般都要求提供一个密码来保护私钥。一般支持TLS的工具都支持加载加密后的私钥和密码来进行通信。 但是密码也是需要存储的,密码泄露也存在安全风险。更近一步的方法就是使用对称加密将私钥密码也进行加密,解密只能使用特定的工具进行。工具被编译成二进制文件,解密直接在内存进行,解密后的密码和私钥不存储,直接使用。 私钥 希望看完上面一段背景之后你还没有晕。 从上面我们可以看出,实际使用中一般开发人员是没机会接触到私钥密码的,大家直接使用加密后的私钥和密码就可以了。实际中也是这样。产品开发中都使用预置的证书、私钥,交付后由客户替换为商用证书就可以了。开发人员也不会关注预置证书是哪里来的,怎么生成的。 一次我们启动新项目,特殊原因要求我们更新一份新的证书。但替换后出了岔子,小部分业务无法相互通信,我被要求定位清楚问题出在哪里。为了确认是证书误用还是业务处理的问题,我需要把加密后的私钥解密,然后和公钥校验。折腾了小半天,拿到校验结果之后,我终于说服其他开发是他们的业务处理的问题。这个问题也就结束了。 私钥密码 解密私钥时,我需要先把私钥密码解密出来。搞定了问题之后,我又回想起那个私钥密码。那个密码很特殊,明显是人名全拼+数字组成的,这激起了我的好奇心。我本以为这数字是一个工号,查了一下并没有,难道已经离职了?又查了一下人名全拼,这次倒是查到了几位,挨个看了之后感觉都不太可能。 这时我的八卦之魂已经熊熊燃烧,停不下来了。那就最简单的方法,查一下这个文件的修改记录。这一下就锁定了第一次添加这个文件的哥们。然后我就试探性的发了个消息确认了下,至此真相大白了。 程序员的浪漫 私钥密码是这个开发人员女朋友的名字+生日组成的。创建的时候,他认为反正也不会有人用到密码,就决定埋这么一个彩蛋。 但他女朋友不是我们公司的,我不清楚他有没有告诉她。甚至,我都怀疑即使他告诉她这个浪漫的故事,她是否在完全听懂前就被前面那一大堆概念整懵了。 试想一下,服务器间每次建链时都要相互确认一下身份,都需要正确喊出她的名字才能通行。全球各地千万个机房里风扇轰鸣,机架上服务器网口灯快速闪烁着,这巨大的比特洪流都在默念着她的名字,不分昼夜。……

阅读全文

Hello World

我的Github Page开张了 突然发现github提供了page功能,简直太赞了。纯静态,使用markdown编写,支持绑定域名,不需要网络主机。。。这么多好处怎能拒绝呢? 有了page,还有什么理由不好好写博客,不好好记笔记呢? 现在开始,把之前博客上的零碎都搬过来,然后开始好好写博客。 致谢 本博客使用了柏荧(BY)的模板,在此向他致谢。……

阅读全文

go get 网络问题失败

go get 网络问题失败 使用go get golang.org/x获取包时,因为GWF问题导致下载失败。可以通过以下方法解决: Comments lism: https://coolshell.cn/articles/9070.html Bugsturb: Generally I don’t read article on blogs, but I wish to say that this write-up very forced me to try and do so! Your writing style has been amazed me. Thanks, very nice post. Free auto approve list 7-27-2018: I added a new list. As you’ll see it’s bigger than most of them. I hope you all have had a great week!……

阅读全文

Hello world!

Hello world! Welcome to WordPress. This is your first post. Edit or delete it, then start writing! Comments A WordPress Commenter: Hi, this is a comment. http://idmcrack.net: If you want to increase your familiarity just keep visiting this website and be updated with the most recent gossip posted here. http://idmcrack.net/ Plomberie Pro, Le Spécialiste Du Matériel, Tube Et Raccord: La plus accessible, c’est l’eau du robinet. http://kaeru-s.halfmoon.jp/01k001KK/nnzklfbkngrlkgnlkdf/cyawabbs.cgi/USER=Pieroweb? prix robinetterie salle de bain: Un robinet mal fermé : gaspillage de l’eau garanti.……

阅读全文