0%

微软、Visual Studio 和 Visual C++ 那些事……

.NET 框架

.NET 框架(.NET Framework)的 维基百科 中有很详细的阐述,其中的 组件堆栈图 一目了然,对于 .NET vs. Java EE.NET vs. COM 的介绍也言简意赅。以下摘取几句,做笔记。

.NET框架是以一种采用系统虚拟机运行的编程平台,以通用语言运行库(Common Language Runtime)为基础,支持多种语言(C#、F#、VB.NET、C++、Python等)的开发。

通用中间语言被设计来即时编译(JIT),而Java的字节码在最初的时候则是设计成用于解释运行,而非即时编译。

前一版软件组件技术由Microsoft所提出的COM,该技术被用来创建大型(large-scale)的软件系统上,使用COM+ 或MTS对于传统分布式组件有强化的作用。很明显的,Microsoft最终将以.NET全面替换COM成为软件组件的架构。

.NET 的语言

CLI是一套运作环境规范,包括一般系统、基础类库和与机器无关的中间代码,全称为Common Language Infrastructure。如果一种语言实现生成了CLI,它也可以通过使用CLR被调用,这样它就可以与任何其他.NET语言生成的数据相交互。CLR也被设计为操作系统无关性。

CLI被设计成支持任何面向对象的编程语言,分享共同对象模型与大型共同类库。

大部分的语言都做了重大改变以搭配.NET框架。厂商通常利用这个机会来同时改变语言的其他特性。

  1. C#,一个以C++和Java语法为基础开发的一个全新的面向对象语言,是.NET开发的首选语言。

    我们圈外人以为做 C# 的就是 .NET,其实前者只是后者的官方开发语言。

  2. Visual Basic .NET,一个加强了面向对象支持的,支持多线程的Visual Basic版本。

    VB .NET 和 VB 不同。

  3. C++/CLI,一个C++的.NET平台版本变种。

    接下来我们就说说这个。

托管 C++ 和 C++/CLI

托管C++并非独立存在的编程语言,而仅仅是微软对C++的一个语法扩展,允许C++程序员在.NET框架和CLR的基础上进行托管编程。

C++ 托管扩展(Managed Extensions for C++,也经常被称为“托管 C++”)自Visual C++ 2005起被正在标准化的C++/CLI所替换。

目前只有托管C++及其后继者C++/CLI可以做到无缝集成托管和非托管代码,在托管代码中调用COM的速度又相当慢(所以用非托管代码调用咯),所以经常被用于其他语言和非托管代码之间的桥梁。

C++/CLI(CLI: Common Language Infrastructure)在计算机语言中是一门由微软设计,用来代替C++托管扩展(Managed C++,下文使用MC++指代)的语言。这门语言在兼容原有的C++标准的同时,重新简化了托管代码扩展的语法,提供了更好的代码可读性。

托管代码和本地代码

在维基百科 托管代码 的词条中提及托管代码的运行:

运行代码时,运行库编译器(runtime-aware compiler)在受控运行环境下,将中间语言(Intermediate Language)编译成本机的机器码。受控运行环境可为代码插入垃圾回收、异常处理、类型安全、数组边界和索引检查等,以保证代码安全的运行。

另有 博客 介绍的很好。

Visual Basic .NET和C#只能产生托管代码。如果你用这类语言写程序,那么所产生的代码就是托管代码。如果你愿意,Visual C++ .NET可以生成托管代码。当你创建一个项目的时候,选择名字是以.Managed开头的项目类型。例如.Managed C++ application。

跟Visual Studio平台的其他编程语言不一样,Visual C++可以创建非托管程序。当你创建一个项目,并且选择名字以M FC,ATL或者Win32开头的项目类型,那么这个项目所产生的就是非托管程序。

托管代码和非托管代码性能的比较:理解这个问题的关键在于对“即时编译器”和“解释器”是否有正确的认识。

.Net程序被加载入内存以后,当某段IL代码被第一次运行的时候,JIT编译器就会将这段IL代码,全部编译成本地代码,然后再执行。这也就是为什么.NET程序第一次运行都启动很慢的原因! 随.NET库,微软还附带了一个工具,可以事先将.NET程序所有的IL代码都编译成本地代码并保存在缓存区中,这样一来,这个程序就跟c++编译的一模一样了,没有任何区别,运行时也可以脱离JIT了(这里不要混淆了,这里不是说可以脱离.NET库,而是说不需要在进行即时编译这个过程了)。

所以,请不要将.NET和Java混为一谈,两个的运行效率根本不是一个等级的!

打包工具

我们在 VS2010 中更习惯使用 Visual Studio Installer 打包项目,进行安装和部署。但不知什么原因(目前我还不知道,也没有在网上认真找过 资料),微软放弃了自家的这款工具,在 VS2012 中不再有,而只能用 InstallShield Limited Edition,同时更新过 VS2010 的朋友也会发现在“安装和部署”下除了原有的 Visual Studio Installer,也增加了 InstallShield LE 项目,看来微软是铁了心放弃 VS Installer 了,那我们也没有坚守的必要了。除非公司跟不上时代,否则还是早放弃的好。

在 stackoverflow 上 Create MSI or setup project with Visual Studio 2012 说了同一件事情。好在 InstallShield LE 太难用,最关键的是在对 ActiveX 控件、Windows services的支持上有 硬伤,事情出现 转机,Visual Studio Installer 通过扩展的形式重新回到 VS 中,不过貌似只有 VS2013 和 2015 版本,但也足够了,你说呢?你要是死磕 VS2012,何必呢

预编译头

进阶操作不包含基础,进阶分为两部分:使用分支和修改提交。主要整理自 猴子都能懂的 GIT 入门。关于工作流,罗列了几个链接,值得反复阅读学习。

其实我们在 Git 官网 就能找到最好的学习材料:《Pro Git》。知识点讲得很全,也讲得很好。无论是进行系统的学习,还是作为参考手册都是不二之选!但如果你真的只有 5 分钟时间,还要快速上手使用,拿这么一本书显然是不合适的,所以就推荐了上面的入门贴。实际工作中,碰到什么问题,我都是来翻书的。

使用分支

先确定运用规则才可以有效地利用分支。

Merge 分支

Merge 分支是为了可以随时发布 release 而创建的分支,它还能作为 Topic 分支的源分支使用。保持分支稳定的状态是很重要的。如果要进行更改,通常先创建 Topic 分支,而针对该分支,可以使用Jenkins 之类的 CI 工具进行自动化编译以及测试

通常,大家会将 master 分支当作 Merge 分支使用。

Topic 分支

Topic 分支是为了开发新功能或修复 Bug 等任务而建立的分支。若要同时进行多个的任务,请创建多个的 Topic 分支。

Topic 分支是从稳定的 Merge 分支创建的。完成作业后,要把 Topic 分支合并回 Merge 分支。

分支的合并

完成作业后的 topic 分支,最后要合并回 merge 分支。合并分支有2种方法:使用 merge 或 rebase。使用这2种方法,合并后分支的历史记录会有很大的差别。

merge

进行 merge 的话,分为两种情况:

  1. fast-forward(快进)合并

    合并 bugfix 分支到 master 分支时,如果 master 分支的状态没有被更改过,那么这个合并是非常简单的。 bugfix 分支的历史记录包含 master 分支所有的历史记录,所以只要把 bugfix 移动到 master 分支就可以导入 bugfix 分支的内容了(即将 master 的 HEAD 移动到 bugfix 的 HEAD 这里)。这样的合并被称为 fast-forward(快进)合并。

    这种场景不会产生新的提交。

  2. non fast-forward

    但是,master 分支的历史记录有可能在 bugfix 分支分叉出去后有新的更新。这种情况下,要把 master 分支的修改内容和 bugfix 分支的修改内容汇合起来。

    因此,合并两个修改会生成一个提交。这时,master 分支的 HEAD 会移动到该提交上。

Note

执行合并时,如果设定了 non fast-forward (--no-ff)选项,即使在能够 fast-forward 合并的情况下也会生成新的提交并合并。

执行 non fast-forward 后,分支会维持原状。那么要查明在这个分支里的操作就很容易了。

阮一峰老师写到:

使用 --no-ff 参数后,会执行正常合并,在 Master 分支上生成一个新节点。为了保证版本演进的清晰,我们希望采用这种做法。

rebase

这个没有亲自实践过,要认真学学。

  1. 首先,rebase bugfix 分支到 master 分支, bugfix 分支的历史记录会添加在 master 分支的后面。

    这时移动提交 X 和 Y 有可能会发生冲突,所以需要修改各自的提交时发生冲突的部分。

  2. rebase 之后,master 的 HEAD 位置不变(此时就是典型的 fast-forward merge 场景了)。因此,要合并 master 分支和 bugfix 分支,即是将 master 的 HEAD 移动到 bugfix 的 HEAD 这里。

rebase 最大的优点是:历史记录成一条线,相当整洁。在《Pro Git》书中提到:

变基使得提交历史更加整洁。 你在查看一个经过变基的分支的历史记录时会发现,尽管实际的开发工作是并行的,但它们看上去就像是先后串行的一样,提交历史是一条直线没有分叉。

一般我们这样做的目的是为了确保在向远程分支推送时能保持提交历史的整洁——例如向某个别人维护的项目贡献代码时。在这种情况下,你首先在自己的分支里进行开发,当开发完成时你需要先将你的代码变基到 origin/master 上,然后再向主项目提交修改。 这样的话,该项目的维护者就不再需要进行整合工作,只需要快进合并便可。

同样,在书中也提到了变基的风险,作为新手我们并不需要做过多的学习。

具体练习见 用 rebase 合并

改写提交

参考自 改写提交。改写提交非常容易出现冲突,需要人工合并。

“改写提交”,我觉得《Pro Git》的表达更好一些——“重写历史”。因为有关的操作大多比较敏感,都有后遗症,操纵不当更会变成灾难,所以建议 认真读书、反复练习之后再在实际项目中操作。

在进入正文之前,我们必须清楚 ^~ 的区别:What’s the difference between HEAD^ and HEAD~ in Git?

强调~ 是对 ^ 的复合运算,而不是 ^n 的复合运算。

修改最近的提交

git commit –amend

取消过去的提交(内容)

git revert 可以取消指定的提交内容。

虽然使用后面要提到的 rebase -i 或 reset 可以直接删除提交,但是有时候并不合适(甚至不能)直接删除提交,比如不能随便删除已经发布的提交。这时就可以通过 revert 创建(否定某一次提交的)提交。

遗弃提交

git reset 除了默认的 mixed 模式,还有 soft 和 hard 模式。

模式名称 HEAD 的位置 索引 工作树
soft 修改 不修改 不修改
mixed 修改 修改 不修改
hard 修改 修改 修改

主要使用的场合:

  • 复原修改过的索引的状态(mixed)
  • 彻底取消最近的提交(hard)
  • 只取消提交(soft)

提取提交

cherry 樱桃

git cherry-pick 可以从其他分支复制指定的提交,然后导入到现在的分支。

例如,假设我们有个稳定版本的分支,叫 v2.0,另外还有个开发版本的分支 v3.0,我们不能直接把两个分支合并,这样会导致稳定版本混乱,但是又想增加一个 v3.0 中的功能到 v2.0 中,这里就可以使用 cherry-pick 了。

改写提交的历史记录

git rebase -i

这个命令很强大,在 rebase 指定 -i(interactive,交互式的) 选项,可以改写、替换、删除或合并提交。但也有一些副作用……

请翻书,重写历史

副作用?

场景描述:今天进行 rebase -i 操作后发现 merge –no-ff 的分支合并记录都没了

![merge –no-ff](https://raw.githubusercontent.com/nielong0610/MarkdownPhotos/master/merge –no-ff.png)

ps:借着这张图,复习一下 ^~ 的知识。

  • git reset head~,head 变更为 7c8d8a;
  • git reset head~2,head 变更为 6af94;
  • git reset head~^2,head 变更为 78663f;
  • git reset head4,报错!因为 head4 不存在
  • 强调~ 是对 ^ 的复合运算,而不是 ^n 的复合运算。(7c8d8a)^2~ = 6af94d = (7c8d8a)~

返回来继续说,执行 git rebase -i head~3 后结果如下:

![merge –no-ff](https://raw.githubusercontent.com/nielong0610/MarkdownPhotos/master/merge –no-ff 2.png)

在执行 git rebase -i head~3 时对话框如下:

1
2
3
4
5
6
pick 6af94d1 modify name.txt
pick 78663ff new age.txt (笔者注:7c8d8a2 透明的)
pick 915b8b1 re modify name.txt

# Rebase d0cd09d..915b8b1 onto d0cd09d (3 command(s))
#

可以看到,是不存在任何合并节点的。这也是 rebase 的目的!

疑问:如果我只是想改写 7c8d8a 之前某个 commit 的备注信息,且不想丢失分支演进,改怎么做呢?

结论:其实这算不得 rebase -i 的副作用,git rebase 默认 ignore merges。我自己以期上述目的,是用错了命令,虽然我至今不知道怎么实现上述目的 查看 --preserve-merges 参数。再往深的追究,其实我至今都不知道该怎么使用 git 去实现一个完善的工作流程,我了解一些命令的用法,但去不知道该怎么组织一个工程,怎么对一个项目进行细致的、完整的版本管理?

参见 git rebase --preserve-merges 手册。此参数的某些使用情景很乱,见 What exactly does git’s “rebase –preserve-merges” do (and why?)

汇合分支上的提交,然后一同合并到分支

git merge –squash

TortoiseGit 的 Show RefLog

TODO:感觉很有用哎,今天进行 rebase -i 操作后发现 merge –no-ff 的分支合并记录都没了,而且进行了两次 reabse -i,所以也没办法通过 git reset ORIG_HEAD 恢复。幸运的是发现了 RefLog 的存在,不知道这是 Git 本身的功能,还是 TortoiseGit 的功能扩展。抽空查探一下。

是 Git 自身的机制,很强大!官方手册,真心没有探索的欲望,怎么破?

其他

Tips

不同类别的修改 (如:Bug修复和功能添加) 要尽量分开提交,以方便以后从历史记录里查找特定的修改内容。

Tips

查看其他人提交的修改内容或自己的历史记录的时候,提交信息是需要用到的重要资料。所以请用心填写修改内容的提交信息,以方便别人理解。

以下是Git的标准注解:

1
2
3
第1行:提交修改内容的摘要
第2行:空行
第3行以后:修改的理由

请以这种格式填写提交信息。

Get

切换分支使用 checkout,检出文件也使用 checkout……不是因为作者懒得起名字,而是因为两者本质是同一种操作,都是从版本库(或索引)检出文件到工作区。分支本质上是某一个提交(的引用),切换分支就是检出对应的提交。

Get

如果在log命令添加 --decorate 选项执行,可以显示包含标签资料的历史记录。

在 windows 下搭建 git 服务器

2016/12/15 12:49:18 前两天将写的代码误删了,没有用版本管理工具。代码在一台连不上互联网的服务器上,因为系统老旧的原因,使用上有好多限制,负责人担心在新系统上写的代码拿到旧系统(最终必须部署在旧系统上)上用还得再次修改兼容性之类的问题,所以一般都是在旧系统上直接一个字母一个字母的敲代码。误删上周的工作内容后,后悔之余,也得考虑以后怎么避免这种情况。

在旧系统上凑活能装上 git,很原始的版本了。但它只能访问到我的笔记本(局域网),而不能访问互联网,所以就需要在 E431 windows7 的电脑上部署 Git 仓库。

依据上一篇搭建完成之后,操作中报错:(看着好像是不能是 --bare Repo 啊)

工作流

工作流 flow,对于大团队和个人肯定是不一样的,不能一概而论。对于个人开发,其重点最终要落在对分支 branch 的利用上,只要合理安排分支基本就可以了。对于人员很多,强调合作开发的团队,在上述基础上还要找准各节点(每个开发人员)的定位,会变得复杂很多。

一个完整的工作流包括对分支的利用、对节点的安排。好在大多时候我们只需关注前面一部分就能达到目的。

分支的安排

3.4 Git 分支 - 分支开发工作流 中描述了“长期分支”和“特性分支”两个概念。

Git分支管理策略 - 阮一峰 中使用了两个常设分支(长期分支)和三个临时分支(特性分支)。master 用于正式发布,develop 用于日常开发;feature 功能分支,Release 预发布分支,fixbug 修补分支。此策略即 Git flow

合并分支使用 --no-ff 选项。

Git flow 并不完美,Git 工作流程 - 阮一峰 中又讲解了另外两种分支策略:Github flow、Gitlab flow。推荐 Gitlab flow。在摸索中学习,在学习后实践。

ps:2017/2/23 15:23:53 我现在认为基于“版本发布”的,还是使用 git flow 更好一些,更符合直觉,更何况有那么多的前辈们(非 web 开发)都是如此呢。

gitlab flow

如何使用gitlab的flow以及代码review

GitLab Flow的使用

分布式节点的安排

5.1 分布式 Git - 分布式工作流程 中提到三种

集中式工作流、集成管理者工作流、司令官与副官工作流。

个人开发或者小团队开发,使用前者足够了。这种单点协作模型完全符合人们的直觉,好多人虽然不知道“集中式工作流”这个名词,但已经在这么用了。

其他

个人学习笔记,学习的总结,肯定是采用文字的形式记录。是以纸笔墨砚的形式,还是以更现代的码字的形式?上学的时候还固执于前者,写写画画了好几个本子,每个上面都有些文字,却也都不成系统,而且基本上全是个人日记,记录心情,发泄情绪。工作后渐渐放弃了纸笔间的习惯,喜欢在键盘上码字,整理的内容也由日记全部过渡成了技术帖。一开始在印象笔记中零零散散保存,后来同时使用为知笔记并逐渐过渡了日记使用前者,技术帖使用后者。因为基本不再写日记,印象笔记也就变成了存储记忆的空间,还是不翻书的那种。

后来迷上 markdown,有半个月打算放弃为知笔记,后来发现 markdown 只能替代为知笔记默认编辑器的位置,而个人知识管理的体系、框架的建立与维护还是得靠为知笔记。但两者的结合并不完美,核心问题在于技术人员(指我自己)使用 GFM,而为知笔记的 markdown 解释器并不能完全支持 GFM,尤其是语法高亮方面(主要使用 shell、vimL、console 的支持),GFM 不支持 [TOC] 标签。

使用 MarkdownPad 2 编辑文字很爽,但保存的 .md 文档在查阅时并不直接。将其拷贝到为知笔记中在首部添加 [TOC] 标签后发布,阅读时直观,查找时无论是根据多层级文件级树形查找还是直接使用内建的搜索框直接搜索都是很方便的,不足之处在于:

  • 一方面即便其支持 markdown 编辑,但并不友好,尤其是在使用 MarkdownPad 2 之后;
  • 另一方面,使用 markdown 语法的前提下,无法实现在笔记中插入对另一篇笔记的引用(链接),这在整理系列笔记时尤其不能容忍。

想通过搭建个人的博客网站来解决上述矛盾:保留使用 MarkdownPad 2 编辑文字的习惯,放弃为知笔记,将文字直接发布到外网上就可以解决无法链接、笔记之间相互跳转的问题。此种情况下带来一点点不便,如果帖子中涉及个人隐私、公司商业机密(我笑),需要仔细筛选。

在搭建个人博客的摸索阶段,从为知笔记到博客网站的过渡阶段,依然会使用为知笔记,并不在时间轴上一刀切,一下子将为知笔记中的内容全部拷贝到网站中,然后清空、销户。

使用 GitHub Pages 和 Hexo 搭建个人博客,静态网页会在 Github 中保留有版本记录,但源 .md 文件的存储(乃至版本控制)尚需考虑:

  • 对编辑的过程(版本控制)要求高,考虑~\hexo\source 创建版本库;2020/12/25,折腾一遭还是选择此方案

  • 只要求结果(对版本控制要求低),有 .md 文件的备份即可,考虑整体迁移到 Dropbox 中;

    因为 hexo\blog\source\_posts 目录(.md 源)只能在 hexo 树形目录的固定节点,所以想将其备份到 dropbox 中,需要 trick / workaround,Windows下硬链接、软链接和快捷方式的区别

  • 图片库的问题,考虑使用图床;2020/12/25,PicGo+GitHub,谁用谁知道

  • 对目录有需求,使用哪种实现呢?[TOC] 不解析,使用支持目录的主题。

搭建博客

阮一峰老师说喜欢写 Blog 的人,会经历三个阶段:

第一阶段,刚接触 Blog,觉得很新鲜,试着选择一个免费空间来写。
第二阶段,发现免费空间限制太多,就自己购买域名和空间,搭建独立博客。
第三阶段,觉得独立博客的管理太麻烦,最好在保留控制权的前提下,让别人来管,自己只负责写文章。

在网上查找两篇教程,按部就班的操作就能得到结果。目前对于博客没有任何复杂的要求,对评论、对布局、主题都没有挑选的欲望。博客只是用来将自己的整理的笔记、学习总结放到互联网上存储,查阅时不用再打开为知笔记,不用打开 MarkdownPad2 实时生成,带来查阅时的便利即可。

对于评论,教程中多提及中文用户推荐“多说”,起步阶段没有需求,加上暂时不想再多花时间在这些“边缘”上,所以未开启评论功能。总的来说,博客还是处于自我查阅阶段。

参考 hexo 官方站点 的介绍,在 下载安装 Node.js 之后,执行以下命令即可完成搭建。

  • 下载 Node.js 时,不使用代理时下载飞快;
  • 安装 Node.js 的过程会自动配置 Path,安装完毕使用 powershell 输入 node 验证
1
2
3
4
5
6
7
8
9
10
$ npm install hexo-cli -g
$ hexo init blog #解释:初始化了一个文件夹叫做blog,如果不想叫blog可以改为别的名字
$ cd blog
$ npm install
$ hexo server

# 通过 github 发布需要
npm install hexo-deployer-git -S
# 使用本地搜索需要
npm install hexo-generator-searchdb --save

拉取笔记仓库,并替换 source 目录:

  1. 进入 blog\ 目录,
  2. 执行 git clone git@github.com:tnie/notes.git
  3. 然后 rm source -rf && mv notes source
  4. 安装 Next 主题 npm install hexo-theme-next
  5. 使用仓库中的配置文件,更新 blog 的配置项目。

注意:绑定本地 hexo 和个人的 Github Pages 后(修改全局配置文件),执行 hexo deploy 命令部署时需要先 npm install hexo-deployer-git -S

发布博客

整理完笔记之后,怎么发布到 github.io 上?在这里针对具体的步骤做一份备忘录,防止因为懒惰或其他原因较长时间不发博客,“回归”时还要再次花费时间查阅 Hexo + GitHub Pages 技术帖子。而且,技术相通,但每个人都会培养出属于自己的习惯,不想“回归”时再摸索。

  1. 在 Git Shell 中打开至 hexo 目录。 cd hexo

  2. 敲入 hexo new "filename" 命令。此命令会在 source/_posts 目录下创建 filename.md 文件

    • filename 建议使用英文,且不要存在空白字符。可以使用下划线 _ 连接两个单词。
    • 用翻译工具查一查,起个好名字。
  3. 打开 source/_posts 目录,使用 MarkdownPad2 编辑文字,修改 title tags categories 属性,保存。

    • 一般来说,都是将写好的笔记全文粘贴过来。记得删除源文件,在过一段时间之后维护多个相似文件是很痛苦的。
    • title 使用中文;tags categories 原则上一律小写。
    • tags 属性存在多个时,可以这样写 tags: [make,makefile],但注意每一个 tag 中不能有空格,比如 tags: design pattern 这是错的
  4. 敲入 hexo generate 生成静态网页。

  5. 敲入 hexo server 启动本地服务器,查看效果。

  6. 敲入 hexo deploy 发布到互联网。

  7. 迁移为知笔记中的内容。“迁移”顾名思义,要从为知笔记中删除。

参考链接

有讲述整体流程的,有着眼于模块的——描述主题的、强调分类和标签的、添加站内搜索、添加评论的……

  1. 其中,在 Debian8 上安装 Node.js 使用 其官方 的 Linux Binaries (.tar.xz) 版本。解压之后直接配置系统 PATH 路径即可。tar xJvf node-v4.4.7-linux-x64.tar.xz
  2. 如果有更换域名的需求,可以参考 简明Github Pages与Hexo教程,而且作者是在 win7 环境下搭建的,同时文末给出了大量的参考资料。

主题

官方是有 辣么多的主题 让我们选,挑花了眼。

2017/4/13,使用新的主题 NexT。原因有二:

  • Jacman 的搜索不能用。网上有很多其搭配 swiftype,但其他的第三方(比如谷歌)却怎么都调试不成功。
  • Jacman 作者自己貌似都放弃了,作者自己的博客 使用 NexT 主题是其一,不再维护 Jacman 是关键。

使用 NexT 主题,碰到的问题 —— 其 404 页面的配置在使用 https 协议时有问题:

Hexo NexT 主题给出的腾讯 404 公益页面的教程仅适用于非 HTTPS 站点,对于严格限制混合内容的 HTTPS 站点来说,腾讯 404 公益页面使用的 JS 文件是无法引入的。

解决方案:关于 Hexo 上传 404 页面的问题

目录和标签

Hexo使用攻略:(四)Hexo的分类和标签设置

目录和标签有什么区别?关键词:一个目录,多个标签;树状结构,网状结构;

  1. 怎么给文章添加合适的标签呢?
  2. 在目录上尽可能的粗放,再用标签进行管理
  3. 使用树状目录尽量不要超过3级。
  4. 树状目录应该是和标签系统互补的;
  5. 标签起一种聚合作用,将相关性很强的东西联系到一起;分类目录则是相当于存放同类文章的容器,里面的文章虽属同类,相关性却可能不太强。
  6. 多参考大牛们的网站

不建议你“未雨绸缪”的打上标签。而是在有一定数量的笔记有大量相同性时,再打上标签,目录也是如此。

遵循这么一个原则:想一下读者看了这篇文章后,还会对哪些其他的文章感兴趣,然后这些文章就都添加上同一个标签。这样标签就可以实现目录交叉

其他

利用swiftype为hexo添加站内搜索v2.0

swiftype 试用结束后,只能付费。免费试用期到期后,官网登录之后无论点击哪里都是“Your Swiftype free trial has come to an end” 页面,猜测 swiftype 不再提供免费版本,佐证。需要寻找备案了……

使用本地搜索,安装插件(需要相应的配置项)。

1
npm install hexo-generator-searchdb --save

过程也是结果

在整理笔记的时候肯定会从网上查找资料,每个知识点肯定都会碰到一篇帖子让自己感慨“哇,总结得好好,娓娓道来,该讲的都讲到了,却也不多一句废话”,每每到此刻都觉得自己整理的笔记就是一坨垃圾,用词不当,表述不清晰,上下文转接不流畅,有的重点落下没讲,废话说太多……可这就是成长必经的过程,如果不是整理这个知识点,就不会一板一眼、较真地去查阅好多资料,线上的博客、线下的书,随手 google 来的终究只是编码过程中的 code demo,而非知识,只有当你读了很多篇笔记,看过了很多风景,才能有足够的理解,才能通过已经掌握的,通过对比,认识到某一篇是够精彩的。

如果因为总结的笔记太烂而气馁,放弃整理,那么就不会查阅足够的资料,就不会见到那篇“哇,精彩”的帖子!整理出来的笔记是结果,整理过程中见到的风景也是结果!

ps:在 2017/4/28 10:13:43 之前的文章大多是从 为知笔记 中手动迁移出来的。

  • 源于为知笔记不支持账号销户,所以也就没有清空其中的内容,暂时保留一段时间还可以校正迁移过程中的失误;
  • 但在文章全部迁移完毕之后也就不再使用为知笔记了,因为它 变更服务策略 太激进了。

我们从 GoF 说起,书中写到单例模式(Singleton)的意图是:

保证一个类仅有一个实例,并提供一个访问它的全局访问点。

在什么时候会用到单例模式呢?开发过程中常见的应用举例:

  • 日志类,一个应用往往只对应一个日志实例:丑陋的写法是在每个场景都实例化一个日志对象使用。
  • 配置类,应用的配置集中管理,并提供全局访问:丑陋的写法是在每次使用配置时都实例化一个配置对象,然后访问配置文件读取其中的配置项。
  • 管理器,比如我们写了一个 windows 服务,然后我们要封装一个启停此服务的管理器。
  • 共享资源类,加载资源需要较长时间,使用单例可以避免重复加载资源,并被多个地方共享访问。比如文件连接,数据库连接等
阅读全文 »

原来的笔记写于 2015年11月23日,当时使用的是为知笔记默认编辑器。十个月过去了,对于 make & Makefile 也有了更多的认识。今天重新整理一下,不过仍然定位在入门的帖子,所以不会添加新的内容,只是修改表述不当的地方,对原有内容作出删减,并用 markdown 格式重写。

这篇本来是打算写成阅读笔记的。但是所知不多,刚开始学习应该博览,求入门,求上手使用。再加上看的东西越来愈多,单纯的记录一篇帖子中的重点、难点,较真于一处在实践中基本不会碰到的细节,格局太小,意义也不大。

阅读全文 »

个人知识管理(PKM)和时间管理(GTD),是很重要的。日常生活学习中肯定有零散的涉及到,但并无系统性、条理性的认识。有关的概念也是近两年才接触到,网上能搜到的东西很多,但太知识性的、深度的“规范”针对普通人又不具有实用价值,即,适当的掌握概念,按照“规则”整理知识,规划时间做事是有价值的,提升会很明显,但对于普通人来说并无掌握其原理的必要,简单的拿来用就可以了。过犹不及。

最重要的是坚持

无论用什么方式,纸笔也好,还是 APP 也好,重要的是坚持,养成习惯。不能三天打鱼两天晒网。

我自己使用过,或正在使用的工具:

  1. 思维导图工具:mindjet

    最初半年的使用频率非常高,慢慢地到现在一两个月甚至用不上一次。工作、生活过程中记录事情,现在更习惯用列表,是思维固化了?还是这半年涉及的场景不需要发散性的思维?

  2. GTD 工具:doit.im,到 any.do,再到接触 Fabulous 之后简单的使用纸笔记录

    挺好用。坚持得不够,买了一年的会员,高频率使用了半年,闲置了半年之后销户了。直到今天,在 Fabulous 中见到一段话,直戳痛点:

    别掉入无止境清单症候群的圈套。换句话说,当你加入太多待办事项,你只会完成最容易的几项。

    (而)迅速完成容易的事项会让你有种虚假的成就感。你会发现忙碌了一整天以后,其实并没有在你的终极目标上取得多少进展。

    必须养成专注的性格。选择最重要的几件事项,然后除掉其他(相对来说不重要的)。

    doit 我没能坚持下来就是因为错误的使用方式。每天罗列好多的内容,带来虚假的成就感;每天完成一些(大多是最容易的),看着任务打上对勾,被划掉,带来虚假的成就感。甚至为了“打对勾,划掉”写上已经完成的事项,写上不重要的、做不做并无关键区别、随手就能解决、非关键任务等等类似的内容。然后把上午、下午大块大块的完整时间用来处理这些个“自讨的”琐碎任务,以“划掉多少”等价“成就多少”。好脆弱的内心,好虚假的幻想!随着时间的积累(也就是半年),doit 列表中堆积了大量的待办事项,而且都是筛选之后需要花费一定时间、精力并且有意义的事情,积累的越多越是提醒着自己的失败。潜意识里回避真相,现实世界里就做出“不再使用这款工具”的决定。关键问题在于:

    • 罗列待办事项清单之后,安排事项处理顺序、处理日期不合理,对自身解决问题的能力没有清晰的认识,“拍脑袋”定日期;

      • 也就是:不抓重点,或者抓不住重点。精力有限,肯定要做取舍。
    • (因此)计划制定完之后,并不能严格的执行,做不到日清月清,更谈不上总结;

      这也是放弃 doit 使用 any.do 之后没多长时间,又放弃 any.do 而使用 Fabulous/纸笔的原因,如果纠正不了根本问题,依旧还会在工具之间跳来跳去,在一个工具上遗留大量的未完事务之后迁移到另一个工具,如此循环,一直得不到解脱。类似的应用层出不穷,上文提到的 3 个是非常棒的了,用好一个就能给生活带来质变,不要因为错误的习惯埋没、错过了。

      收集任务是前提;安排任务是关键;完成任务是目的。不能把时间浪费在“前提”上,在对自身解决问题的能力有清晰的认识的基础上,以量力而行、轻重缓急为原则制定当日计划是重中之重。

      制定计划,包含完成时间,还隐含完成的能力,而不单单是罗列待办事项,想到的就罗列出来,不管今天能不能完成,今天想到的就列到今天的待办事项里,没有这么无脑操作。

整理为知笔记中和 make makefile automake 有关内容,成此篇。

gcc 说起

在介绍 make 之前,首先要保证你会使用 gcc/g++ 编译。了解 gcc 执行的四个阶段,知道预编译阶段、链接阶段需要什么,做什么操作;知道预编译时找不到头文件,链接时缺少库文件怎么处理;知道动态库、静态库的区别,怎么创建并使用它们。以上是必需的!只有掌握了上面的内容,才能保证面对一个简单的项目(可能包含头文件、源文件和链接库)知道怎么组织,最终得到可执行文件。

维基百科的描述:

GNU编译器套装(英语:GNU Compiler Collection,缩写为 GCC),一套编程语言编译器,以GPL及LGPL许可证所发行的自由软件,也是GNU项目的关键部分,也是GNU工具链的主要组成部分之一。GCC(特别是其中的C语言编译器)也常被认为是跨平台编译器的事实标准。

gcc 编译过程

预处理、编译、汇编和链接。学习 gcc,比较重要的是预处理和链接:

参考 gcc编译4个阶段

  1. 预处理阶段

    预处理器(cpp)根据以字符#开头的命令(directives),修改原始的C程序。

    也就是说预处理阶段,会查找并加载头文件生成一个新的C程序。

  2. 编译阶段

    编译阶段会将代码翻译成汇编语言。

    汇编语言是非常有用的,它为不同高级语言不同编译器提供了 通用的语言。如:C编译器和Fortran编译器产生的输出文件用的都是一样的汇编语言。

  3. 汇编阶段

    汇编阶段将汇编语言程序转换成为目标文件,也就是二进制机器码。

  4. 链接阶段

    该阶段将用到的一个或多个目标文件(库文件)链接生成可执行文件。

    在预编译阶段包含进来的“stdio.h”中只有“printf”函数的声明,而没有函数的实现,那么,是在哪里实现的“printf”函数呢?答案是:系统把这些函数实现都做到名为libc.so.6的库文件中去了,链接阶段gcc会到系统默认的搜索路径”/usr/lib”下进行查找,也就是链接到libc.so.6库函数中去,这样就能实现函数”printf” 了,而这也就是链接的作用。

    从上面可以看出,链接时需要目标文件的名称及其所在目录。

常用参数

知道了预处理器(cpp)要读取头文件,那么报错“xxx.h:No such file or directory”就需要确认我们是不是指定了查找目录,或者核对我们指定的路径是否包含该头文件;同样的道理,链接器(ld)需要链接目标文件,和前者稍有不用的地方在于:头文件一般都写在源代码中,我们只需要指定头文件路径;而链接目标文件时,既需要我们指出目标文件所在的目录,同时还需要我们指定使用目标路径下的哪个文件。

以上算是原理。知其所以然之后,我们来看工具具体的使用方法:

  1. 头文件相关的: -I 后跟绝对路径或相对路径,例如 -I ./include。预处理器会优先在 @搜索路径
  2. 链接过程: -L 后跟绝对路径或相对路径,例如 -L ./lib, -l 后跟库名,例如 -lmath,对应库文件 libmath.a 或 libmath.so
  3. -c:使用源文件生成对应的目标文件,而不进行链接,使用 ,例如 g++ -c main.cpp -o main.o
  4. -Wall:使 gcc 产生尽可能多的警告信息,并非全部。
  5. -Werror:把警告当做错误处理,即产生 warning 时就停止编译操作。

特殊的参数:

  1. -nostdinc:使编译器不在系统缺省的头文件目录里面找头文件,一般和-I联合使用,明确限定头文件的位置。

  2. -Wl,-rpath:在编译过程指定程序在运行时动态库的搜索路径,示例:

    1
    2
    # 当指定多个动态库搜索路径时,路径之间用冒号":"分隔
    gcc -Wl,-rpath,libPath -L libPath -ltest hello.c

    将搜索路径信息写入可执行文件(rpath代表runtime path)。这样就不需要设置环境变量。坏处是,如果库文件移动位置,我们需要重新编译test。

更多的参数,更详细的使用说明可以查看 官方手册,或者使用 man info –help 等方式获取。

搜索顺序

测试环境:

1
2
3
4
5
6
7
8
9
vimer@debian8light:~/code/test_search$ uname -a
Linux debian8light 3.16.0-4-amd64 #1 SMP Debian 3.16.7-ckt20-1+deb8u4 (2016-02-29) x86_64 GNU/Linux
vimer@debian8light:~/code/test_search$ lsb_release -a
No LSB modules are available.
Distributor ID: Debian
Description: Debian GNU/Linux 8.3 (jessie)
Release: 8.3
Codename: jessie
vimer@debian8light:~/code/test_search$

在 gcc version 4.9.2 (Debian 4.9.2-10) 环境下实际测试,linux 编译时头文件的搜索路径:

  1. 搜索会从 -I 开始
  2. 再找 /usr/lib/gcc/x86_64-linux-gnu/4.9/include
  3. 再找 /usr/local/include/
  4. 查找 /usr/include/

此测试只是确定以上 4 者相对的先后。测试时未包含更多的潜在的搜索路径,比如 gcc 的环境变量 C_INCLUDE_PATH、CPLUS_INCLUDE_PATH、OBJC_INCLUDE_PATH

两种不类型的头文件 #include<>#include"" 搜索规则:

  1. 使用<>包含的头文件一般会先搜索-I选项后的路径(即用gcc编译时的-I选项,注意是大写),之后就是标准的系统头文件路径。
  2. 而用””号包含的头文件会首先搜索当前的工作目录,之后的搜索路径才是和<>号包含的头文件所搜索的路径一样的路径。

事实上,知道上述提及的搜索路径即可。不必学究式地死记硬背之间的搜索顺序,能有多少意义呢?。

更直接、简单的方式,确认查找头文件时的路径搜索顺序。参考 使用gcc时头文件路径和动态链接库路径, 通过使用 -v 参数看到:

1
2
3
4
5
6
7
8
9
10
11
12
#include "..." search starts here:
#include <...> search starts here:
./include
/usr/include/c++/4.9
/usr/include/x86_64-linux-gnu/c++/4.9
/usr/include/c++/4.9/backward
/usr/lib/gcc/x86_64-linux-gnu/4.9/include
/usr/local/include
/usr/lib/gcc/x86_64-linux-gnu/4.9/include-fixed
/usr/include/x86_64-linux-gnu
/usr/include
End of search list.

在此不再具体测试编译过程中链接库文件时的搜索顺序、运行时动态库的搜索顺序,真正需要用之间的搜索顺序时再写代码测试。

动态链接和静态链接

在“链接阶段”一节中出现了库文件“libc.so.6”。@Linxu 中动态库

@引出静态库

@动态链接和静态链接

make 和 Makefile

make 命令用来解释、执行 Makefile文件。针对开发 C++ 程序来说:Makefile 文件是对项目文件(以源文件、链接库为主,也包括头文件)依赖关系的描述,是对 gcc 命令的有效组织。抛开依赖关系,如果项目不复杂,你也可以用 shell 脚本来组织 gcc 命令,前提是根据依赖关系调整好编译命令的排序。虽然能够得到同样的结果,但是执行的效率不如前者,容错的能力不如前者,耗费的精力更是项目越大越难以承受。事实上:

在make诞生之前,编译工作主要依赖于操作系统里面的类似于“make”、“install”功能的shell脚本。

来看看维基百科中的描述:

在软件开发中,make 是一个工具程序(Utility software),经由读取叫做“makefile”的文件,自动化建构软件。

它是一种转化文件形式的工具,转换的目标称为“target”;与此同时,它也检查文件的依赖关系,如果需要的话,它会调用一些外部软件来完成任务。它的依赖关系检查系统非常简单,主要根据依赖文件的修改时间进行判断。大多数情况下,它被用来编译源代码,生成结果代码,然后把结果代码连接起来生成可执行文件或者库文件。

它使用叫做“makefile”的文件来确定一个target文件的依赖关系,然后把生成这个target的相关命令传给shell去执行。

需要注意的是我们在 Linux 上用的一般是 make 的重写/改写版本 GNU Make,除此之外还有 BSD Make 等。

我们来看怎么写 Makefile 文件。

automake

人工写 Makefile 文件已经满足不了我们了。事实是人越来越懒,项目也越来越大,我们希望电脑帮我们做更多的工作。引用 Wikipedia 中的描述:

GNU Automake 是一种编程工具,可以产生供 make 程式使用的 Makefile,用来编译程式。它是自由软件基金会发起的 GNU 计划的其中一项,作为 GNU 构建系统的一部分。automake 所产生的Makefile 符合 GNU 编程标准。

automake 是由 Perl 语言所写的,必须和 GNU autoconf 一并使用。

延伸

以下概念当做扩展知识保留下来,如果有兴趣可以展开学习:

由内部链接、外部链接引出怎么写头文件。

我看了一下,这篇笔记最早是在六月二号创建的,可是现在 2016/8/13 16:12:05 ,呵呵。一方面是自己懒,另一方面,对于“内部链接、外部链接”的概念,不多了解一些,不敢随便整理啊。

我们先看看 维基百科- 头文件 中说了什么:

当一个子程序在定义的位置以外的地方被使用时,就需要进行前置声明,声明函数原型等。

假设一个程序员不用“头文件”,那么他至少需要在两个地方维护函数的声明:一个是包含函数实现的的文件,以及使用该函数的文件。如果使用该函数的文件有很多个,那么对函数的定义进行更改时就是灾难。

从某个方面来说,头文件降低了这种场景中程序员手工操作的复杂度(解放双手,繁琐的工作交给机器/编译器)。更重要的是保证了编写大型项目的易用性,难以想象如果没有头文件,几十万行的代码全都在一个源文件中。

将函数原型移到 XXX 头文件中之后,我们可以在需要的地方通过 #include <XXX> 预处理器指令 将其包含进来,这样每次编译时预处理阶段就会将 XXX 文件中的内容替换掉 #include <XXX> ,我们的函数原型也就被“前置声明”了。

阅读 wikipedia - include derective,我们可以从更高的层次考虑 Include 行为,想想 Makefile 中的 include,shell 脚本中的 include,表示类似甚至同样意义的关键字还有 import、copy。

在 C 语言中,什么内容需要放在头文件中,什么内容可以放在头文件中相对来说是比较容易区分的。接下来我们看看在 C++ 中,什么东西可以放在 .h 文件中,什么不能,什么东西又可以放在 .cpp 文件中。

声明和定义

首先需要区分开这两个概念,只是理解所有关键问题的前提。在笔记中暂时不展开说了,如果分不清楚,自行 Google。这里只备注几个容易混淆的条目:

  • 声明

    1. 仅仅提供函数原型。类外面的,类里面的

    2. class A;

    3. typedef声明

    4. 在类中定义的静态数据成员的声明

      1
      2
      3
      4
      5
      class A
      {
      public:
      static int a; // 声明
      };
  • 定义

    1. 在类定义之外,定义并初始化一个静态数据成员。如 A::a=0;

      1
      2
      3
      4
      5
      6
      class A
      {
      public:
      static int a; // 声明
      };
      A::a=0// 定义
    2. 在类外定义非内联成员函数

内部链接和外部链接

参考自:参考链接1解析C++中的内部连接与外部连接

链接把不同编译单元产生的符号联系起来。有两种链接方式:内部链接和外部链接。

内部链接

如果一个符号名对于它的编译单元来说是局部的,并且在链接时不可能与其他编译单元中的同样的名称相冲突,那个这个符号就是内部链接。内部链接意味着对此符号的访问仅限于当前的编译单元中,对其他编译单元都是不可见的。

具有内部链接的符号无法作用于当前文件外部,要让其影响程序的其他部分,可以将其放在.h文件中。此时在所有包含此.h文件的源文件都有自己的定义且互不影响。

  1. 所有的声明,包括类的声明,比如:class A;;(有时也将声明看作是无连接的,这里我们统一看成是内部连接的)。

    由于声明只对当前编译单元有用,因此声明并不将任何东西写入.o文件。

    这些声明本身不会影响到.o文件的内容。在编译阶段(狭义的,从高级语言到汇编语言到二进制,从 main.i 到 main.o,预处理已过,尚未链接),编译器只检测程序语法和函数、变量是否被声明。如果函数未被声明,编译器会报错。链接阶段,声明已经没有用了。

    而函数调用会导致一个未定义的符号被写入到.o文件,此后此.o文件与定义此符号的.o文件被连接在一起,前面未定义的符号被解析。

  2. 局部变量肯定是内部链接性质的,更应该被看作无连接的;

  3. 全局变量,如果使用了 static、const 关键词修饰,其作用域仅仅在当前文件作用域内,其他文件中即使使用extern声明也是无法使用的。因此,带有 static、const 关键字的全局变量也是内部链接性质的;

    static 和 const 还是有区别的。static 和 extern不能对同一个变量同时声明;但 const 和 extern 不是同一存储类别,可以同时用在同一个变量的声明。所以,我们可以用使用extern关键字修改const的连接属性。更多详情特别说明

  4. 枚举 enum、联合 union 类型是内部链接性质的;

  5. 类的定义是内部链接性质的:

    • 定义,意味着在同一编译单元中不能重复出现;

    • 内部链接性质,意味着如果需要在其他编译单元使用,类必须被定义在头文件且被其他文件包含。

      仅仅在其他文件中使用class a;声明是不行的,原因就是类的定义是内部链接,不会在目标文件导出符号。也就不会被其他单元解析它们的未定义符号。

  6. 内联函数定义(包括自由函数和非自由函数)。

    内联函数之所有具有内部链接,因为编译器在可能的时候,会将所有 对函数的调用替换为函数体,不将任何符号写入.o文件。

  7. 名字空间(包括全局名字空间)中的静态自由函数、静态友元函数、静态变量的定义。补充条目来源

外部链接

在一个多文件的程序中,如果一个符号在链接时可以和其他编译单元交互,那么这个名称就有外部链接。外部链接意味着该定义不仅仅局限在单个编译单元中。它可以在.o文件中产生外部符号。可以被其他编译单元访问用来解析它们未定义的符号。因此它们在整个程序中必须是唯一的,否则将会导致重复定义。

区分:判断一个符号是内部链接还是外部链接的一个很好的方法就是看该符号是否被写入.o文件。

  1. 类非内联成员函数,包括类成员函数和类静态成员函数

  2. 类的静态数据成员的定义具有外部链接性质

    1
    2
    3
    4
    5
    6
    7
    class A
    {
    public:
    static int a; // 内部链接
    };

    A::a = 0; // 外部链接
  3. 非内联函数

  4. 名字空间(包括全局名字空间)中非静态自由函数、非静态友元函数及非静态变量。补充条目来源

    前提是认为,类之外一般不存在使用 inline 修饰的函数。

学习

摘抄来源

Linkage

To understand the behavior of C and C++ programs, you need to know about linkage. In an executing program, an identifier is represented by storage in memory that holds a variable or a compiled function body. Linkage describes this storage as it is seen by the linker. There are two types of linkage: internal linkage and external linkage.

Internal linkage means that storage is created to represent the identifier only for the file being compiled. Other files may use the same identifier name with internal linkage, or for a global variable, and no conflicts will be found by the linker – separate storage is created for each identifier. Internal linkage is specified by the keyword static in C and C++.

External linkage means that a single piece of storage is created to represent the identifier for all files being compiled. The storage is created once, and the linker must resolve all other references to that storage. Global variables and function names have external linkage. These are accessed from other files by declaring them with the keyword extern. Variables defined outside all functions (with the exception of const in C++) and function definitions default to external linkage. You can specifically force them to have internal linkage using the static keyword. You can explicitly state that an identifier has external linkage by defining it with the extern keyword. Defining a variable or function with extern is not necessary in C, but it is sometimes necessary for const in C++.

Automatic (local) variables exist only temporarily, on the stack, while a function is being called. The linker doesn’t know about automatic variables, and so these have no linkage.

结论

综上,我们可以知道:将具有外部链接的定义放在头文件中几乎都是编程错误。因为如果该头文件中被多个源文件包含,那么就会存在多个定义,链接时就会出错。

在头文件中放置内部链接的定义却是合法的,但不推荐使用的。

链接,本质上都是具有外部链接性质的符号们的事情!

惯例

头文件为相关声明提供了一个集中存放的位置。头文件一般包含类的定义、枚举的定义、extern变量的声明、函数的声明、const int的定义、inline函数的定义。使用或者定义这些实体的文件要包含适当的头文件。

跌跌撞撞,兜来绕去,终究还是在 C++ 这个圈子里。毕业时,因为身外事去选择工作单位,而工作单位决定了工作性质,决定了自己处在做软件的边缘,甚至一度成为网管。两年之后跳出陷阱时,背负着“过去的选择”走进了 C++ 的世界,因为一无所长,在诸多技术更迭交替的 IT 圈我像个刚入门的新人似的,只了解一点点 VC++。我没有从头来过的魄力,我也并不清楚 Java 是否更有前途,互联网是否更多辛苦。

在毕业刚好三年的边上,想着“就是 C++ 了”其实很愚蠢。通过做决定安慰自己“明智”,其实只是没得选择的妥协。此时此刻应该想的是“如何学好 C++”,更现实一点“掌握 C++ 的哪些内容,能找到更好的工作”。暂时确定的目标:

  1. 会写代码。强调编码规范。
  2. 会编写类,使用类。强调抽象思维。
  3. 数据库操作,封装
  4. 并发(多线程),POSIX、boost、C++11原生支持
  5. 通信,socket 编程

曾经完美主义,现在坚持实用主义。站在公司的角度考虑,现实中生产使用,强调实用性。如果新特性带不来生产力上的提升,是不会有公司买账的。就是说是否掌握 C++11 甚至更新的特性,对找工作没有最直接的影响,如果带不了更高的生产力,都是空谈。不应该只为了关注新特性,学究式地死板地投入时间精力,要明白花时间做一件事的初衷、目的、意义和价值。举例来说,做并发时是否使用 C++11 的原生支持,在投产使用时没有什么影响的,重要的是能否解决问题,解决问题的能力,关键是把并发做出来,用什么技术往往并不重要。

那就好好学 C++11。工欲善其事,必先利其器。看看 C++ compiler support ,编译器有了,编辑代码呢?主要关注代码提示,开发者体验。

  • 在 Windows 上考虑使用 VS2015。个人观点,除了太大并不排斥 VS。
  • Linux 上呢?很难找到完善支持 C++11 代码提示的 IDE…Vim代码补全
  • Vim 和 IDE 之争 - 知乎,个人观点,两者会越来越像,追求生产力。
阅读全文 »

Flume 学习笔记

Flume(NG)架构设计要点及配置实践,在此基础上的扩展:Flume-ng的原理和使用

  1. Agent 的串联、并联
  2. Source 一般都是单一 Source;Source 一对多 Channel,分为复制(replication)和分流(multiplexing)
  3. Channel 一对多 Sink,实现负载均衡(load balance)和故障转移(failover)

在上述中多提到 Avro,在 Flume 的配置文件中也用到此项。Avro 介绍,但目前不需要了解。

Avro 是 Hadoop 中的一个子项目,也是 Apache 中一个独立的项目,Avro 是一个基于二进制数据传输高性能的中间件。

Redis 学习笔记

Redis Quick Start

  1. 在安装过程中,如果缺少依赖项,需要先编译 deps 目录。
  2. 将 redis 加入系统启动项,手册中使用 sudo update-rc.d redis_6379 defaults。但此命令只在 Debian 系下使用。

70.13x 系列服务器搭建指南

  1. 先安装 jdk,配置好 JAVA_HOME 环境变量

    1
    2
    3
    4
    5
    export JAVA_HOME=/home/cts/tool/jdk1.6.0_45/
    export JAVA_BIN=$JAVA_HOME/bin/
    export PATH=$JAVA_HOME/bin:$PATH
    export CLASSPATH=.:$JAVA_HOME/jreb:$JAVA_HOMEb/tools.jar
    export JRE_HOME=$JAVA_HOME/jre
  2. 安装 redis启动 redis

    • 如果需要,可以配置主从 redis
  3. 使用 cts2Cache-v0.2 程序初始化 redis

    • 执行 run-Init.sh 脚本执行初始化,需要修改 cts2Cache-0.1.0.jar 中配置文件 redis IP;
    • 使用 crontab 命令设置定时任务,每日执行 run-update-recv.sh 和 run-update-send.sh 脚本生成次日的节目表;
  4. 使用 cts2LogGate 程序开启日志网关

    • 参考 visio 图
    • 依照 send、recv、mu、lb 顺序开启,使用 kill 命令逆序关闭
    • 开启时可以使用 nohup 命令
    • 怎么关闭呢?
  5. 向日志网关发送日志