├── .gitignore ├── 404.html ├── LICENSE ├── README.md ├── README_zh_CN.md ├── _config.yml ├── _includes ├── footer.html ├── head.html └── header.html ├── _layouts ├── default.html ├── page.html └── post.html ├── _posts ├── 2012-09-17-why-complement.md ├── 2013-09-26-java-io.md ├── 2013-10-19-java-nio.md ├── 2013-11-26-effective-java.md ├── 2014-01-02-linux-print.md ├── 2014-01-05-vim.md ├── 2014-01-25-linux-filecmd.md ├── 2014-02-08-linux-power.md ├── 2014-02-13-linux-ps.md ├── 2014-02-20-linux-boot.md ├── 2014-02-23-ip.md ├── 2014-03-13-resubmit.md ├── 2014-03-15-file-upload.md ├── 2014-03-16-tomcat-https.md ├── 2014-03-22-reentrantlock.md ├── 2014-04-05-encode-decode.md ├── 2014-04-09-algorithm-gist.md ├── 2014-04-20-flex.md ├── 2014-05-07-linux-job.md ├── 2014-05-12-acturl-generic.md ├── 2014-05-18-java-concurrent.md ├── 2014-06-10-ssh.md ├── 2014-06-22-design-pattern.md ├── 2014-07-21-the-scheme-eval.md ├── 2014-08-20-jvm.md ├── 2014-09-19-sort.md ├── 2014-10-09-bitmap.md ├── 2014-10-17-shell-trash.md ├── 2014-12-13-java-newfeature.md ├── 2014-12-21-webservice.md ├── 2015-01-11-linux-make.md ├── 2015-01-22-linux-shell.md ├── 2015-02-06-closure.md ├── 2015-02-11-webtool.md ├── 2015-02-13-sqlappend.md ├── 2015-02-22-javajs.md ├── 2015-03-05-validator.md ├── 2015-03-12-spring-security.md ├── 2015-03-15-FullGc.md ├── 2015-03-26-large-file-diff.md ├── 2015-04-09-rmi.md ├── 2015-05-07-js-summary.md ├── 2015-05-27-mvc-validator.md └── 2015-05-28-large-file-diff-concurrent.md ├── favicon.ico ├── feed.xml ├── index.html ├── pages ├── 1archive.html ├── 2category.html ├── 3tag.html ├── 4about.md └── 5link-external.md ├── sitemap.xml └── static ├── bg.jpg ├── css ├── archive.css ├── article-list.css ├── category.css ├── highlight.css ├── post.css ├── style.css ├── tagCloud.css └── tags.css ├── img ├── JavaScriptMindMap.jpg ├── bg-canvas_bg.jpg ├── bitmap.jpg ├── boundary-A.png ├── boundary-B.png ├── boundary-C.png ├── http-get.png ├── http-post.png ├── i0.jpg ├── icon-picture.svg ├── io-match-2.jpg ├── io-match.jpg ├── ip.jpg ├── java-system.png ├── jvm-arg0.png ├── jvm-arg1.png ├── jvm-gc.png ├── jvm-handle.png ├── jvm-point.png ├── jvm-runtime.png ├── mvc-validator │ ├── adapter.jpg │ ├── driven.jpg │ ├── google.jpg │ ├── hasErrors.jpg │ ├── initBinder.jpg │ ├── notNull.jpg │ └── validator-config.jpg ├── namespace.png ├── rmi.jpg ├── servlet-encode │ ├── body-iso.png │ ├── body-utf.png │ ├── encodeURI.png │ ├── get.png │ ├── param.png │ ├── post.png │ └── world.png ├── shell-trash.png ├── spring-security-filter.png ├── spring-security-filterChain.jpg ├── xml-data-type.jpg └── xml-field-type.jpg ├── js ├── category.js ├── post.js ├── script.js ├── tagCloud.js ├── tags.js └── toc.js └── octicons ├── LICENSE.txt ├── README.md ├── octicons-local.ttf ├── octicons.css ├── octicons.eot ├── octicons.less ├── octicons.svg ├── octicons.ttf ├── octicons.woff └── sprockets-octicons.scss /.gitignore: -------------------------------------------------------------------------------- 1 | _site 2 | .sass-cache 3 | .idea 4 | .local 5 | .local/* 6 | .jekyll-cache -------------------------------------------------------------------------------- /404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 bit-ranger 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ##[点我查看中文说明/Click here for Chinese instructions](https://github.com/bit-ranger/blog/blob/gh-pages/README_zh_CN.md) 2 | 3 | # Blog Address 4 | 5 | 6 | 7 | 8 | # Must Modify 9 | 10 | ## 1.swiftype 11 | 12 | This service provides the on-site search function. 13 | 14 | Service address: . 15 | 16 | Documentation: 17 | 18 | After the setup is complete, you need to modify the `swiftype.searchId` in `_config.yml`. 19 | 20 | In your swiftype engine, go to `Install Search`, you will find the `swiftype.searchId`. 21 | 22 | ```html 23 | 28 | ``` 29 | 30 | ## 2.gitalk 31 | 32 | This service provides the comment function. 33 | 34 | Service address: . 35 | 36 | After the setup is complete, you need to modify the `comment` in `_config.yml`. 37 | -------------------------------------------------------------------------------- /README_zh_CN.md: -------------------------------------------------------------------------------- 1 | # 博客地址 2 | 3 | 4 | 5 | # 必改内容 6 | 7 | ## 1.swiftype 8 | 9 | 此服务提供站内搜索功能 10 | 11 | 服务地址: 12 | 13 | 文档: 14 | 15 | 设置完毕后,您需要修改 `_config.yml` 中 `swiftype.searchId`。 16 | 17 | 在自己的引擎中,进入 `Install Search`, 你将找到 `swiftype.searchId`。 18 | 19 | ```html 20 | 25 | ``` 26 | 27 | ## 2.gitment 28 | 29 | 此服务提供评论功能 30 | 31 | 服务地址: 32 | 33 | 设置完毕后, 需要修改 `_config.yml` 中的 `comment`。 34 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | plugins: [jekyll-paginate] 2 | 3 | permalink: /:categories/:title/ 4 | 5 | highlighter: rouge 6 | markdown: kramdown 7 | kramdown: 8 | input: GFM 9 | syntax_highlighter: rouge 10 | 11 | 12 | paginate: 10 13 | paginate_path: "page/:num" 14 | baseurl: "" 15 | 16 | 17 | 18 | defaults: 19 | - 20 | scope: 21 | path: "" 22 | type: posts 23 | values: 24 | layout: post 25 | styles : [highlight.css,post.css] 26 | scripts : [post.js] 27 | 28 | 29 | 30 | title: "bit-ranger" 31 | email: sincerebravefight@gmail.com 32 | description: "" 33 | github: 34 | username: bit-ranger 35 | swiftype: 36 | searchId: iXZyPokVfxFaBTvi64Y6 37 | comment: 38 | repo: blog 39 | client_id: a6fb73b3e790e234bab8 40 | client_secret: cc10aaff53a03d05ab2ee002dbf401dd7627c7a3 41 | 42 | url: "https://bit-ranger.github.io/blog" 43 | imgrepo: "https://bit-ranger.github.io/blog/static/img" 44 | 45 | 46 | #url: "http://127.0.0.1:4000" 47 | #imgrepo: "http://127.0.0.1:4000/static/img" 48 | 49 | -------------------------------------------------------------------------------- /_includes/footer.html: -------------------------------------------------------------------------------- 1 | 11 | 12 | 13 | 14 | 15 | {% for script in page.scripts %} 16 | {%endfor%} 17 | -------------------------------------------------------------------------------- /_includes/head.html: -------------------------------------------------------------------------------- 1 | 2 | {% if page.title %}{{ page.title }}{% else %}{{ site.title }}{% endif %} 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | {% for style in page.styles %} 18 | {%endfor%} 19 | 20 | 21 | -------------------------------------------------------------------------------- /_includes/header.html: -------------------------------------------------------------------------------- 1 |
2 | 33 |
34 | 35 | -------------------------------------------------------------------------------- /_layouts/default.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {% include head.html %} 5 | 6 | 7 | 8 | {% include header.html %} 9 | 10 |
11 |
12 | {{ content }} 13 |
14 | 20 |
21 | 22 | {% include footer.html %} 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /_layouts/page.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | --- 4 |
5 |
6 |
7 |
8 |

{{ page.title }}

9 |
10 |
11 |
12 | {{ content }} 13 |
14 |
15 |
16 |
-------------------------------------------------------------------------------- /_layouts/post.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | --- 4 |
5 |
6 |
7 |
8 |

{{ page.title }}

9 | 12 | 19 |
20 |
21 |
22 | {{content}} 23 |
24 |
25 |
26 | 27 | 28 | 29 | 30 | 31 | 32 |
33 |
34 |
35 | 38 |
39 |
40 |
41 |
42 |
43 |  Toc 44 |
45 |
46 |
47 |
48 |
49 |  Tags 50 |
51 |
52 |
    53 | {% for tag in page.tags %} 54 |
  • 55 |  {{ tag }} 56 |
  • 57 | {% endfor %} 58 |
59 |
60 |
61 | 101 |
102 |
103 |
-------------------------------------------------------------------------------- /_posts/2012-09-17-why-complement.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: post 3 | title: 为什么要用补码表示负数 4 | tags: 补码 computer 5 | categories: common 6 | --- 7 | 8 | 抛开二进制不谈,我们先来看看10进制 9 | 10 | 假设世界上没有负号且数字最大只有3位,我们要把 `0~999` 分成两部分,一部分表示负数,一部分表示正数,而且不影响他们的运算规律,应当如何去做? 11 | 12 | 首先,最大的负数加上一等于零,那么用999表示最大的负数再合适不过,现在需要正负数各一半,那么正数部分应当为 `0 ~ 499`,负数部分应当为 `500~999`,我们暂时把这些表示负数的数字成为`对照数字`。 13 | 14 | 验证一下,`18 - 5 = 18 + 995 = 1013 = 13` 15 | 16 | 有没有一种计算方法可以方便地找出`-5`与`995`的关系呢? 17 | 18 | 有,那就是`999 - 5 + 1` 19 | 20 | --- 21 | 22 | 将这个规律推及到二进制中 23 | 24 | 假设二进制数只有8位,那么8个1代表最大的负数,将这些数分为正负各一半,那么正数部分应该为 `0 ~ 01111111`,负数部分应当为 `10000000 ~ 11111111`。 25 | 26 | 验证一下,`00010010 - 00000101 = 00010010 + 11111011 = 00001101` 27 | 28 | 与十进制同理,`-00000101`的对照数字可以用相同方法计算出来,`11111111 - 00000101 + 1` 29 | 30 | 等等,我们发现了一个特性,这真是一个神奇的特性! 31 | 32 | `11111111 - 00000101` 等于`00000101` 按位取反! 33 | 34 | 现在,我们已经总结出了一点经验,在二进制中,负数的对照数字等于它的数值位按位取反再加一。 35 | 36 | 对于一个二进制负数来说,它是有符号的,因此他的第一位为符号位,剩余的为数值位,那么完整的表述就是负数的对照数字为`符号位不变,数值位按位取反再加一`,这个对照数字就是`补码`。 37 | 38 | --- 39 | 40 | 最后回答一下标题中的问题,显而易见,使用补码的好处就是,可以用加法来计算减法,从而简化电路设计。 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /_posts/2013-09-26-java-io.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: post 3 | title: Java IO 4 | tags: Java IO 5 | categories: Java 6 | --- 7 | 8 | 9 | * TOC 10 | {:toc} 11 | 12 | ![io][io] 13 | 14 | # IO流的分类 15 | 16 | 根据处理数据类型的不同分为:字符流和字节流 17 | 18 | 根据数据流向不同分为:输入流和输出流 19 | 20 | ## 字符流和字节流 21 | 22 | 字符流的由来: 因为数据编码的不同,而有了对字符进行高效操作的流对象。本质其实就是基于字节流读取时,去查了指定的码表。 字节流和字符流的区别: 23 | 24 | * 读写单位不同:字节流以字节(8bit)为单位,字符流以字符为单位,根据码表映射字符,一次可能读多个字节。 25 | 26 | * 处理对象不同:字节流能处理所有类型的数据(如图片、avi等),而字符流只能处理字符类型的数据。 27 | 28 | 结论:只要是处理纯文本数据,就优先考虑使用字符流。 除此之外都使用字节流。 29 | 30 | 31 | ## 输入流和输出流 32 | 33 | 对输入流只能进行读操作,对输出流只能进行写操作,程序中需要根据待传输数据的不同特性而使用不同的流。 34 | 35 | # 字节流 36 | 37 | ## 输入 38 | 39 | 40 | * `InputStream` 是所有的输入字节流的父类,它是一个抽象类。 41 | 42 | * `ByteArrayInputStream`、
`StringBufferInputStream`、
`FileInputStream` 是三种基本的介质流,它们分别从Byte 数组、StringBuffer、和本地文件中读取数据。`PipedInputStream` 是从与其它线程共用的管道中读取数据 43 | 44 | 45 | * `ObjectInputStream` 和所有`FilterInputStream` 的子类都是装饰流(装饰器模式的主角) 46 | 47 | ## 输出 48 | 49 | 50 | * `OutputStream` 是所有的输出字节流的父类,它是一个抽象类。 51 | 52 | * `ByteArrayOutputStream`、
`FileOutputStream` 是两种基本的介质流,它们分别向Byte 数组、和本地文件中写入数据。`PipedOutputStream` 是向与其它线程共用的管道中写入数据, 53 | 54 | * `ObjectOutputStream` 和所有`FilterOutputStream` 的子类都是装饰流。 55 | 56 | ## 配对 57 | 58 | ![io-match][io-match] 59 | 60 | 图中蓝色的为主要的对应部分,红色的部分就是不对应部分。紫色的虚线部分代表这些流一般要搭配使用。 61 | 62 | 63 | * `LineNumberInputStream` 主要完成从流中读取数据时,会得到相应的行号,至于什么时候分行、在哪里分行是由改类主动确定的,并不是在原始中有这样一个行号。在输出部分没有对应的部分,我们完全可以自己建立一个LineNumberOutputStream,在最初写入时会有一个基准的行号,以后每次遇到换行时会在下一行添加一个行号,看起来也是可以的。好像更不入流了。 64 | 65 | * `PushbackInputStream` 的功能是查看最后一个字节,不满意就放回流中。主要用在编译器的语法、词法分析部分。输出部分的BufferedOutputStream 几乎实现相近的功能。(预读,可以将读取内容的一部分放回流的开头,下回重读) 66 | 67 | * `StringBufferInputStream` 已经被Deprecated,本身就不应该出现在InputStream 部分,主要因为String 应该属于字符流的范围。已经被废弃了,当然输出部分也没有必要需要它了!还允许它存在只是为了保持版本的向下兼容而已。 68 | 69 | * `SequenceInputStream` 可以认为是一个工具类,将两个或者多个输入流当成一个输入流依次读取。完全可以从IO 包中去除,还完全不影响IO 包的结构,却让其更“纯洁”――纯洁的Decorator 模式。 70 | 71 | * `PrintStream` 也可以认为是一个辅助工具。主要可以向其他输出流,或者FileInputStream 写入数据,本身内部实现还是带缓冲的。本质上是对其它流的综合运用的一个工具而已。一样可以踢出IO 包!System.out 和System.out 就是PrintStream 的实例! 72 | 73 | # 字符流 74 | 75 | ## 输入 76 | 77 | 78 | * `Reader` 是所有的输入字符流的父类,它是一个抽象类。 79 | 80 | * `CharReader`、`StringReader` 是两种基本的介质流,它们分别将Char 数组、String中读取数据。`PipedReader`是从与其它线程共用的管道中读取数据。 81 | 82 | * `BufferedReader` 很明显就是一个装饰器,它和其子类负责装饰其它Reader 对象。 83 | 84 | * `FilterReader` 是所有自定义具体装饰流的父类,其子类PushbackReader 对Reader 对象进行装饰,会增加一个行号。 85 | 86 | * `InputStreamReader` 是一个连接字节流和字符流的桥梁,它将字节流转变为字符流(`转换流`)。`FileReader` 可以说是一个达到此功能、常用的工具类,在其源代码中明显使用了将FileInputStream`转变为Reader 的方法。 87 | 88 | ## 输出 89 | 90 | 91 | * `Writer` 是所有的输出字符流的父类,它是一个抽象类。 92 | 93 | * `CharArrayWriter`、`StringWriter` 是两种基本的介质流,它们分别向Char 数组、String 中写入数据。PipedWriter 是向与其它线程共用的管道中写入数据, 94 | 95 | * `BufferedWriter` 是一个装饰器为Writer 提供缓冲功能。 96 | 97 | * `PrintWriter` 和`PrintStream` 极其类似,功能和使用也非常相似。 98 | 99 | * `OutputStreamWriter` 是OutputStream 到Writer 转换的桥梁(`转换流`),它的子类`FileWriter` 其实就是一个实现此功能的具体类。 100 | 101 | ## 配对 102 | 103 | ![io-match-2][io-match-2] 104 | 105 | 106 | # RandomAccessFile 107 | 108 | 该类并不是流体系中的一员,其封装了字节流,同时还封装了一个缓冲区(字符数组),通过内部的指针来操作字符数组中的数据。 该对象特点: 109 | 110 | 111 | * 该对象只能操作文件,所以构造函数接收两种类型的参数:a.字符串文件路径;b.File对象。 112 | 113 | * 该对象既可以对文件进行读操作,也能进行写操作,在进行对象实例化时可指定操作模式(r,rw) 114 | 115 | **注意**:该对象在实例化时,如果要操作的文件不存在,会自动创建;如果文件存在,写数据未指定位置,会从头开始写,即覆盖原有的内容。 可以用于多线程下载或多个线程同时写数据到文件。 116 | 117 | 118 | # 管道 119 | 120 | Java的I/O库提供了一个称做链接(Chaining)的机制,可以将一个流处理器跟另一个流处理器首尾相接,以其中之一的输出为输入,形成一个 流管道的链接。 121 | 122 | 例如,`DataInputStream`流处理器可以把`FileInputStream`流对象的输出当作输入,将Byte类型的数据转换成Java的原始类型和String类型的数据。 123 | 124 | # 设计模式 125 | 126 | Java I/O库的两个设计模式: 127 | 128 | 129 | * `装饰者模式`:也叫**包装器模式**,在由InputStream,OutputStream,Reader和Writer代表的等级结构内部,有一些流处理器可以 对另一些流处理器起到装饰作用,形成新的,具有改善了的功能的流处理器。装饰者模式是Java I/O库的整体设计模式。 130 | 131 | * `适配器模式`:在由InputStream,OutputStream,Reader和Writer代表的等级结构内部,有一些流处理器是对其它类型的流源的适配。 132 | 133 | [io]: {{"/i0.jpg" | prepend: site.imgrepo }} 134 | [io-match]: {{"/io-match.jpg" | prepend: site.imgrepo }} 135 | [io-match-2]: {{"/io-match-2.jpg" | prepend: site.imgrepo }} -------------------------------------------------------------------------------- /_posts/2014-01-02-linux-print.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: post 3 | title: Linux打印 4 | tags: Linux 打印 5 | categories: Linux 6 | published: false 7 | --- 8 | 9 | 10 | * TOC 11 | {:toc} 12 | 13 | # echo 14 | 15 | echo是Shell的一个内部指令,用于在屏幕上打印出指定的字符串。 16 | 17 | ## 参数 18 | 19 | `-n` 不要在最后自动换行 20 | 21 | `-e` 若字符串中出现以下字符,则特别加以处理,而不会将它当成一般 22 | 23 | 文字输出: 24 | >` \a` 发出警告声; 25 | > 26 | >` \b` 删除前一个字符; 27 | > 28 | >` \c` 最后不加上换行符号; 29 | > 30 | >` \f` 换行但光标仍旧停留在原来的位置; 31 | > 32 | >` \n` 换行且光标移至行首; 33 | > 34 | >` \r` 光标移至行首,但不换行; 35 | > 36 | >` \t` 插入tab; 37 | > 38 | >` \v` 与\f相同; 39 | > 40 | >` \\` 插入\字符; 41 | > 42 | >` \nnn` 插入nnn(八进制)所代表的ASCII字符; 43 | 44 | `–help` 显示帮助 45 | 46 | `–version` 显示版本信息 47 | 48 | ## 原样输出字符串 49 | 50 | 若需要原样输出字符串(不进行转义),请使用单引号 51 | 52 | echo '$name\"' 53 | 54 | ## 显示命令执行结果 55 | 56 | echo `date` 57 | 58 | # printf 59 | 60 | printf 命令用于格式化输出, 是echo命令的增强版。它是C语言printf()库函数的一个有限的变形,并且在语法上有些不同。 61 | 62 | 注意:printf 由 POSIX 标准所定义,移植性要比 echo 好。 63 | 64 | printf 不像 echo 那样会自动换行,必须显式添加换行符(\n)。 65 | 66 | $printf "Hello, Shell\n" 67 | Hello, Shell 68 | $ 69 | 70 | printf 命令的语法 71 | 72 | `printf format-string [arguments...]` 73 | 74 | format-string 为格式控制字符串,arguments 为参数列表。 75 | 76 | 这里仅说明与C语言printf()函数的不同: 77 | 78 | * printf 命令不用加括号 79 | 80 | * format-string 可以没有引号,但最好加上,单引号双引号均可。 81 | 82 | * 参数多于格式控制符(%)时,format-string 可以重用,可以将所有参数都转换。 83 | 84 | * arguments 使用空格分隔,不用逗号。 85 | 86 | 注意,根据POSIX标准,浮点格式%e、%E、%f、%g与%G是“不需要被支持”。这是因为awk支持浮点预算,且有它自己的printf语句。这样Shell程序中需要将浮点数值进行格式化的打印时,可使用小型的awk程序实现。然而,内建于bash、ksh93和zsh中的printf命令都支持浮点格式。 87 | 88 | ## 参数 89 | 90 | `--help`:在线帮助; 91 | 92 | `--version`:显示版本信息。 93 | 94 | ## 格式替代符 95 | 96 | `%b` 相对应的参数被视为含有要被处理的转义序列之字符串。 97 | 98 | `%c` ASCII字符。显示相对应参数的第一个字符 99 | 100 | `%d`, `%i` 十进制整数 101 | 102 | `%e`, `%E`, `%f` 浮点格式 103 | 104 | `%g` `%e`或`%f`转换,看哪一个较短,则删除结尾的零 105 | 106 | `%G` `%E`或`%f`转换,看哪一个较短,则删除结尾的零 107 | 108 | `%o` 不带正负号的八进制值 109 | 110 | `%s` 字符串 111 | 112 | `%u` 不带正负号的十进制值 113 | 114 | `%x` 不带正负号的十六进制值,使用a至f表示10至15 %X 不带正负号的十六进制值,使用A至F表示10至15 115 | 116 | `%%` 字面意义的% 117 | 118 | ## 转义 119 | 120 | `\a` 警告字符,通常为ASCII的BEL字符 121 | 122 | `\b` 后退 123 | 124 | 125 | `\c` 抑制(不显示)输出结果中任何结尾的换行字符(只在%b格式指示符控制下的参数字符串中有效),而且,任何留在参数里的字符、任何接下来的参数以及任何留在格式字符串中的字符,都被忽略 126 | 127 | `\f` 换页(formfeed) 128 | 129 | `\n` 换行 130 | 131 | `\r` 回车(Carriage return) 132 | 133 | `\t` 水平制表符 134 | 135 | `\v` 垂直制表符 136 | 137 | `\\` 一个字面上的反斜杠字符 138 | 139 | `\ddd` 表示1到3位数八进制值的字符,仅在格式字符串中有效 \0ddd 表示1到3位的八进制值字符 -------------------------------------------------------------------------------- /_posts/2014-01-05-vim.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: post 3 | title: VIM 4 | tags: VIM Linux 5 | categories: special 6 | published: false 7 | --- 8 | 9 | 10 | * TOC 11 | {:toc} 12 | 13 | ## 1.切换模式 14 | 15 | `i` → Insert 模式,在光标前插入 16 | 17 | `ESC` → 回到 Normal 模式,Normal 模式下,所有键都是功能键 18 | 19 | `:help ` → 显示相关命令的帮助。你也可以就输入 :help 而不跟命令。(退出帮助需要输入:q) 20 | 21 | ## 2.存盘 & 退出 22 | 23 | `:e file` → 打开一个文件 24 | 25 | `:w` → 存盘 26 | 27 | `:saveas file` → 另存为 28 | 29 | `:x`, `ZZ` 或 `:wq` → 保存并退出 (:x 表示仅在需要时保存,ZZ不需要输入冒号并回车) 30 | 31 | `:q!` → 退出不保存 32 | 33 | `:qa!` 强行退出所有的正在编辑的文件,就算别的文件有更改。 34 | 35 | `:bn` 和 `:bp` → 你可以同时打开很多文件,使用这两个命令来切换下一个或上一个文件。(`:n`到下一个文件) 36 | 37 | ## 3.插入 38 | 39 | `a` → 在光标后插入 40 | 41 | `o` → 在当前行后插入一个新行 42 | 43 | `O` → 在当前行前插入一个新行 44 | 45 | `cw` → 替换从光标所在位置后到一个单词结尾的字符 46 | 47 | ## 4.删除 48 | 49 | `x` → 删当前光标所在的一个字符。 50 | 51 | `dd` → 删除当前行,并把删除的行存到剪贴板里 52 | 53 | `d` → 删除,常用于组合 54 | 55 | ## 5.拷贝 & 粘贴 56 | 57 | `yy` → 拷贝当前行当行于 ddP 58 | 59 | `p` → 粘贴(小写后) 60 | 61 | `P` → 粘贴(大写前) 62 | 63 | `y` → 拷贝,常用于组合 64 | 65 | >vim有12个粘贴板,分别是`0 1 2 ... 9 a " +`用`:reg`命令可以查看各个粘贴板里的内容,在vim中简单用`y`只是复制到`"`粘贴板里,同样用`p`粘贴的也是这个粘贴板里的内容。 66 | > 67 | >* `"Ny` 指定`N`粘贴板复制(主意引号) 68 | > 69 | >* `+` 粘贴板是系统粘贴板,`"+y`复制,`"+p`粘贴 70 | 71 | 72 | ## 6.定位 73 | 74 | `hjkl` (←↓↑→) 75 | 76 | ### 6.1 行内定位 77 | 78 | `0` → 数字零,到行头 79 | 80 | `^` → 到本行第一个不是blank字符的位置(所谓blank字符就是空格,tab,换行,回车等) 81 | 82 | `$` → 到本行行尾 83 | 84 | `g_` → 到本行最后一个不是blank字符的位置。 85 | 86 | `fa` → 到本行下一个为`a`的字符处 87 | 88 | `Fa` → 到本行上一个为`a`的字符处 89 | 90 | `ta` → 到`a`前的第一个字符 91 | 92 | `Ta` → 到`a`后的第一个字符 93 | 94 | ### 6.2 行间定位 95 | 96 | `NG` → 到第 `N` 行 (命令中的G是大写的) 97 | 98 | `:N` → 到第 `N` 行 99 | 100 | `gg` → 到第一行。(相当于1G,或 :1) 101 | 102 | `G` → 到最后一行。 103 | 104 | ### 6.3 全文定位 105 | 106 | `w` → 到下一个单词的开头。(默认单词形式) 107 | 108 | `e` → 到当前单词的结尾。(默认单词形式) 109 | 110 | `W` → 到下一个单词的开头。(包含空格`?`) 111 | 112 | `E` → 到当前单词的结尾。(包含空格`?`) 113 | 114 | `%` → 到匹配的括号处,包括 `( { [` (需要把光标先移到括号上) 115 | 116 | `*` → 到`下一个`匹配单词 117 | 118 | `# ` → 到`上一个`匹配单词 119 | 120 | `/pattern` → 搜索 `pattern` 的字符串(需要回车,如果搜索出多个匹配,可按`n`键到下一个) 121 | 122 | ## 7.Undo & Redo 123 | 124 | `u` → undo 125 | 126 | ``→ redo 127 | 128 | ## 8.重复 129 | 130 | `.` → (小数点) 可以重复`上一次`的命令 131 | 132 | `N ` → 重复某个命令`N次`,command可以为`.` 133 | 134 | >使用`.`时,若上一次的命令为`N `,则原样执行`N ` 135 | > 136 | >使用`N `时,若`command`为`.`,则`N`会覆盖`.`自带的次数 137 | 138 | ## 9.组合 139 | 140 | * `` 141 | 142 | >例如 143 | > 144 | >`0y$` → 从行头拷贝到行尾 145 | > 146 | >`ye` → 从当前位置拷贝到本单词的最后一个字符 147 | > 148 | >`y2/foo` → 拷贝2个foo之间的内容 149 | 150 | ## 10.区域选择 151 | 152 | `Na` 包括object 153 | 154 | `Ni` 不包括object 155 | 156 | * `action` 可以是任何的命令,如 d (删除), y (拷贝), v (可以视模式选择)。 157 | 158 | * `object` 可能是: `w` 一个单词, `W` 一个以空格为分隔的单词, `s` 一个句字, `p` 一个段落;也可以是成对出现的字符:`" ' ) } ]` 159 | 160 | * `N` 表示选取第N层,不写默认为1 161 | 162 | 163 | ## 11.自动提示 164 | 165 | `` 或 `` 166 | 167 | ## 12.宏录制 168 | 169 | `qa` → 把操作记录在寄存器 `a` 170 | 171 | `q` → 停止录制 172 | 173 | `@a` → replay`a`寄存器中的宏。 174 | 175 | `@@` → replay最新录制的宏。 176 | 177 | >示例: 178 | > 179 | >在一个只有一行且这一行只有“1”的文本中,键入如下命令: 180 | > 181 | >`qaYpq` 182 | > 183 | >`qa` 开始录制 184 | > 185 | >`Yp` 复制行. 186 | > 187 | >`` 增加1. 188 | > 189 | >`q` 停止录制. 190 | 191 | 这个宏的作用是:复制上一行并增加1 192 | 193 | 测试: 194 | 195 | @a → 在1下面写下 2 196 | 197 | @@ → 在2 正面写下3 198 | 199 | 现在做 100@@ 会创建新的100行,并把数据增加到 103. 200 | 201 | ## 13.可视化选择 202 | 203 | `v` 可视 204 | 205 | `V` 可视行 206 | 207 | `` 可视块 208 | 209 | 选择了可视化范围后,可做如下操作: 210 | 211 | >* `J` → 把所有的行连接起来(变成一行) 212 | > 213 | >* `<` → 左缩进 214 | > 215 | >* `>` → 右缩进 216 | > 217 | >* `=` → 自动缩进 218 | 219 | 在所有被选择的行后加上点东西: 220 | 221 | `` 222 | 223 | 选中行 224 | 225 | `$` 到行最后(不加将在每行行首编辑) 226 | 227 | `A` 块操作中进入插入模式 228 | 229 | 输入 230 | 231 | `ESC` 232 | 233 | ## 14.分屏 234 | 235 | `split` → 创建分屏 236 | 237 | `vsplit` → 创建垂直分屏 238 | 239 | `方向` → 方向可以是 hjkl 或 ←↓↑→,用来切换分屏。 240 | 241 | `_ ` → 最大化尺寸 242 | 243 | `|` → 垂直分屏最大化尺寸 244 | 245 | `+` → 增加尺寸 246 | 247 | `-` → 减小尺寸 248 | 249 | 250 | --- 251 | 252 | ## 其他 253 | 254 | 帮助文档 → `:help usr_02.txt` 255 | 256 | gvim 启动设置(显示行号,配色,代码高亮): 257 | 258 | >编辑安装目录下`_vimrc`文件 259 | > 260 | >添加如下代码 261 | > 262 | >set nu! 263 | > 264 | >colorscheme desert 265 | > 266 | >syntax enable 267 | > 268 | >syntax on 269 | -------------------------------------------------------------------------------- /_posts/2014-01-25-linux-filecmd.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: post 3 | title: Linux目录与文件 4 | tags: Linux 命令 5 | categories: Linux 6 | published: false 7 | --- 8 | 9 | 10 | * TOC 11 | {:toc} 12 | 13 | >`.` 14 | > 15 | >代表此层目录 16 | > 17 | >`..` 18 | > 19 | >代表上一层目录 20 | > 21 | >`-` 22 | > 23 | >代表前一个工作目录 24 | > 25 | >`~` 26 | > 27 | >代表『目前使用者身份』所在的家目录 28 | > 29 | >`~account` 30 | > 31 | >代表 account 这个使用者的家目录(account是个帐号名称) 32 | 33 | --- 34 | 35 | ## `cd ` 36 | 37 | **变换目录** 38 | 39 | --- 40 | 41 | ## `pwd` 42 | 43 | **显示目前所在的目录** 44 | 45 | [root@www ~]# `pwd [-Pl]` 46 | 47 | 选项与参数: 48 | 49 | `-P`:显示出真实的路径,而非使用连结 (link) 路径。 50 | 51 | `-l ` : 显示逻辑路径(默认) 52 | 53 | --- 54 | 55 | ## `mkdir` 56 | 57 | **创建新目录** 58 | 59 | [root@www ~]# `mkdir [-mp]` 目录名称 60 | 61 | 选项与参数: 62 | 63 | `-m` :配置文件的权限喔!直接配置,不需要看默认权限 (umask) 的脸色~ 64 | 65 | `-p` :帮助你直接将所需要的目录(包含上一级目录)递归创建起来! 66 | 67 | --- 68 | 69 | ## `rmdir` 70 | 71 | **删除『空』的目录** 72 | 73 | [root@www ~]# `rmdir [-p]` 目录名称 74 | 75 | 选项与参数: 76 | 77 | `-p` :连同上一级『空的』目录也一起删除 78 | 79 | --- 80 | 81 | ## `$PATH` 82 | 83 | **环境变量** 84 | 85 | 不同身份使用者默认的PATH不同,默认能够随意运行的命令也不同(如root与vbird); 86 | 87 | PATH是可以修改的,所以一般使用者还是可以透过修改PATH来运行某些位於/sbin或/usr/sbin下的命令来查询; 88 | 89 | 使用绝对路径或相对路径直接指定某个命令的档名来运行,会比搜寻PATH来的正确; 90 | 91 | 命令应该要放置到正确的目录下,运行才会比较方便;本目录(.)最好不要放到PATH当中。 92 | 93 | --- 94 | 95 | ## `ls` 96 | 97 | **查看文件与目录** 98 | 99 | [root@linux ~]# `ls [-aAdfFhilRS]` 目录名称 100 | 101 | [root@linux ~]# `ls [--color={none,auto,always}]` 目录名称 102 | 103 | [root@linux ~]# `ls [--full-time]` 目录名称 104 | 105 | 选项与参数: 106 | 107 | `-a` :全部的档案,连同隐藏档( 开头为 . 的档案) 一起列出来~ 108 | 109 | `-A` :全部的档案,连同隐藏档,但不包括 . 与 .. 这两个目录,一起列出来~ 110 | 111 | `-d` :仅列出目录本身,而不是列出目录内的档案数据 112 | 113 | `-f` :直接列出结果,而不进行排序 (ls 预设会以档名排序!) 114 | 115 | `-F` :根据档案、目录等信息,给予附加数据结构 116 | 117 | >例如: `*`:代表可执行档; `/`:代表目录; `=`:代表 socket 档案; `|`:代表 FIFO 档案 118 | 119 | `-h` :将档案容量以人类较易读的方式(例如 GB, KB 等等)列出来 120 | 121 | `-i` :列出 inode 位置,而非列出档案属性 122 | 123 | `-l` :长数据串行出,包含档案的属性等等数据; 124 | 125 | `-n` :列出 UID 与 GID 而非使用者与群组的名称 126 | 127 | `-r` :将排序结果反向输出 128 | 129 | `-R` :连同子目录内容一起列出来; 130 | 131 | `-S` :以档案容量大小排序! 132 | 133 | `-t` :依时间排序 134 | 135 | `--color=never` :不要依据档案特性给予颜色显示; 136 | 137 | `--color=always` :显示颜色 138 | 139 | `--color=auto` :让系统自行依据设定来判断是否给予颜色 140 | 141 | `--full-time` :以完整时间模式 (包含年、月、日、时、分) 输出 142 | 143 | `--time={atime,ctime}` :输出 access 时间或 改变权限属性时间 (ctime) 而非内容变更时间 (modification time) 144 | 145 | --- 146 | 147 | ## `cp` 148 | 149 | **复制文件或目录** 150 | 151 | [root@www ~]# `cp [-adfilprsu]` 来源档(source) 目标档(destination) 152 | 153 | [root@www ~]# `cp [options]` source1 source2 source3 .... directory 154 | 155 | 选项与参数: 156 | 157 | `-a ` :相当於 -pdr 的意思,至於 pdr 请参考下列说明;(常用) 158 | 159 | `-d` :若来源档为连结档的属性(link file),则复制连结档属性而非文件本身; 160 | 161 | `-f` :为强制(force)的意思,若目标文件已经存在且无法开启,则移除后再尝试一次; 162 | 163 | `-i` :若目标档(destination)已经存在时,在覆盖时会先询问动作的进行(常用) 164 | 165 | `-l` :进行硬式连结(hard link)的连结档创建,而非复制文件本身; 166 | 167 | `-p` :连同文件的属性一起复制过去,而非使用默认属性(备份常用); 168 | 169 | `-r` :递回持续复制,用於目录的复制行为;(常用) 170 | 171 | `-s` :复制成为符号连结档 (symbolic link),亦即『捷径』文件; 172 | 173 | `-u` :若 destination 比 source 旧才升级 destination ! 174 | 175 | 最后需要注意的,如果来源档有两个以上,则最后一个目的档一定要是『目录』才行! 176 | 177 | --- 178 | 179 | ## `rm` 180 | 181 | **移除文件或目录** 182 | 183 | [root@www ~]# `rm [-fir]` 文件或目录 184 | 185 | 选项与参数: 186 | 187 | `-f` :就是 force 的意思,忽略不存在的文件,不会出现警告信息; 188 | 189 | `-i` :互动模式,在删除前会询问使用者是否动作 190 | 191 | `-r` :递回删除啊!最常用在目录的删除了!这是非常危险的选项!!! 192 | 193 | --- 194 | 195 | ## `mv` 196 | 197 | **移动文件与目录,或更名** 198 | 199 | [root@www ~]# `mv [-fiu]` source destination 200 | 201 | [root@www ~]# `mv [options]` source1 source2 source3 .... directory 202 | 203 | 选项与参数: 204 | 205 | `-f` :force 强制的意思,如果目标文件已经存在,不会询问而直接覆盖; 206 | 207 | `-i` :若目标文件 (destination) 已经存在时,就会询问是否覆盖! 208 | 209 | `-u` :若目标文件已经存在,且 source 比较新,才会升级 (update) 210 | 211 | --- 212 | 213 | ## `basename`,`dirname` 214 | 215 | **取得路径的文件名称与目录名称** 216 | 217 | [root@www ~]# `basename` /etc/sysconfig/network 218 | 219 | network <== 很简单!就取得最后的档名~ 220 | 221 | [root@www ~]# `dirname` /etc/sysconfig/network 222 | 223 | /etc/sysconfig <== 取得的变成目录名了! 224 | 225 | --- 226 | 227 | ## `cat` 228 | 229 | **查看文件** 230 | 231 | [root@www ~]# `cat [-AbEnTv]` 232 | 233 | 选项与参数: 234 | 235 | `-A` :相当於 -vET 的整合选项,可列出一些特殊字符而不是空白而已; 236 | 237 | `-b` :列出行号,仅针对非空白行做行号显示,空白行不标行号! 238 | 239 | `-E` :将结尾的断行字节 $ 显示出来; 240 | 241 | `-n` :列印出行号,连同空白行也会有行号,与 -b 的选项不同; 242 | 243 | `-T` :将 [tab] 按键以 ^I 显示出来; 244 | 245 | `-v` :列出一些看不出来的特殊字符 246 | 247 | ## `tac` 248 | 249 | **反向显示** 250 | 251 | --- 252 | 253 | ## `nl` 254 | 255 | **添加行号显示** 256 | 257 | [root@www ~]# `nl [-bnw]` 文件 258 | 259 | 选项与参数: 260 | 261 | `-b` :指定行号指定的方式,主要有两种: 262 | 263 | > `-b a` :表示不论是否为空行,也同样列出行号(类似 cat -n); 264 | > 265 | > `-b t` :如果有空行,空的那一行不要列出行号(默认值); 266 | 267 | 268 | 269 | `-n` :列出行号表示的方法,主要有三种: 270 | 271 | > `-n ln` :行号在萤幕的最左方显示; 272 | > 273 | > `-n rn` :行号在自己栏位的最右方显示,且不加 0 ; 274 | > 275 | > `-n rz` :行号在自己栏位的最右方显示,且加 0 ; 276 | 277 | 278 | 279 | `-w` :行号栏位的占用的位数。 280 | 281 | --- 282 | 283 | ## `more` 284 | 285 | **一页一页翻动** 286 | 287 | [root@www ~]# `more` /etc/man.config 288 | 289 | 在more查看文件时可在最底行输入: 290 | 291 | 空白键 (`space`):代表向下翻一页; 292 | 293 | `Enter` :代表向下翻『一行』; 294 | 295 | `/`字串 :代表在这个显示的内容当中,向下搜寻『字串』这个关键字; 296 | 297 | `:f` :立刻显示出档名以及目前显示的行数; 298 | 299 | `q` :代表立刻离开 more ,不再显示该文件内容。 300 | 301 | `b` 或 `[ctrl]-b` :代表往回翻页,不过这动作只对文件有用,对管线无用。 302 | 303 | --- 304 | 305 | ## `less` 306 | 307 | **一页一页翻动** 308 | 309 | [root@www ~]# `less` /etc/man.config 310 | 311 | 在more查看文件时可在最底行输入: 312 | 313 | 空白键(`space`) :向下翻动一页; 314 | 315 | [`pagedown`]:向下翻动一页; 316 | 317 | [`pageup`] :向上翻动一页; 318 | 319 | `/`字串 :向下搜寻『字串』的功能; 320 | 321 | `?`字串 :向上搜寻『字串』的功能; 322 | 323 | `n` :重复前一个搜寻 (与 / 或 ? 有关!) 324 | 325 | `N` :反向的重复前一个搜寻 (与 / 或 ? 有关!) 326 | 327 | `q` :离开 less 这个程序; 328 | 329 | --- 330 | 331 | ## `head` 332 | 333 | **取出前面几行** 334 | 335 | [root@www ~]# `head [-n number]` 文件 336 | 337 | 选项与参数: 338 | 339 | `-n` :后面接数字,代表显示几行的意思 340 | 341 | --- 342 | 343 | ## `tail` 344 | 345 | **取出后面几行** 346 | 347 | [root@www ~]# `tail [-n number]` 文件 348 | 349 | 选项与参数: 350 | 351 | `-n` :后面接数字,代表显示几行的意思 352 | 353 | `-f` :表示持续侦测后面所接的档名,要等到按下[ctrl]-c才会结束tail的侦测 354 | 355 | --- 356 | 357 | ## `od` 358 | 359 | **非纯文字档** 360 | 361 | [root@www ~]# `od [-t TYPE]` 文件 362 | 363 | 选项或参数: 364 | 365 | `-t` :后面可以接各种『类型 (TYPE)』的输出,例如: 366 | 367 | >`a` :利用默认的字节来输出; 368 | > 369 | >`c` :使用 ASCII 字节来输出 370 | > 371 | >`d`[size] :利用十进位(decimal)来输出数据,每个整数占用 size bytes 372 | > 373 | >`f`[size] :利用浮点数值(floating)来输出数据,每个数占用 size bytes 374 | > 375 | >`o`[size] :利用八进位(octal)来输出数据,每个整数占用 size bytes 376 | > 377 | >`x`[size] :利用十六进位(hexadecimal)来输出数据,每个整数占用 size bytes 378 | 379 | --- 380 | 381 | ## `touch` 382 | 383 | **修改文件时间或建置新档** 384 | 385 | [root@www ~]# `touch [-acdmt]` 文件 386 | 387 | 选项与参数: 388 | 389 | `-a` :仅修订 access time; 390 | 391 | `-c` :仅修改文件的时间,若该文件不存在则不创建新文件; 392 | 393 | `-d` :后面可以接欲修订的日期而不用目前的日期,也可以使用 --date="日期或时间" 394 | 395 | `-m` :仅修改 mtime ; 396 | 397 | `-t` :后面可以接欲修订的时间而不用目前的时间,格式为[YYMMDDhhmm] 398 | 399 | --- 400 | 401 | ## `file` 402 | 403 | **查看文件类型** 404 | 405 | [root@www ~]# `file` 文件 406 | 407 | -------------------------------------------------------------------------------- /_posts/2014-02-08-linux-power.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: post 3 | title: Linux权限管理 4 | tags: Linux 命令 5 | categories: Linux 6 | published: false 7 | --- 8 | 9 | * TOC 10 | {:toc} 11 | 12 | ## `chmod` 13 | 14 | **改变权限** 15 | 16 | *数字法* 17 | 18 | root@www ~]# `chmod [-R] xyz` 文件或目录 19 | 20 | 选项与参数: 21 | 22 | `xyz` : 就是刚刚提到的数字类型的权限属性,为 rwx 属性数值的相加。 23 | 24 | `-R` : 进行递归(recursive)的持续变更,亦即连同次目录下的所有文件都会变更 25 | 26 | *符号法* 27 | 28 | chmod 29 | 30 | ugoa 31 | 32 | +(加入) -(除去) =(设定) 33 | 34 | rwx 35 | 36 | 文件或目录 37 | 38 | >例: 39 | > 40 | >[root@www ~]# chmod u=rwx,go=rx .bashrc 41 | > 42 | >\# 注意!那个 u=rwx,go=rx 是连在一起的,中间并没有任何空格! 43 | 44 | --- 45 | 46 | ## `chgrp ` 47 | 48 | 49 | **改变所属群组** 50 | 51 | chgrp [root@www ~]# `chgrp [-R] filename ...` 52 | 53 | 选项与参数: 54 | 55 | `-R` : 进行递归(recursive)的持续变更,亦即连同次目录下的所有文件、目录 都更新成为这个群组之意。常常用在变更某一目录内所有的文件之情况。 56 | 57 | --- 58 | 59 | ## `chown` 60 | 61 | **改变文件拥有者** 62 | 63 | [root@www ~]# `chown [-R]` 账号名称 文件或目录 64 | 65 | [root@www ~]# `chown [-R]` 账号名称:组名 文件或目录 66 | 67 | 选项与参数: 68 | 69 | `-R` : 进行递归(recursive)的持续变更,亦即连同次目录下的所有文件都变更 70 | 71 | --- 72 | 73 | ## `chattr` 74 | 75 | **配置文件隐藏属性** 76 | 77 | [root@www ~]# `chattr [+-=][ASacdistu]` 文件或目录名称 78 | 79 | 选项与参数: 80 | 81 | `+` :添加某一个特殊参数,其他原本存在参数则不动。 82 | 83 | `-` :移除某一个特殊参数,其他原本存在参数则不动。 84 | 85 | `=` :配置一定,且仅有后面接的参数 86 | 87 | >`A` :当配置了 A 这个属性时,若你有存取此文件(或目录)时,他的存取时间 atime将不会被修改,可避免I/O较慢的机器过度的存取磁碟。这对速度较慢的计算机有帮助。 88 | 89 | `S` :一般文件是非同步写入磁碟的(原理请参考第五章sync的说明),如果加上 S 这个属性时,当你进行任何文件的修改,该更动会『同步』写入磁碟中。 90 | 91 | `a` :当配置 a 之后,这个文件将只能添加数据,而不能删除也不能修改数据,只有root才能配置这个属性。 92 | 93 | `c` :这个属性配置之后,将会自动的将此文件『压缩』,在读取的时候将会自动解压缩,但是在储存的时候,将会先进行压缩后再储存(看来对於大文件似乎蛮有用的!) 94 | 95 | `d` :当 dump 程序被运行的时候,配置 d 属性将可使该文件(或目录)不会被 dump 备份 96 | 97 | `i` :这个 i 可就很厉害了!他可以让一个文件『不能被删除、改名、配置连结也无法写入或新增数据!』对於系统安全性有相当大的助益!只有 root 能配置此属性 98 | 99 | `s` :当文件配置了 s 属性时,如果这个文件被删除,他将会被完全的移除出这个硬盘空间,所以如果误删了,完全无法救回来了喔! 100 | 101 | `u` :与 s 相反的,当使用 u 来配置文件时,如果该文件被删除了,则数据内容其实还存在磁碟中,可以使用来救援该文件喔! 102 | 103 | 注意:属性配置常见的是 a 与 i 的配置值,而且很多配置值必须要身为 root 才能配置 104 | 105 | --- 106 | 107 | ## `lsattr` 108 | 109 | **显示文件隐藏属性** 110 | 111 | [root@www ~]# `lsattr [-adR]` 文件或目录 112 | 113 | 选项与参数: 114 | 115 | `-a` :将隐藏档的属性也秀出来; 116 | 117 | `-d` :如果接的是目录,仅列出目录本身的属性而非目录内的档名; 118 | 119 | `-R` :连同子目录的数据也一并列出来! 120 | 121 | --- 122 | 123 | ## `umask` 124 | 125 | **被排除的权限默认值** 126 | 127 | *目前使用者在创建文件或目录时候的被排除的权限默认值* 128 | 129 | [root@www ~]# `umask` 显示`数字`型态的权限配置分数 130 | 131 | 0022 <==与一般权限有关的是后面三个数字,表示在777中被排除的权限,即该目录中新增文件或目录的默认权限为755,又因为新增文件默认不具有x权限,所以该目录中新增目录的默认权限为755,文件的默认权限为644 132 | 133 | [root@www ~]# `umask -S` 以`符号`类型的方式来显示出权限 134 | 135 | u=rwx,g=rx,o=rx 136 | 137 | [root@www ~]# `umask 002` `指定`权限配置分数 138 | 139 | `!` 异或运算可排除权限 140 | 141 | --- 142 | 143 | ## 文件特殊权限 144 | 145 | **`SUID`** 146 | 147 | >`1` SUID 权限仅对二进位程序(binary program)有效;(对目录无效) 148 | 149 | >`2` 运行者对於该程序需要具有 x 权限; 150 | 151 | >`3` 运行者将具有该程序拥有者 (owner) 的权限,该权限仅在运行该程序的过程中有效 (run-time) 152 | 153 | >例如:/etc/shadow只有root能够访问,但是普通用户对于passwd有s权限,passwd的owner是root,所以普通用户可以在运行passwd时暂时获得root权限对/etc/shadow进行操作。 154 | 155 | `!` SUID权限指的是文件权限中owner部分x权限的特殊版 156 | 157 | `! chmod 4---` 可以赋予SUID权限,所属用户的x位将变成s, 若不具备x权限,s将变成S 158 | 159 | **`SGID`** 160 | 161 | *当为文件时* 162 | 163 | >`1` SGID 仅对二进位程序有用; 164 | > 165 | >`2` 运行者对於该程序需具有x 权限; 166 | > 167 | >`3` 运行者在运行的过程中将会获得该程序群组(group)的权限,该权限仅在运行该程序的过程中有效 (run-time) --- 基本同SUID 168 | 169 | *当为目录时* 170 | 171 | >`1` 使用者若对於此目录具有 r 与 x 的权限时,该使用者能够进入此目录; 172 | > 173 | >`2` 使用者在此目录下的有效群组(effective group)将会变成该目录所属的群组;用途:若使用者在此目录下具有 w 的权限(可以新建文件),则使用者所创建的新文件的群组与此目录所属的群组相同。 174 | 175 | `!` SGID权限指的是文件或目录权限中group部分x权限的特殊版 176 | `! chmod 2---` 可以赋予SGID权限,所属组的x位将变成s, 若不具备x权限,s将变成S 177 | 178 | **`SBIT`** 179 | 180 | ·SBIT权限仅对目录有效;(对文件无效) 181 | 182 | ·使用者对於此目录需要具有 w, x 权限; 183 | 184 | ·当使用者在该目录下创建文件或目录时,仅有自己与 root 才有权力删除该文件 185 | 186 | `! chmod 1---` 可以赋予SBIT权限,其他的x位将变成t 187 | -------------------------------------------------------------------------------- /_posts/2014-02-13-linux-ps.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: post 3 | title: Linux进程管理 4 | tags: Linux 进程管理 5 | categories: Linux 6 | published: false 7 | --- 8 | 9 | 10 | * TOC 11 | {:toc} 12 | 13 | ## `&` 14 | 15 | 16 | **将命令放在后台运行** 17 | 18 | command & 19 | 20 | 此时将会产生1个任务编号与一个PID,命令执行完成后将会在前台出现提示 21 | 22 | >后台执行的任务如果存在信息输出,最好将其写入到文件,否则将会在前台显示,影响操作 23 | 24 | --- 25 | 26 | ## `[ctrl]-z` 27 | 28 | **将命令放在后台暂停** 29 | 30 | 此时将会产生1个任务编号及其命令 31 | 32 | --- 33 | 34 | ## `jobs` 35 | 36 | **查看后台任务状态** 37 | 38 | [root@www ~]# `jobs [-lrs]` 39 | 40 | 选项与参数: 41 | 42 | `-l` :除了列出 job number 与命令串之外,同时列出 PID 的号码; 43 | 44 | `-r` :仅列出正在背景 run 的工作; 45 | 46 | `-s` :仅列出正在背景当中暂停 (stop) 的工作。 47 | 48 | >`+` 代表最近被放到背景的工作号码, `-` 代表最近最后第二个被放置到背景中的工作号码,其余没有符号 49 | 50 | --- 51 | 52 | ## `fg` 53 | 54 | **将后台任务放到前台** 55 | 56 | [root@www ~]# `fg %jobnumber` 57 | 58 | 选项与参数: 59 | 60 | `%jobnumber` :jobnumber 为任务号码(数字),那个 % 是可有可无的! 61 | 62 | --- 63 | 64 | ## `bg` 65 | 66 | **将后台暂停的任务变为运行中** 67 | 68 | [root@www ~]# `bg %jobnumber` 69 | 70 | --- 71 | 72 | ## `kill` 73 | 74 | **移除任务** 75 | 76 | [root@www ~]# `kill -signal %jobnumber` 77 | 78 | [root@www ~]# `kill -l` 79 | 80 | 选项与参数: 81 | 82 | `-l` :这个是 L 的小写,列出目前 kill 能够使用的讯号 (signal) 有哪些 83 | 84 | `signal` :代表给予后面接的那个工作什么样的指示罗!用 man 7 signal 可知: 85 | 86 | > `-1` :重新读取一次参数的配置档 (类似 reload); 87 | > 88 | > `-2` :代表与由键盘输入 [ctrl]-c 同样的动作; 89 | > 90 | > `-9` :立刻强制删除一个工作; 91 | > 92 | > `-15`:以正常的程序方式终止一项工作。与 -9 是不一样的。 93 | 94 | --- 95 | 96 | ## `nohup` 97 | 98 | **离线任务** 99 | 100 | >`!特别说明` 前面的几个命令中所谓的"背景”,都是指终端机背景(不会被ctrl+c中断),并非系统背景,一旦退出终端,任务立即终止。`nohup`可以在退出终端后继续执行任务。 101 | 102 | [root@www ~]# `nohup` [命令与参数] <==在终端机前台中工作 103 | 104 | [root@www ~]# `nohup` [命令与参数] & <==在终端机后台中工作 105 | 106 | --- 107 | 108 | ## `pstree` 109 | 110 | **显示进程树** 111 | 112 | [root@www ~]# `pstree [-A|U] [-up]` 113 | 114 | 选项与参数: 115 | 116 | `-A` :各程序树之间的连接以 ASCII 字节来连接; 117 | 118 | `-U` :各程序树之间的连接以万国码的字节来连接。在某些终端介面下可能会有错误; 119 | 120 | `-p` :并同时列出每个 process 的 PID; 121 | 122 | `-u` :并同时列出每个 process 的所属帐号名称。 123 | 124 | --- 125 | 126 | ## `ps` 127 | 128 | **将某个时间点的程序运行情况撷取下来** 129 | 130 | [root@www ~]# `ps aux` <==观察系统所有的程序数据 131 | 132 | [root@www ~]# `ps -lA` <==也是能够观察所有系统的数据 133 | 134 | [root@www ~]# `ps axjf` <==连同部分程序树状态 135 | 136 | 选项与参数: 137 | 138 | `-A` :所有的 process 均显示出来,与 -e 具有同样的效用; 139 | 140 | `-a` :不与 terminal 有关的所有 process ; 141 | 142 | `-u` :有效使用者 (effective user) 相关的 process ; 143 | 144 | `x` :通常与 a 这个参数一起使用,可列出较完整资讯。 145 | 146 | 输出格式规划: 147 | 148 | `l` :较长、较详细的将该 PID 的的资讯列出; 149 | 150 | `j` :工作的格式 (jobs format) 151 | 152 | `-f` :做一个更为完整的输出。 153 | 154 | --- 155 | 156 | ### `ps -l` 157 | 158 | **仅观察自己的 bash 相关程序** 159 | 160 | `F`:代表这个程序旗标 (process flags),说明这个程序的总结权限,常见号码有: 161 | 162 | >若为 `4` 表示此程序的权限为 root ; 163 | > 164 | >若为 `1` 则表示此子程序仅进行复制(fork)而没有实际运行(exec)。 165 | 166 | `S`:代表这个程序的状态 (STAT),主要的状态有: 167 | 168 | >`R` (Running):该程序正在运行中; 169 | > 170 | >`S` (Sleep):该程序目前正在睡眠状态(idle),但可以被唤醒(signal)。 171 | > 172 | >`D` :不可被唤醒的睡眠状态,通常这支程序可能在等待 I/O 的情况(ex>列印) 173 | > 174 | >`T` :停止状态(stop),可能是在工作控制(背景暂停)或除错 (traced) 状态; 175 | > 176 | >`Z` (Zombie):僵尸状态,程序已经终止但却无法被移除至内存外。 177 | 178 | `UID/PID/PPID`:代表此程序被该 UID 所拥有/程序的 PID 号码/此程序的父程序 PID 号码 179 | 180 | `C`:代表 CPU 使用率,单位为百分比 181 | 182 | `PRI/NI`:Priority/Nice 的缩写,代表此程序被 CPU 所运行的优先顺序,数值越小代表该程序越快被 CPU 运行 183 | 184 | `ADDR/SZ/WCHAN`:都与内存有关,ADDR 是 kernel function,指出该程序在内存的哪个部分,如果是个 running 的程序,一般就会显示『 - 』 / SZ 代表此程序用掉多少内存 / WCHAN 表示目前程序是否运行中,同样的, 若为 - 表示正在运行中。 185 | 186 | `TTY`:登陆者的终端机位置,若为远程登陆则使用动态终端介面 (pts/n); 187 | 188 | `TIME`:使用掉的 CPU 时间,注意,是此程序实际花费 CPU 运行的时间,而不是系统时间; 189 | 190 | `CMD`:就是 command 的缩写,造成此程序的触发程序之命令为何 191 | 192 | --- 193 | 194 | ### `ps aux` 195 | 196 | **观察系统所有程序** 197 | 198 | `USER`:该 process 属於那个使用者帐号的? 199 | 200 | `PID` :该 process 的程序识别码。 201 | 202 | `%CPU`:该 process 使用掉的 CPU 资源百分比; 203 | 204 | `%MEM`:该 process 所占用的实体内存百分比; 205 | 206 | `VSZ` :该 process 使用掉的虚拟内存量 (Kbytes) 207 | 208 | `RSS` :该 process 占用的固定的内存量 (Kbytes) 209 | 210 | `TTY` :该 process 是在那个终端机上面运行,若与终端机无关则显示 ?,另外, tty1-tty6 是本机上面的登陆者程序,若为 pts/0 等等的,则表示为由网络连接进主机的程序。 211 | 212 | `STAT`:该程序目前的状态,状态显示与 ps -l 的 S 旗标相同 (R/S/T/Z) 213 | 214 | `START`:该 process 被触发启动的时间; 215 | 216 | `TIME` :该 process 实际使用 CPU 运行的时间。 217 | 218 | `COMMAND`:该程序的实际命令为何? 219 | 220 | >ps结果后接 `` 表示为僵尸程序 221 | 222 | --- 223 | 224 | ## `top` 225 | 226 | **动态观察程序的变化** 227 | 228 | [root@www ~]# `top [-d 数字] | top [-bnp]` 229 | 230 | 选项与参数: 231 | 232 | `-d` :后面可以接秒数,就是整个程序画面升级的秒数。默认是 5 秒; 233 | 234 | `-b` :以批量的方式运行 top ,还有更多的参数可以使用,通常会搭配数据流重导向来将批量的结果输出成为文件。 235 | 236 | `-n` :与 -b 搭配,意义是,需要进行几次 top 的输出结果。 237 | 238 | `-p` :指定某些个 PID 来进行观察监测。 239 | 240 | 在 top 运行过程当中可以使用的按键命令: 241 | 242 | `?` :显示在 top 当中可以输入的按键命令; 243 | 244 | `P`:以 CPU 的使用资源排序显示; 245 | 246 | `M` :以 Memory 的使用资源排序显示; 247 | 248 | `N` :以 PID 来排序喔! 249 | 250 | `T` :由该 Process 使用的 CPU 时间累积 (TIME+) 排序。 251 | 252 | `k` :给予某个 PID 一个讯号 (signal) 253 | 254 | `r` :给予某个 PID 重新制订一个 nice 值。 255 | 256 | `q` :离开 top 软件的按键。 257 | 258 | --- 259 | 260 | **`echo $$`** ( 查看bash的pid) 261 | 262 | --- 263 | 264 | ## `signal` 265 | 266 | | 代号 | 名称 | 内容 | 267 | |:----:|:----:|:----| 268 | | 1 | SIGHUP | 启动被终止的程序,可让该 PID 重新读取配置,类似重新启动 | 269 | | 2 | SIGINT | 相当於用键盘输入 [ctrl]-c 来中断一个程序 | 270 | | 9 | SIGKILL | 强制中断一个程序,尚未完成的部分可能会有遗留物| 271 | | 15 | SIGTERM | 正常结束程序,如果该程序已经发生问题,signal将失效 | 272 | | 17 | SIGSTOP | 相当於用键盘输入 [ctrl]-z 来暂停一个程序 | 273 | 274 | --- 275 | 276 | ## `kill` 277 | 278 | **杀死进程** 279 | 280 | [root@www ~]# `kill PID` 281 | 282 | `!特别说明`:kill后面直接加数字表示杀死进程,这与上面的移除任务用法是不同的 283 | 284 | --- 285 | 286 | ## `killall` 287 | 288 | **按程序启动命令杀死进程** 289 | 290 | [root@www ~]# ` killall [-iIe] [command name]` 291 | 292 | 选项与参数: 293 | 294 | `-i` :interactive 互动的意思,删除时,会出现提示 295 | 296 | `-e` :exact 准确的意思,后面接的 command name要与运行中程序的启动命令一致,但整个完整的命令不能超过 15 个字节(若程序启动时使用了参数,则程序名与后面接的参数作为整体) 297 | 298 | `-I` :命令名称(可能含参数)忽略大小写。 299 | 300 | 301 | --- 302 | 303 | ## `nice` 304 | 305 | **指定nice值启动新程序** 306 | 307 | [root@www ~]# `nice [-n 数字] command` 308 | 309 | 选项与参数: 310 | 311 | `-n` :后面接一个数值,数值的范围 -20 ~ 19。 312 | 313 | --- 314 | 315 | ## `renice` 316 | 317 | **重设指定程序的nice值** 318 | 319 | [root@www ~]# `renice [number] PID` 320 | 321 | 选项与参数: 322 | 323 | `PID` :某个程序的 ID 324 | 325 | --- 326 | 327 | ## `free` 328 | 329 | **观察内存使用情况** 330 | 331 | [root@www ~]# `free [-b|-k|-m|-g] [-t]` 332 | 333 | 选项与参数: 334 | 335 | 默认单位为 Kbytes,可以使用 `-b`(bytes),`-m`(Mbytes),`-k`(Kbytes),`-g`(Gbytes) 来显示单位 336 | 337 | `-t` :显示实体内存与 swap 的总量。 338 | 339 | --- 340 | 341 | ## `uname` 342 | 343 | **查看系统核心信息** 344 | 345 | [root@www ~]# `uname [-asrmpi]` 346 | 347 | 选项与参数: 348 | 349 | `-a` :所有系统相关的资讯,包括底下的数据都会被列出来; 350 | 351 | `-s` :系统核心名称 352 | 353 | `-r` :核心的版本 354 | 355 | `-m` :本系统的硬件名称,例如 i686 或 x86_64 等; 356 | 357 | `-p` :CPU 的类型,与 -m 类似,只是显示的是 CPU 的类型! 358 | 359 | `-i` :硬件的平台 (ix86) 360 | 361 | --- 362 | 363 | ## `uptime` 364 | 365 | **观察系统启动时间与工作负载** 366 | 367 | 368 | --- 369 | 370 | ## `netstat` 371 | 372 | **追踪网络与Socket** 373 | 374 | [root@www ~]# `netstat -[atunlp]` 375 | 376 | 选项与参数: 377 | 378 | `-a` :将目前系统上所有的连线、监听、Socket 数据都列出来 379 | 380 | `-t` :列出 tcp 网络封包的数据 381 | 382 | `-u` :列出 udp 网络封包的数据 383 | 384 | `-n` :不以程序的服务名称,以端口号 (port number) 来显示; 385 | 386 | `-l` :列出目前正在网络监听 (listen) 的服务; 387 | 388 | `-p` :列出该网络服务的程序 PID 389 | 390 | 391 | 网络参数 392 | 393 | `Proto` :网络的封包协议,主要分为 TCP 与 UDP 封包,相关数据请参考服务器篇; 394 | 395 | `Recv-Q`:非由使用者程序连结到此 socket 的复制的总 bytes 数; 396 | 397 | `Send-Q`:非由远程主机传送过来的 acknowledged 总 bytes 数; 398 | 399 | `Local Address` :本地端的 IP:port 情况 400 | 401 | `Foreign Address`:远程主机的 IP:port 情况 402 | 403 | `State` :连线状态,主要有创建(ESTABLISED)及监听(LISTEN); 404 | 405 | 本机程序 406 | 407 | `Proto` :一般就是 unix 啦; 408 | 409 | `RefCnt`:连接到此 socket 的程序数量; 410 | 411 | `Flags` :连线的旗标; 412 | 413 | `Type` :socket 存取的类型。主要有确认连线的 STREAM 与不需确认的 DGRAM 两种; 414 | 415 | `State` :若为 CONNECTED 表示多个程序之间已经连线创建。 416 | 417 | `Path` :连接到此 socket 的相关程序的路径!或者是相关数据输出的路径。 418 | 419 | --- 420 | 421 | ## `dmesg` 422 | 423 | **查看核心产生的信息** 424 | 425 | --- 426 | 427 | ## `vmstat` 428 | 429 | **侦测系统资源变化** 430 | 431 | [root@www ~]# `vmstat [-a] [间隔时间] [总次数]]` <==CPU/内存等资讯 432 | 433 | [root@www ~]# ` vmstat [-fs]` <==内存相关 434 | 435 | [root@www ~]# `vmstat [-S 单位]` <==配置显示数据的单位 436 | 437 | [root@www ~]# `vmstat [-d]` <==与磁碟有关 438 | 439 | [root@www ~]# `vmstat [-p 分割槽]` <==与磁碟有关 440 | 441 | 选项与参数: 442 | 443 | `-a` :使用 inactive/active(活跃与否) 取代 buffer/cache 的内存输出资讯; 444 | 445 | `-f` :启动到目前为止,系统复制 (fork) 的程序数; 446 | 447 | `-s` :将一些事件 (启动至目前为止) 导致的内存变化情况列表说明; 448 | 449 | `-S` :后面可以接单位,让显示的数据有单位。例如 K/M 取代 bytes 的容量; 450 | 451 | `-d` :列出磁碟的读写总量统计表 452 | 453 | `-p` :后面列出分割槽,可显示该分割槽的读写总量统计表 454 | 455 | >内存栏位 (`procs`) 的项目分别为: 456 | 457 | >>`r` :等待运行中的程序数量;`b`:不可被唤醒的程序数量。这两个项目越多,代表系统越忙碌 (因为系统太忙,所以很多程序就无法被运行或一直在等待而无法被唤醒之故)。 458 | 459 | >内存栏位 (`memory`) 项目分别为: 460 | 461 | >>`swpd`:虚拟内存被使用的容量; `free`:未被使用的内存容量; `buff`:用於缓冲内存; `cache`:用於高速缓存。 这部份则与 `free` 是相同的。 462 | 463 | >内存置换空间 (`swap`) 的项目分别为: 464 | 465 | >>`si`:由磁碟中将程序取出的量; `so`:由於内存不足而将没用到的程序写入到磁碟的 `swap` 的容量。 如果 si/so 的数值太大,表示内存内的数据常常得在磁碟与主内存之间传来传去,系统效能会很差! 466 | 467 | >磁碟读写 (`io`) 的项目分别为: 468 | 469 | >>`bi`:由磁碟写入的区块数量; `bo`:写入到磁碟去的区块数量。如果这部份的值越高,代表系统的 I/O 非常忙碌! 470 | 471 | >系统 (`system`) 的项目分别为: 472 | 473 | >>`in`:每秒被中断的程序次数; `cs`:每秒钟进行的事件切换次数;这两个数值越大,代表系统与周边设备的沟通非常频繁! 这些周边设备当然包括磁碟、网络卡、时间钟等。 474 | 475 | >`CPU` 的项目分别为: 476 | 477 | >>`us`:非核心层的 CPU 使用状态; `sy`:核心层所使用的 CPU 状态; `id`:闲置的状态; `wa`:等待 I/O 所耗费的 CPU 状态; `st`:被虚拟机器 (virtual machine) 所盗用的 CPU 使用状态 (2.6.11 以后才支持)。 478 | 479 | --- 480 | 481 | ## `fuser` 482 | 483 | **查看使用指定的文件或文件系统的进程** 484 | 485 | [root@www ~]# `fuser [-umv] [-k [i] [-signal]]` file/dir 486 | 487 | 选项与参数: 488 | 489 | `-u` :除了程序的 PID 之外,同时列出该程序的拥有者; 490 | 491 | `-m` :后面接的那个档名会主动的上提到该文件系统的最顶层,对 umount 不成功很有效! 492 | 493 | `-v` :可以列出每个文件与程序还有命令的完整相关性! 494 | 495 | `-k` :找出使用该文件/目录的 PID ,并试图以 SIGKILL 这个讯号给予该 PID; 496 | 497 | `-i` :必须与 -k 配合,在删除 PID 之前会先询问使用者意愿! 498 | 499 | `-signal`:例如 -1 -15 等等,若不加的话,默认是 SIGKILL (-9) 罗! 500 | 501 | >权限: 502 | 503 | `c` :此程序在当前的目录下(非次目录); 504 | 505 | `e` :可被触发为运行状态; 506 | 507 | `f` :是一个被开启的文件; 508 | 509 | `r` :代表顶层目录 (root directory); 510 | 511 | `F` :该文件被开启了,不过在等待回应中; 512 | 513 | `m` :可能为分享的动态函式库; 514 | 515 | --- 516 | 517 | ## `lsof` 518 | 519 | **列出由程序开启的文件** 520 | 521 | [root@www ~]# `lsof [-aUu] [+d]` 522 | 523 | 选项与参数: 524 | 525 | `-a` :多项数据需要『同时成立』才显示出结果时!(内连接`?`) 526 | 527 | `-U` :仅列出 Unix like 系统的 socket 文件类型; 528 | 529 | `-u` :后面接 username,列出该使用者相关程序所开启的文件; 530 | 531 | `+d` :后面接目录,即找出某个目录底下已经被开启的文件! 532 | 533 | --- 534 | 535 | ## `pidof` 536 | 537 | **找出某个正在的运行程序的pid** 538 | 539 | [root@www ~]# `pidof [-sx] program_name...` 540 | 541 | 选项与参数: 542 | 543 | `-s` :仅列出一个 PID 而不列出所有的 PID 544 | 545 | `-x` :同时列出该 program name 可能的 PPID 那个程序的 PID 546 | 547 | -------------------------------------------------------------------------------- /_posts/2014-02-20-linux-boot.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: post 3 | title: Linux启动流程 4 | tags: Linux boot 5 | categories: Linux 6 | published: false 7 | --- 8 | 9 | 10 | * TOC 11 | {:toc} 12 | 13 | ![](http://image.beekka.com/blog/201308/bg2013081708.png) 14 | 15 | # 1.加载内核 16 | 17 | 操作系统接管硬件以后,首先读入`/boot `目录下的内核文件。 18 | 19 | # 2.启动初始化进程 20 | 21 | 内核文件加载以后,就开始运行第一个程序`/sbin/init`,它的作用是初始化系统环境。 22 | 23 | # 3.确定运行级别 24 | 25 | 许多程序需要开机启动。它们在Windows叫做"服务"(service),在Linux就叫做"守护进程"(daemon)。 26 | 27 | init进程的一大任务,就是去运行这些开机启动的程序。但是,不同的场合需要启动不同的程序,比如用作服务器时,需要启动Apache,用作桌面就不需要。Linux允许为不同的场合,分配不同的开机启动程序,这就叫做"`运行级别`"(runlevel)。也就是说,启动时根据"运行级别",确定要运行哪些程序。 28 | 29 | Linux预置七种运行级别(0-6)。 30 | 31 | * 0是关机, 32 | 33 | * 1是单用户模式(也就是维护模式), 34 | 35 | * 6是重启。 36 | 37 | * 运行级别2-5,各个发行版不太一样,对于Debian来说,都是同样的多用户模式(也就是正常模式)。 38 | 39 | init进程首先读取文件 /etc/inittab,它是运行级别的设置文件。第一行内容: 40 | 41 | id:2:initdefault: 42 | 43 | initdefault的值是2,表明系统启动时的运行级别为2。 44 | 45 | 每个运行级别在/etc目录下面,都有一个对应的子目录,指定要加载的程序。 46 | 47 | /etc/rc0.d 48 | /etc/rc1.d 49 | /etc/rc2.d 50 | /etc/rc3.d 51 | /etc/rc4.d 52 | /etc/rc5.d 53 | /etc/rc6.d 54 |    55 | "rc"表示run command(运行程序),"d"表示directory(目录) 。 56 | 57 | /etc/rc2.d中指定的程序: 58 | 59 | $ ls /etc/rc2.d 60 | README 61 | S01motd 62 | S13rpcbind 63 | S14nfs-common 64 | S16binfmt-support 65 | S16rsyslog 66 | S16sudo 67 | S17apache2 68 | S18acpid 69 | ...  70 | 71 | 除了第一个文件README以外,其他文件名都是"字母S+两位数字+程序名"的形式。 72 | 73 | * 字母`S`表示Start,也就是启动的意思(启动脚本的运行参数为start),如果这个位置是字母`K`,就代表Kill(关闭),即如果从其他运行级别切换过来,需要关闭的程序(启动脚本的运行参数为stop)。 74 | 75 | * 后面的两位数字表示处理顺序,数字越小越早处理,所以第一个启动的程序是motd,然后是rpcbing、nfs......数字相同时,则按照程序名的字母顺序启动,所以rsyslog会先于sudo启动。 76 | 77 | # 4.加载开机启动顺序 78 | 79 | 七种预设的"运行级别"各自有一个目录,存放需要开机启动的程序。如果多个"运行级别"需要启动同一个程序,那么这个程序的启动脚本,就会在每一个目录里都有一个拷贝。这样会造成管理上的困扰:如果要修改启动脚本,岂不是每个目录都要改一遍? 80 | 81 | Linux的解决办法,就是七个 /etc/rcN.d 目录里列出的程序,都设为`链接文件`,指向另外一个目录 /etc/init.d ,真正的启动脚本都统一放在这个目录中。init进程逐一加载开机启动程序,其实就是运行这个目录里的启动脚本。 82 | 83 | 这样做的另一个好处,就是如果你要手动关闭或重启某个进程,直接到目录 /etc/init.d 中寻找启动脚本即可。 84 | 85 | 比如,我要重启Apache服务器,就运行下面的命令: 86 | 87 | $ sudo /etc/init.d/apache2 restart 88 | 89 | /etc/init.d 这个目录名最后一个字母d,是directory的意思,表示这是一个目录,用来与程序 /etc/init 区分。 90 | 91 | # 5.用户登陆 92 | 93 | 用户的登录方式有三种: 94 | 95 | * 命令行登录 96 | 97 | init进程调用getty程序(意为get teletype),让用户输入用户名和密码。输入完成后,再调用login程序,核对密码(Debian还会再多运行一个身份核对程序/etc/pam.d/login)。如果密码正确,就从文件 /etc/passwd 读取该用户指定的shell,然后启动这个shell。 98 | 99 | * ssh登录 100 | 101 | 系统调用sshd程序(Debian还会再运行/etc/pam.d/ssh ),取代getty和login,然后启动shell。 102 | 103 | * 图形界面登录 104 | 105 | init进程调用显示管理器,Gnome图形界面对应的显示管理器为gdm(GNOME Display Manager),然后用户输入用户名和密码。如果密码正确,就读取/etc/gdm3/Xsession,启动用户的会话。 106 | 107 | # 6.进入 login shell 108 | 109 | Debian默认的shell是Bash,它会读入一系列的配置文件,不同的登陆方式加载不同的配置文件 110 | 111 | * 命令行登录:首先读入 /etc/profile,这是对所有用户都有效的配置;然后`依次`寻找下面三个文件,这是针对当前用户的配置。 112 | 113 | ~/.bash_profile 114 | ~/.bash_login 115 | ~/.profile   116 | 117 | `*`需要注意的是,这三个文件只要有一个存在,就不再读入后面的文件了。 118 | 119 | * ssh登录:与命令行登录完全相同。 120 | 121 | * 图形界面登录:只加载 /etc/profile 和 ~/.profile。~/.bash_profile 不管有没有,都不会运行。 122 | 123 | # 7.打开 non-login shell 124 | 125 | 进入 login shell完成后,Linux的启动过程就算结束了。 126 | 127 | 用户进入操作系统以后,常常会再手动开启一个shell。这个shell就叫做 non-login shell,意思是它不同于登录时出现的那个shell,不读取 /etc/profile 和 ~/.profile 等配置文件。 128 | 129 | non-login shell 是用户最常接触的shell,它会读入用户自己的bash配置文件 ~/.bashrc。大多数时候,我们对于bash的定制,都是写在这个文件里面的。 130 | 131 | `*` 如果不启动 non-login shell , ~/.bashrc 照样会运行,文件 ~/.profile 中存在下面的代码 132 | 133 | ~~~ 134 |   if [ -n "$BASH_VERSION" ]; then 135 |     if [ -f "$HOME/.bashrc" ]; then 136 |       . "$HOME/.bashrc" 137 |     fi 138 |   fi 139 | ~~~ 140 | 141 | 因此,只要运行~/.profile文件,~/.bashrc文件就会连带运行,但是如果存在~/.bash_profile文件,那么有可能不会运行~/.profile文件。解决办法是把下面代码写入 ~/.bash_profile,让 ~/.profile 始终能够运行。 142 | 143 | 144 | ~~~ 145 |   if [ -f ~/.profile ]; then 146 |     . ~/.profile 147 |   fi 148 | ~~~ 149 | -------------------------------------------------------------------------------- /_posts/2014-02-23-ip.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: post 3 | title: IP 类型 4 | tags: IP computer 5 | categories: network 6 | published: false 7 | --- 8 | 9 | 10 | # 分类 11 | 12 | ![ip][ip] 13 | 14 | IP通过地址开头判断类型,将以 `0`,`10`,`110` 开头的地址分为ABC三类 15 | 16 | 将ip转换成十进制后 17 | 18 | A类地址第一个字节在 `0 - 127` 之间, 19 | 20 | B类地址第一个字节在 `128 - 191` 之间 21 | 22 | C类地址第一个字节在 `192 - 223` 之间 23 | 24 | 然后剩下的位被分为网络号与主机号,主机有两个特殊的值: 25 | 26 | 主机号全部为`0`的ip代表一个网段 27 | 28 | 主机号全部为`1`的ip代表广播地址,应用程序可以通过这个ip将信息发送到该网段下的所有主机 29 | 30 | 31 | 32 | # 子网掩码 33 | 34 | IP寻址时还需用到子网掩码,子网掩码与IP等长,由连续的1组成 35 | 36 | ip中被子网掩码掩去(对ip进行与运算)的部分将被视为网络号,剩余部分将被视为主机号 37 | 38 | 如需要划分5个子网,其二进制为`101`,这将在ip中占去`3`位,3位可以划分出6(`2 ^ 3 - 2`)个子网满足5个子网的要求 39 | 40 | 一个byte中占去前3位后为`11100000`,该子网掩码十进制为`224` 41 | 42 | 43 | 44 | # 私有地址 45 | 46 | tcp/ip协议中,专门保留了三个IP地址区域作为私有地址,其地址范围如下: 47 | 48 | 10.0.0.0/8:10.0.0.0~10.255.255.255 49 | 50 | 172.16.0.0/12:172.16.0.0~172.31.255.255 51 | 52 | 192.168.0.0/16:192.168.0.0~192.168.255.255 53 | 54 | ip后面的斜线和数字,表示ip的中网络号所占的位数 55 | 56 | 例如172.16.0.0/12表示将前12位全部作为网络号,效果等同于子网掩码 255.240.0.0 57 | 58 | 59 | 60 | 61 | 62 | [ip]: {{"/ip.jpg" | prepend: site.imgrepo }} 63 | -------------------------------------------------------------------------------- /_posts/2014-03-13-resubmit.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: post 3 | title: 防止重复提交的几种办法 4 | tags: resubmit 5 | categories: web 6 | published: false 7 | --- 8 | 9 | ## 重定向 10 | 提交操作完成后,将请求重定向到一个只读操作,此后无论客户端如何刷新页面,发送的都是只读操作的请求。 11 | 12 | ## 副作用 13 | 页面初始化时生成随机码,提交操作后删除随机码,然后在服务端进行判断,如果请求中随机码为空,则认为重复提交。 14 | 15 | ## 令牌 16 | 生成form页面之前在session中保存一个随机生成的token,并写入到form中的隐藏域。 17 | 18 | 然后在提交时进行判断:如果token与session中的相同,则为首次提交,此时删除session中的token,否则一律为非正常提交。 19 | 20 | ## UI 21 | 提交后禁用提交按钮 22 | 23 | ## 数据库 24 | 数据库添加约束,禁止重复数据 25 | -------------------------------------------------------------------------------- /_posts/2014-03-15-file-upload.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: post 3 | title: 解析HTTP, 实现文件上传 4 | tags: file-upload Java Http multi-part 5 | categories: web 6 | published: true 7 | --- 8 | 9 | * TOC 10 | {:toc} 11 | 12 | 本文的目的是简要说明如何编写一个文件上传组件,使他的功能类似 [commons-fileupload][commons-fileupload], 并在结尾处提供了完整代码的获取方式。 13 | 14 | # HTTP 15 | 本文讨论的是基于 HTTP 协议的文件上传,下面先来看看 HTTP 请求的真面目。 16 | 17 | 首先,用 JavaSe 类库中的 Socket 搭建一个超简单的服务器,这个服务器只有一个功能,就是完整地打印整个 HTTP 请求体。 18 | 19 | ~~~java 20 | public class Server { 21 | 22 | private ServerSocket serverSocket; 23 | 24 | public Server() throws IOException{ 25 | serverSocket = new ServerSocket(8080); 26 | } 27 | 28 | public void show() throws IOException{ 29 | while(true){ 30 | Socket socket = serverSocket.accept(); 31 | byte[] buf = new byte[1024]; 32 | InputStream is = socket.getInputStream(); 33 | OutputStream os = new ByteArrayOutputStream(); 34 | int n = 0; 35 | while ((n = is.read(buf)) > -1){ 36 | os.write(buf,0,n); 37 | } 38 | os.close(); 39 | is.close(); 40 | socket.close(); 41 | 42 | System.out.println(os); 43 | } 44 | } 45 | 46 | public static void main(String[] args) throws IOException { 47 | new Server().show(); 48 | } 49 | 50 | } 51 | ~~~ 52 | 53 | 将服务器运行起来之后,在浏览器中输入地址:`http://localhost:8080` 54 | 55 | 在我的机器上,显示如下内容,可以看到,这个一个get请求 56 | 57 | ![http-get][http-get] 58 | 59 | 下面利用一个 html 的 form表单提交 post 请求 60 | 61 | ~~~html 62 |
63 | 64 | 65 | 66 |
67 | ~~~ 68 | 69 | 在我的机器上,显示如下内容 70 | 71 | ![http-post][http-post] 72 | 73 | 注意图中被红色框起来的部分,第一个红框指示了本次请求中,用来分隔不同元素的分隔线。 74 | 75 | 每个元素将以此分隔线作为第一行,后面紧跟对元素的描述,描述与内容用空行分隔。 76 | 77 | 分隔线的后面加两个小短横代表整个请求体结束,即**EOF**。 78 | 79 | 我们需要做的工作,就是利用分隔线,从请求体中分离出每个元素,分析HTTP请求头的工作可以交给Servlet。 80 | 81 | # 分析 82 | 83 | 那么,如何分离呢? 84 | 85 | java中的 `InputStream` 只能读取一次,所以我们想要方便地分析一个流,最直接的办法就是将其缓存下来。 86 | 87 | RandomAccessFile 或许能够满足需求,RandomAccessFile 可以提供一个指针用于在文件中的随意移动,然而需要读写本地文件的方案不会是最优方案。 88 | 89 | 先将整个流读一遍将内容缓存到内存中? 这种方案在多个客户端同时提交大文件时一定是不可靠的。 90 | 91 | 最理想的方案可能是,我只需要读一遍 `InputStream` , 读完后将得到一个有序列表,列表中存放每个元素对象。 92 | 93 | 很明显,JavaSe的流没有提供这个功能 94 | 95 | 我们知道从 `InputStreeam` 中获取内容需要使用 `read` 方法,返回 `-1` 表示读到了流的末尾,如果我们增强一下`read`的功能,让其在读到每个元素末尾的时候返回 `-1`,这样不就可以分离出每个元素了吗,至于判断是否到了整个流的末尾,自有办法。 96 | 97 | 98 | # 设计 99 | 100 | 如何增强`read`方法呢? 101 | 102 | `read`方法要在读到元素末尾时返回`-1` , 一定需要先对已读取的内容进行分析,判断是否元素末尾。 103 | 104 | 我的做法是,内部维护一个buffer,`read`方法在读取时先将字节写入到这个buffer中,然后分析其中是否存在分隔线,然后将buffer中可用的元素复制到客户端提供的buffer。 105 | 106 | 这个内部维护的buffer并不总是满的,其中的字节来自read方法的原始功能,所以我们需要一个变量来记录buffer中有效字节的末尾位置 `tail`。 107 | 108 | 我们还需要一个变量 `pos` 来标记buffer中是否存在分隔线,pos的值即为分隔线的开头在buffer中的位置,如果buffer中不存在分隔线pos的值将为-1。 109 | 110 | 但是问题没这个简单,分隔线在buffer中存在状态有两种情况: 111 | 112 | 情况A,分隔线完好地存在于buffer中,图中的bundary即为分隔线 113 | 114 | ![boundary-A][boundary-A] 115 | 116 | 情况B,分隔线的一部分存在于buffer中 117 | 118 | ![boundary-B][boundary-B] 119 | 120 | 在B情况下,`boundary`有多少字节存在于buffer中是不确定的,而且依靠这些不完整的字节根本无法判断他是否属于boundary开头。 121 | 122 | 例如,buffer中没有发现boundary,但是buffer末尾的3个字节与boundary开头相同,这种情况可能只是巧合,boundary并没有被截断。 123 | 124 | 对于这个问题,有一个解决办法,我们不必检查到buffer末尾,而是在buffer末尾留一个关健区`pad`。 125 | 126 | 这个关健区中很有可能存在被截断boundary,每次检查到`pad`开头时立即收手,此位置之前的数据可以确保没有boundary,在下次填充buffer时,将这个关健区中的数据复制到buffer开头再处理。很显然,关健区`pad`长度应该等于boundary,如图: 127 | 128 | ![pad][pad] 129 | 130 | # 关键代码 131 | 132 | **在buffer中检查boundary** 133 | 134 | ~~~java 135 | private int findSeparator() { 136 | int first; 137 | int match = 0; 138 | //若buffer中head至tail之间的字节数小于boundaryLength,那么maxpos将小于head,循环将不会运行,返回值为-1 139 | int maxpos = tail - boundaryLength; 140 | for (first = head; first <= maxpos && match != boundaryLength; first++) { 141 | first = findByte(boundary[0], first); 142 | if (first == -1 || first > maxpos) { 143 | return -1; 144 | } 145 | for (match = 1; match < boundaryLength; match++) { 146 | if (buffer[first + match] != boundary[match]) { 147 | break; 148 | } 149 | } 150 | } 151 | if (match == boundaryLength) { 152 | return first - 1; 153 | } 154 | return -1; 155 | } 156 | ~~~ 157 | 158 | 159 | **填充buffer** 160 | 161 | ~~~java 162 | private int makeAvailable() throws IOException { 163 | //该方法在available返回0时才会被调用,若pos!=-1那pos==head,表示boundary处于head位,可用字节数为0 164 | if (pos != -1) { 165 | return 0; 166 | } 167 | 168 | // 将pad位之后的数据移动到buffer开头 169 | total += tail - head - pad; 170 | System.arraycopy(buffer, tail - pad, buffer, 0, pad); 171 | 172 | // 将buffer填满 173 | head = 0; 174 | tail = pad; 175 | //循环读取数据,直至将buffer填满,在此过程中,每次读取都将检索buffer中是否存在boundary,无论存在与否,都将即时返回可用数据量 176 | for (;;) { 177 | int bytesRead = input.read(buffer, tail, bufSize - tail); 178 | if (bytesRead == -1) { 179 | //理论上因为会对buffer不断进行检索,读到boundary时就会return 0,read方法将返回 -1, 180 | //所以不会读到input末尾,如果运行到了这里,表示发生了错误. 181 | final String msg = "Stream ended unexpectedly"; 182 | throw new RuntimeException(msg); 183 | } 184 | if (notifier != null) { 185 | notifier.noteBytesRead(bytesRead); 186 | } 187 | tail += bytesRead; 188 | findSeparator(); 189 | //若buffer中的数据量小于keepRegion(boundaryLength),av将必定等于0,循环将继续,直至数据量大于或等于keepRegion(boundaryLength). 190 | //此时将检索buffer中是否包含boundary,若包含,将返回boundary所在位置pos之前的数据量,若不包含,将返回pad位之前的数据量 191 | int av = available(); 192 | 193 | if (av > 0 || pos != -1) { 194 | return av; 195 | } 196 | } 197 | } 198 | ~~~ 199 | 200 | **强化后的read方法** 201 | 202 | ~~~java 203 | @Override 204 | public int read(byte[] b, int off, int len) throws IOException { 205 | if (closed) { 206 | throw new RuntimeException("the stream is closed"); 207 | } 208 | if (len == 0) { 209 | return 0; 210 | } 211 | int res = available(); 212 | if (res == 0) { 213 | res = makeAvailable(); 214 | if (res == 0) { 215 | return -1; 216 | } 217 | } 218 | res = Math.min(res, len); 219 | System.arraycopy(buffer, head, b, off, res); 220 | head += res; 221 | total += res; 222 | return res; 223 | } 224 | ~~~ 225 | 226 | # 源码获取 227 | 228 | 我已经按照这套想法完整地实现了文件上传组件 229 | 230 | 有兴趣的朋友可以从我的Gighub获取源码 [点我获取][github] 231 | 232 | 使用方法:[点我查看][use] 233 | 234 | 235 | 236 | 237 | 238 | 239 | [http-get]: {{"/http-get.png" | prepend: site.imgrepo }} 240 | [http-post]: {{"/http-post.png" | prepend: site.imgrepo }} 241 | [boundary-A]: {{"/boundary-A.png" | prepend: site.imgrepo }} 242 | [boundary-B]: {{"/boundary-B.png" | prepend: site.imgrepo }} 243 | [pad]: {{"/boundary-C.png" | prepend: site.imgrepo }} 244 | [github]: https://github.com/bit-ranger/http-multipart/releases 245 | [use]: https://github.com/bit-ranger/http-multipart 246 | [commons-fileupload]: https://github.com/apache/commons-fileupload -------------------------------------------------------------------------------- /_posts/2014-03-16-tomcat-https.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: post 3 | title: Tomcat Https 配置 4 | tags: tomcat Http Https ssl Java 5 | categories: Java 6 | published: false 7 | --- 8 | 9 | #### `1`.生成秘钥库 10 | 11 | ~~~bash 12 | keytool 13 | –genkey 14 | –keyalg 算法(通常用RSA) 15 | –dname "cn=服务器名, 16 | ou=组织单位名, 17 | o=组织名, 18 | l=城市名, 19 | st=省/市/自治区, 20 | c=国家双字母代码" 21 | -alias 别名(非必选项) 22 | -keypass 密码 23 | -keystore 秘钥库文件名 24 | -storepass 密码 25 | -validity 有效天数 26 | ~~~ 27 | 28 | #### `2`.生成浏览器证书文件 29 | 30 | ~~~bash 31 | keytool 32 | -export 33 | -keystore 秘钥库文件名 34 | -alias 秘钥库别名(非必选项) 35 | -storepass 秘钥库密码 36 | -file 证书文件名 37 | ~~~ 38 | 39 | #### `3`.生成私钥文件 40 | 41 | ~~~bash 42 | keytool 43 | -importkeystore 44 | -srckeystore 秘钥库文件名 45 | -deststoretype PKCS12 46 | -destkeystore p12文件名 47 | 48 | openssl 49 | pkcs12 50 | -in p12文件名 51 | -out pem文件名 52 | -nodes 53 | ~~~ 54 | 55 | #### `4`.Tomcat配置 56 | 57 | 打开 %CATALINA_HOME%/conf/server.xml 58 | 59 | 解开注释或添加代码(443为https默认端口,不会在URL中显示) 60 | 61 | ~~~bash 62 | 67 | ~~~ 68 | 69 | #### `5`.强制https访问 70 | 71 | 打开 %CATALINA_HOME%/conf/web.xml 72 | 73 | 添加代码 74 | 75 | ~~~xml 76 | 77 | 78 | 79 | CLIENT-CERT 80 | Client Cert Users-only Area 81 | 82 | 83 | 84 | 85 | SSL 86 | /* 87 | 88 | 89 | CONFIDENTIAL 90 | 91 | 92 | ~~~ 93 | -------------------------------------------------------------------------------- /_posts/2014-03-22-reentrantlock.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: post 3 | title: 可重入锁 4 | tags: Java Concurrent Lock 5 | categories: Java 6 | published: false 7 | --- 8 | 9 | Java 中的锁是可重入的,当线程试图获得它自己占有的锁时,请求会成功。 10 | 11 | 这样使用`synchronized`并不会造成死锁 12 | 13 | ~~~java 14 | public synchronized void test(){ 15 | //do something 16 | synchronized(this) { 17 | //do something 18 | synchronized (this){ 19 | //do something 20 | synchronized (this){ 21 | //do something 22 | } 23 | } 24 | } 25 | } 26 | ~~~ 27 | 28 | 这是因为java线程是基于 “每线程(per-thread)”,而不是基于“每调用的(per-invocation)” 的,也就是说java为每个线程分配一个锁,而不是为每次调用分配一个锁。 29 | 30 | 重入的实现是通过为每个锁关联一个请求计数和一个占有它的线程。当请求计数器为0时,这个锁可以被认为是未占用的,当一个线程请求一个未占用的锁时,JVM记录锁的拥有者,并把锁的请求计数加1,如果同一个线程再次请求这个锁时,请求计数器就会增加,当该线程退出`syncronized`块时,计数器减1,当计数器为0时,锁被释放。 -------------------------------------------------------------------------------- /_posts/2014-04-05-encode-decode.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: post 3 | title: 乱码现象分析 4 | tags: servlet encode Java 5 | categories: web 6 | --- 7 | 8 | 我们知道,web浏览器会将form中的内容打包成HTTP请求体,然后发送到服务端,服务端对请求体解析后可以得到传递的数据。这当中包含两个过程:`encode`与`decode`。 9 | 10 | * TOC 11 | {:toc} 12 | 13 | 14 | # HTTP 15 | 16 | 我们使用ServerSocket搭建一个小服务器来看清http请求的全貌, 该服务器只有一个功能, 就是打印请求体。 17 | 18 | ~~~java 19 | public class HttpPrint { 20 | private ServerSocket serverSocket; 21 | 22 | public HttpPrint() throws IOException { 23 | serverSocket = new ServerSocket(8080); 24 | } 25 | 26 | public void show() throws IOException{ 27 | while(true){ 28 | Socket socket = serverSocket.accept(); 29 | byte[] buf = new byte[1024]; 30 | InputStream is = socket.getInputStream(); 31 | ByteArrayOutputStream os = new ByteArrayOutputStream(); 32 | int n = 0; 33 | while ((n = is.read(buf)) > -1){ 34 | os.write(buf,0,n); 35 | } 36 | os.close(); 37 | is.close(); 38 | socket.close(); 39 | System.out.println(os); 40 | } 41 | } 42 | 43 | public static void main(String[] args) throws IOException { 44 | new HttpPrint().show(); 45 | } 46 | } 47 | ~~~ 48 | 49 | 用html页面来发送get与post请求 50 | 51 | ~~~html 52 | Test 53 |
54 | 55 | 56 |
57 | ~~~ 58 | 59 | 启动服务器后,查看打印内容,在我的机器上,请求内容如下: 60 | 61 | get 62 | 63 | ![get][get] 64 | 65 | post 66 | 67 | ![post][post] 68 | 69 | 从post中的`Content-Type:application/x-www-form-urlencoded`可以看到,虽然数据为中文,但是在传递的时候,经过了一次urlEncode,这样一来,在数据交换层面就可以屏蔽编码的不一致性。 70 | 71 | # UrlEncode 72 | 73 | `urlEncode`的任务是将form中的数据进行编码, 编码过程非常简单, 任何字符只要不是`ASCII`码, 它们都将被转换成字节形式, 每个字节都写成这种形式:一个 "%" 后面跟着两位16进制的数值。 74 | urlEncode只能识别ASCII码,可以想象的是,那些urlEncode不能识别的字符,也就是十六进制数,一定是依赖于特定的字符集产生的, 字符集包括unicode,iso等。 75 | 76 | 那么浏览器用的是什么字符集呢? 答案是:默认与`contentType`相同, form可以通过属性`accept-charset`指定。 77 | 78 | 例如我们通常可以在jsp中看到这样的设置: 79 | 80 | ~~~jsp 81 | <%@page contentType="text/html;charset=UTF-8" %> 82 | ~~~ 83 | 84 | 或者在html中这样设置: 85 | 86 | ~~~html 87 | 88 | ~~~ 89 | 90 | 这表示浏览器得到响应流之后,用contentType指定的字符集,将流中的字节转换为字符,同样地,也会用这个字符集将页面中字符转换为字节。 91 | 92 | 关于浏览器设定字符集的问题,我们不过多讨论,现在只需要知道有这么个过程就行了, 需要注意的是,无论浏览器使用什么字符集,服务端都是无法获知的。 93 | 这里需要换位考虑一下,浏览器是一个客户端,应该让客户端 "迁就" 服务端, 所以浏览器请求一个服务的时候,应该让浏览器考虑服务端支持什么字符集, 得到了响应后, 用服务端告诉浏览器的字符集进行解析。 94 | 95 | 96 | # UrlDecode 97 | 98 | 现在我们将目光转向Servlet, 并使用上面的html来请求服务,请确保请求的字符集为`unicode`, 应用服务器使用tomcat6。 99 | 100 | ~~~java 101 | public class HttpServletPrint extends HttpServlet{ 102 | @Override 103 | protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 104 | System.out.println(req.getParameter("param")); 105 | } 106 | 107 | @Override 108 | protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 109 | this.doPost(req,resp); 110 | } 111 | } 112 | ~~~ 113 | 114 | get与post结果如下,果然如预料中乱码了。 115 | 116 | ![param][param] 117 | 118 | 现在我们从Servlet中看看请求体, 修改上面的Servlet代码如下: 119 | 120 | ~~~java 121 | public class HttpServletPrint extends HttpServlet{ 122 | @Override 123 | protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 124 | //System.out.println(req.getParameter("param")); 125 | byte[] buf = new byte[1024]; 126 | int n = 0; 127 | ByteArrayOutputStream bos = new ByteArrayOutputStream(); 128 | InputStream is = req.getInputStream(); 129 | while ((n = is.read(buf, 0, buf.length))>-1){ 130 | bos.write(buf, 0, n); 131 | } 132 | String param = bos.toString(); 133 | String s = URLDecoder.decode(param,"utf-8"); 134 | System.out.println(s); 135 | } 136 | 137 | @Override 138 | protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 139 | this.doPost(req,resp); 140 | } 141 | } 142 | ~~~ 143 | 144 | get与post结果如下,Servlet将http头部解析完成后,将请求体留了下来供应用程序使用, 这是考虑到http请求可能有多种 [enctype][enctype] , 请求体的结构可能不同, 145 | 例如,`multipart/form-data`就不是这样的key=value结构,关于multipart/form-data,我在这篇 [fileupload][fileupload] 中曾有过简要分析。 146 | 147 | ![body-utf][body-utf] 148 | 149 | 从图中可以看到, 设置了正确的字符集后, 服务端将能够正确地解析, get部分什么都没有,这是因为get没有请求体。 150 | 151 | 让我们换种字符集试试, 比如, 将servet中的"utf-8"换成"iso-8859-1"。 152 | 153 | ![body-iso][body-iso] 154 | 155 | 嘿,果然如我所料,而且这个字符串好像很眼熟,和req.getParameter("param")结果是一样的,没错,事实上,tomcat默认的字符集就是`iso-8859-1`, 我们从中可以得到一个推论,tomcat使用默认的字符集,对http请求进行过一次decode。 156 | 157 | 158 | 159 | # 方案 160 | 161 | `urlDecode`的任务是将请求中的百分号码转换成字符,显而易见的是,使用与`urlEncode`时相同的字符集才能成功转换。通常的做法是,让服务端支持涵盖多国语言的"utf-8",然后让客户端也用"utf-8"请求服务。 162 | 163 | 指定服务端字符集的方式有两种,一是修改应用服务器的默认编码,二是添加一个过滤器进行编码转换, 方法一最方便, 但是影响了程序的可移植性, 方法二可移植, 它只需要做一件事:`requet.setCharacterEncoding("UTF-8");`, 164 | 实际上,该过滤器并没有进行任何编码转换的工作,它仅仅只是一个配置,该配置项将被后续程序使用,这些后续程序包括web服务器内置的解析程序,以及第三方解析工具等。 165 | 166 | 需要注意的是,requet.setCharacterEncoding("UTF-8");,只对请求体有效,也就是说,请求头不归它管,而是由web服务器采用自己配置的字符编码进行解析,此时如果url中包含中文(如get请求的参数),那么将不可避免地出现字符丢失。 167 | 解决办法是在客户端对url进行`encodeURI`**两次**, 然后再在服务端`URLDecoder.decode(param,"utf-8");`。 168 | 169 | 为什么要 [encodeURI][encodeURI-link] 两次?talk is cheap, let's code! 170 | 171 | ![encodeURI][encodeURI] 172 | 173 | 注意观察这张图片,从中发现了什么? 没错,第一次encodeURI生成了HTTP一节的示例中一样的结果。 174 | 我们在浏览器窗口中输入 "http://localhost:8080/hsp?param=%E4%BD%A0%E5%A5%BD%E5%85%A8%E4%B8%96%E7%95%8C", 会发现它变成了 "http://localhost:8080/hsp?param=你好全世界", 175 | 在url里,浏览器认为%是个转义字符,浏览器会把%与%之间的编码,两位两位取出后进行decode, 也就是变回 "你好全世界", 然后再用这个url发送请求, 最终实际发送的内容实际上还是`%E4%BD%A0%E5%A5%BD%E5%85%A8%E4%B8%96%E7%95%8C`。 176 | 换言之,以明文传递的这种url会被浏览器否决一次,再换言之,在js中进行一次encodeURI等于什么都没做。 177 | 178 | 再注意观察第2和第3个输出,有什么规律? 是的,从第二次开始encodeURI只是将`%`变成了`%25`, 179 | 根据我们刚才总结出的规律可知,在encodeURI两次的情况下,最后发送到浏览器中的数据为`%25E4%25BD%25A0%25E5%25A5%25BD%25E5%2585%25A8%25E4%25B8%2596%25E7%2595%258C`, 180 | 理所当然的,web服务器将使用默认的字符集对其decode, 然而, 无论选择哪种字符集, 将`%25`转换成`%`总是不会出错的, decode之后,`%E4%BD%A0%E5%A5%BD%E5%85%A8%E4%B8%96%E7%95%8C` 将完整地送到Servlet手上。 181 | 182 | ~~~java 183 | System.out.println(URLDecoder.decode(req.getParameter("param"),"utf-8")); 184 | ~~~ 185 | 186 | ~~~javascript 187 | window.location.href="http://localhost:8080/hsp?param=" + encodeURI(encodeURI('你好全世界')); 188 | ~~~ 189 | 190 | ![world][world] 191 | 192 | [get]: {{"/servlet-encode/get.png" | prepend: site.imgrepo }} 193 | [post]: {{"/servlet-encode/post.png" | prepend: site.imgrepo }} 194 | [param]: {{"/servlet-encode/param.png" | prepend: site.imgrepo }} 195 | [body-utf]: {{"/servlet-encode/body-utf.png" | prepend: site.imgrepo }} 196 | [enctype]: http://www.w3school.com.cn/tags/att_form_enctype.asp 197 | [body-iso]: {{"/servlet-encode/body-iso.png" | prepend: site.imgrepo }} 198 | [fileupload]: https://bit-ranger.github.io/blog/web/2014-03-15/file-upload 199 | [encodeURI]: {{"/servlet-encode/encodeURI.png" | prepend: site.imgrepo }} 200 | [encodeURI-link]: http://www.w3school.com.cn/jsref/jsref_encodeuri.asp 201 | [world]: {{"/servlet-encode/world.png" | prepend: site.imgrepo }} -------------------------------------------------------------------------------- /_posts/2014-04-09-algorithm-gist.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: post 3 | title: 算法小汇 4 | tags: 三色旗 汉诺塔 斐波那契数列 骑士周游 算法 algorithm 5 | categories: algorithm 6 | published: false 7 | --- 8 | 9 | * TOC 10 | {:toc} 11 | 12 | # 三色旗 13 | 14 | 问题描述:一条绳子上悬挂了一组旗帜,旗帜分为三种颜色,现在需要把旗帜按顺序将相同的颜色的放在一起,没有旗帜的临时存放点,只能在绳子上操作,每次只能交换两个旗帜 15 | 16 | 例如:原本旗帜的顺序为`rwbrbwwrbwbrbwrbrw`需要变成`bbbbbbwwwwwwrrrrrr` 17 | 18 | 解决思路:遍历元素,如果元素该放到左边就与左边交换,该放到右边就与右边交换,左右边界动态调整,剩余的正好属于中间 19 | 20 | ~~~java 21 | public class Tricolour { 22 | 23 | private final static String leftColor = "b"; 24 | private final static String rightColor = "r"; 25 | 26 | public void tricolour(Object[] src){ 27 | //中间区域的左端点 28 | int mli = 0; 29 | //中间区域的右端点 30 | int mri = src.length-1; 31 | //遍历元素,从mli开始到mri结束,mli与mri会动态调整,但是i比mli变化快 32 | for (int i = mli; i <= mri; i++) { 33 | //如果当前元素属于左边 34 | if(isPartOfLeft(src[i])){ 35 | //将当前元素换与中间区域左端点元素互换 36 | swap(src,mli,i); 37 | //mli位的元素是经过处理的,一定不是红色,所以不需要再分析i位新元素 38 | //左端点右移 39 | mli++; 40 | } 41 | //如果当前元素属于右边 42 | else if(isPartOfRight(src[i])){ 43 | //从中间区域的右端点开始向左扫描元素 44 | while (mri > i) { 45 | //如果扫描到的元素属于右边,右端点左移,继续向左扫描,否则停止扫描 46 | if(isPartOfRight(src[mri])){ 47 | mri--; 48 | } else { 49 | break; 50 | } 51 | } 52 | //将当前元素交换到中间区域右端点 53 | swap(src,mri,i); 54 | //右端点左移 55 | mri--; 56 | //mri位的元素可能是蓝色的,需要再分析i位新元素 57 | i--; 58 | } 59 | } 60 | } 61 | 62 | private boolean isPartOfLeft(Object item){ 63 | return leftColor.equals(item); 64 | } 65 | 66 | private boolean isPartOfRight(Object item){ 67 | return rightColor.equals(item); 68 | } 69 | 70 | private void swap(Object[] src, int fst, int snd){ 71 | Object tmp = src[fst]; 72 | src[fst] = src[snd]; 73 | src[snd] = tmp; 74 | } 75 | 76 | public static void main(String[] args) { 77 | String[] flags = 78 | new String[]{"r","b","w","w","b","r","r","w","b","b","r","w","b"}; 79 | new Tricolour().tricolour(flags); 80 | for (String flag : flags) { 81 | System.out.printf("%s,",flag); 82 | } 83 | } 84 | } 85 | ~~~ 86 | 87 | # 汉诺塔 88 | 89 | ~~~java 90 | public class Hanoi { 91 | 92 | private void move(int n, char a, char b, char c){ 93 | if(n == 1){ 94 | System.out.printf("盘%d由%s移动到%s\n",n,a,c); 95 | } else{ 96 | move(n-1,a,c,b); 97 | System.out.printf("盘%d由%s移动到%s\n",n,a,c); 98 | move(n-1,b,a,c); 99 | } 100 | } 101 | 102 | public static void main(String[] args) { 103 | new Hanoi().move(5, 'A','B','C'); 104 | } 105 | } 106 | ~~~ 107 | 108 | # 斐波那契数列 109 | 110 | ~~~java 111 | public class Fibonacci { 112 | 113 | private int[] src ; 114 | 115 | public int fibonacci(int n){ 116 | if(src == null){ 117 | src = new int[n+1]; 118 | } 119 | if(n == 0 || n==1){ 120 | src[n] = n; 121 | return n; 122 | } 123 | src[n] = fibonacci(n-1) + fibonacci(n-2); 124 | return src[n]; 125 | } 126 | 127 | public int fibonacci2(int n){ 128 | if(src == null){ 129 | src = new int[n+1]; 130 | } 131 | src[0] = 0; 132 | src[1] = 1; 133 | for (int i = 2; i < src.length; i++) { 134 | src[i] = src[i-1] + src[i-2]; 135 | } 136 | return src[n]; 137 | } 138 | 139 | public static void main(String[] args) { 140 | Fibonacci fib = new Fibonacci(); 141 | int n = fib.fibonacci(20); 142 | System.out.println(n); 143 | for (int i : fib.src) { 144 | System.out.println(i); 145 | } 146 | } 147 | } 148 | ~~~ 149 | 150 | # 骑士周游 151 | 152 | 骑士只能按照如图所示的方法前进,且每个格子只能路过一次,现在指定一个起点,判断骑士能否走完整个棋盘. 153 | 154 | 思路:对任意一个骑士所在的位置,找出其所有可用的出口,若无可用出口则周游失败,再对每个出口找出其可用的子出口,然后骑士移动至子出口最少的出口处,重复以上过程. 155 | 156 | ![](http://img.blog.csdn.net/20130710174118812?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvamlhamlheW91YmE=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) 157 | 158 | ~~~java 159 | import java.awt.Point; 160 | import static java.lang.System.out; 161 | 162 | public class KnightTour { 163 | 164 | // 对应骑士可走的八個方向 165 | private final static Point[] relatives = new Point[]{ 166 | new Point(-2,1), 167 | new Point(-1,2), 168 | new Point(1,2), 169 | new Point(2,1), 170 | new Point(2,-1), 171 | new Point(1,-2), 172 | new Point(-1,-2), 173 | new Point(-2,-1) 174 | }; 175 | 176 | private final static int direct = 8; 177 | 178 | public KnightTour(int sideLen){ 179 | this.sideLen = sideLen; 180 | board = new int[sideLen][sideLen]; 181 | } 182 | 183 | private int sideLen; 184 | 185 | private int[][] board; 186 | 187 | public boolean travel(int startX, int startY) { 188 | 189 | // 当前出口的位置集 190 | Point[] nexts; 191 | 192 | // 记录每个出口的可用出口个数 193 | int[] exits; 194 | 195 | //当前位置 196 | Point current = new Point(startX, startY); 197 | 198 | board[current.x][current.y] = 1; 199 | 200 | //当前步的编号 201 | for(int m = 2; m <= Math.pow(board.length, 2); m++) { 202 | 203 | //清空每个出口的可用出口数 204 | nexts = new Point[direct]; 205 | exits = new int[direct]; 206 | 207 | int count = 0; 208 | // 试探八个方向 209 | for(int k = 0; k < direct; k++) { 210 | int tmpX = current.x + relatives[k].x; 211 | int tmpY = current.y + relatives[k].y; 212 | 213 | // 如果这个方向可走,记录下来 214 | if(accessable(tmpX, tmpY)) { 215 | nexts[count] = new Point(tmpX,tmpY); 216 | // 可走的方向加一個 217 | count++; 218 | } 219 | } 220 | 221 | //可用出口数最少的出口在exits中的索引 222 | int minI = 0; 223 | if(count == 0) { 224 | return false; 225 | } else { 226 | // 记录每个出口的可用出口数 227 | for(int l = 0; l < count; l++) { 228 | for(int k = 0; k < direct; k++) { 229 | int tmpX = nexts[l].x + relatives[k].x; 230 | int tmpY = nexts[l].y + relatives[k].y; 231 | if(accessable(tmpX, tmpY)){ 232 | exits[l]++; 233 | } 234 | } 235 | } 236 | 237 | // 从可走的方向中寻找最少出路的方向 238 | int tmp = exits[0]; 239 | for(int l = 1; l < count; l++) { 240 | if(exits[l] < tmp) { 241 | tmp = exits[l]; 242 | minI = l; 243 | } 244 | } 245 | } 246 | 247 | // 走最少出路的方向 248 | current = new Point(nexts[minI]); 249 | board[current.x][current.y] = m; 250 | } 251 | 252 | return true; 253 | } 254 | 255 | private boolean accessable(int x, int y){ 256 | return x >= 0 && y >= 0 && x < sideLen && y < sideLen && board[x][y] == 0; 257 | } 258 | 259 | public static void main(String[] args) { 260 | int sideLen = 9; 261 | KnightTour knight = new KnightTour(sideLen); 262 | 263 | if(knight.travel(4,2)) { 264 | out.println("游历完成!"); 265 | } else { 266 | out.println("游历失败!"); 267 | } 268 | 269 | for(int y = 0; y < sideLen; y++) { 270 | for(int x = 0; x < sideLen; x++) { 271 | out.printf("%3d ", knight.board[x][y]); 272 | } 273 | out.println(); 274 | } 275 | } 276 | } 277 | ~~~ 278 | 279 | # 帕斯卡三角 280 | 281 | ~~~java 282 | public class Pascal extends JFrame{ 283 | 284 | private final int maxRow; 285 | 286 | Pascal(int maxRow){ 287 | setBackground(Color.white); 288 | setTitle("帕斯卡三角"); 289 | setSize(520, 350); 290 | setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 291 | setVisible(true); 292 | this.maxRow = maxRow; 293 | } 294 | 295 | /** 296 | * 获取值 297 | * @param row 行号 从0开始 298 | * @param col 列号 从0开始 299 | * @return 300 | */ 301 | private int value(int row,int col){ 302 | int v = 1; 303 | for (int i = 1; i < col; i++) { 304 | v = v * (row - i + 1) / i; 305 | } 306 | return v; 307 | } 308 | 309 | @Override 310 | public void paint(Graphics g) { 311 | int r, c; 312 | for(r = 0; r <= maxRow; r++) { 313 | for(c = 0; c <= r; c++){ 314 | g.drawString(String.valueOf(value(r, c)), (maxRow - r) * 20 + c * 40, r * 20 + 50); 315 | } 316 | } 317 | } 318 | 319 | public static void main(String[] args) { 320 | new Pascal(12); 321 | } 322 | } 323 | ~~~ 324 | -------------------------------------------------------------------------------- /_posts/2014-05-07-linux-job.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: post 3 | title: Linux计划任务 4 | tags: 计划任务 Linux 5 | categories: Linux 6 | published: false 7 | --- 8 | 9 | * TOC 10 | {:toc} 11 | 12 | 13 | ## `at` 14 | 15 | at 是个可以处理**仅运行一次**就结束计划任务的命令,不过要运行 at 时, 必须要有 atd 这个服务的支持才行。 16 | 17 | [root@www ~]# `/etc/init.d/atd restart` 18 | 19 | 使用 at 这个命令来产生所要运行的任务,并将任务以文件的方式写入 `/var/spool/at` 目录内,该任务便能被 atd 服务运行 20 | 21 | >对at进行权限控制 22 | > 23 | >* 先找寻 `/etc/at.allow` 这个文件,写在这个文件中的使用者才**能**使用 at ,没有在这个文件中的使用者则不能使用 at (即使没有写在 at.deny 当中); 24 | > 25 | >* 如果 /etc/at.allow 不存在,就寻找 `/etc/at.deny` 这个文件,写在这个 at.deny 的使用者**不能**使用 at ,而没有在这个 at.deny 文件中的使用者,就可以使用 at 咯; 26 | > 27 | >* 如果两个文件都不存在,那么只有 root 可以使用 at 命令。 28 | 29 | [root@www ~]# `at [-mldv] TIME` 30 | 31 | [root@www ~]# `at -c 任务号码` 32 | 33 | 选项与参数: 34 | 35 | `-m` :当 at 的任务完成后,即使没有输出信息,亦以 email 通知使用者该任务已完成。 36 | 37 | `-l` :at -l 相当於 `atq`,列出目前系统上面的所有该使用者的 at 任务; 38 | 39 | `-d` :at -d 相当於 `atrm` ,可以取消一个在 at 排程中的任务; 40 | 41 | `-v` :可以使用较明显的时间格式列出 at 排程中的任务列表; 42 | 43 | `-c` :可以列出后面接的该项工作的实际命令内容。 44 | 45 | >TIME:时间格式,这里可以定义出『什么时候要进行 at 这项工作』的时间,格式有: 46 | > 47 | >* `HH:MM` 48 | > 49 | > ex> 04:00 50 | > 51 | > 在今日的 HH:MM 时刻进行,若该时刻已超过,则明天的 HH:MM 进行此工作。 52 | > 53 | >* `HH:MM YYYY-MM-DD` 54 | > 55 | > ex> 04:00 2009-03-17 56 | > 57 | > 强制在某年某月某日的某时刻进行! 58 | > 59 | >* `HH:MM[am|pm] [Month] [Date]` 60 | > 61 | > ex> 04pm March 17 62 | > 63 | > 强制在某年某月某日的某时刻进行! 64 | > 65 | >* `HH:MM[am|pm] + number [minutes|hours|days|weeks]` 66 | > 67 | > ex> now + 5 minutes ex> 04pm + 3 days 68 | > 69 | > 在某个时间点『再加几个时间后』才进行。 70 | 71 | `!`at命令会进入atshell环境,使用绝对路径输入指令不易出现问题 72 | 73 | `!`at 的运行与终端机环境无关,想要查看io结果,需要指定终端`echo "Hello" > /dev/tty1` 74 | 75 | `!`at 的运行独立于bash,所以at任务可以离线运行 76 | 77 | **`batch`**:系统有空(负载低于0.8 )时才进行背景任务,用法与at完全相同,本质上是是at命令加一些控制参数 78 | 79 | 80 | 81 | ## `crontab` 82 | 83 | crontab 这个命令所配置的工作将会循环的**一直进行**下去, 可编辑 /etc/crontab 来支持,让 crontab 生效的服务是 `crond` 84 | 85 | [root@www ~]# ·`/etc/init.d/crond restart` 86 | 87 | 当使用者使用 crontab 这个命令来创建工作排程之后,该项工作就会被纪录到 `/var/spool/cron/`目录内 88 | 89 | cron 运行的每一项工作都会被纪录到 `/var/log/cron` 内 90 | 91 | >权限控制(与at相同,默认保留/etc/cron.deny ) 92 | > 93 | >* `/etc/cron.allow`:将可以使用 crontab 的帐号写入其中,若不在这个文件内的使用者则不可使用 crontab; 94 | > 95 | >* `/etc/cron.deny`:将不可以使用 crontab 的帐号写入其中,若未记录到这个文件当中的使用者,就可以使用 crontab 96 | 97 | [root@www ~]# `crontab [-u username] [-l|-e|-r]` 98 | 99 | 选项与参数: 100 | 101 | `-u` :只有 root 才能进行这个任务,亦即帮其他使用者创建/移除 crontab 工作排程; 102 | 103 | `-e` :编辑 crontab 的工作内容 104 | 105 | `-l` :查阅 crontab 的工作内容 106 | 107 | `-r` :移除所有的 crontab 的工作内容,若仅要移除一项,请用 -e 去编辑。 108 | 109 | [dmtsai@www ~]$ `crontab -e` 110 | 111 | >`0 12 * * * ` 112 | > 113 | >`分 时 日 月 周 <命令串>` 114 | 115 | 特殊字符 116 | 117 | `*`代表任何时刻都接受的意思 118 | 119 | `,`代表分隔时段的意思 120 | 121 | `-`代表一段时间范围内 122 | 123 | `/n`代表每间隔n单位 124 | 125 | 系统计划任务(不受crontab -e命令管理) 126 | 编辑`/etc/crontab`文件,该文件每1分钟被检查一次 127 | 128 | 129 | ## `anacron` 130 | 131 | 侦测系统未进行的 crontab 任务(例如 crontab 设置在周末运行,但是周末关机了) 132 | 133 | [root@www ~]# `anacron [-sfn] [job]..` 134 | 135 | [root@www ~]# `anacron -u [job]..` 136 | 137 | 选项与参数: 138 | 139 | `-s` :开始一连续的运行各项工作 (job),会依据时间记录档的数据判断是否进行; 140 | 141 | `-f` :强制进行,而不去判断时间记录档的时间戳记; 142 | 143 | `-n` :立刻进行未进行的任务,而不延迟 (delay) 等待时间; 144 | 145 | `-u` :仅升级时间记录档的时间戳记,不进行任何工作。 146 | 147 | `job` :由 **/etc/anacrontab** 定义的各项工作名称。 148 | 149 | 配置文件位于`/etc/anacrontab` 150 | 151 | >[root@www ~]# cat /etc/anacrontab 152 | > 153 | >SHELL=/bin/sh 154 | > 155 | >PATH=/sbin:/bin:/usr/sbin:/usr/bin 156 | > 157 | >MAILTO=root 158 | 159 | >1 65 cron.daily run-parts /etc/cron.daily 160 | > 161 | >7 70 cron.weekly run-parts /etc/cron.weekly 162 | > 163 | >30 75 cron.monthly run-parts /etc/cron.monthly 164 | 165 | 天数 延迟时间(分) 工作名称定义 实际要进行的命令串 166 | 167 | 任务文件位于`/var/spool/anacron/` -------------------------------------------------------------------------------- /_posts/2014-05-12-acturl-generic.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: post 3 | title: 超类中的泛型 4 | tags: generic type Java 5 | categories: java 6 | published: false 7 | --- 8 | 9 | 为了减少工作量,开发者往往喜欢将相同的特性放入超类,通过继承实现代码共享 10 | 11 | 例如web项目中常见的`BaseDao`,并使用泛型对参数与返回值类型进行约束 12 | 13 | 某些情况下,我们可能需要获得真实的类型参数,也就是`T`的真实类型 14 | 15 | 首先,沿着继承链向上追溯,找到传入类型参数的位置,并获取存储类型参数的对象 16 | 17 | ~~~java 18 | private static ParameterizedType getParameterizedType(Class clazz){ 19 | Type st = clazz.getGenericSuperclass(); 20 | if(st == null){ 21 | return null; 22 | } 23 | if(st instanceof ParameterizedType){ 24 | ParameterizedType pt = (ParameterizedType)st; 25 | return pt; 26 | } else{ 27 | return getParameterizedType(clazz.getSuperclass()); 28 | } 29 | } 30 | ~~~ 31 | 32 | 类的参数数量是不定的,如 `Map` 定义了两个,`List` 只定义了一个,所以类型参数是个列表,获取真实类型时需要指定要取哪一个 33 | 34 | ~~~java 35 | private static Class getClass(ParameterizedType pt, int index){ 36 | Type param = pt.getActualTypeArguments()[index]; 37 | if(param instanceof Class){ 38 | return (Class)param; 39 | } else { 40 | return null; 41 | } 42 | } 43 | ~~~ 44 | 45 | 通常情况下,到这里已经可以取到真实类型了,但是上面的代码忽略了一些不正常情况 46 | 47 | **情况一:** 48 | 49 | 超类定义了多个参数,而子类中是分批传参的,此时从继承链上最近的位置获得的参数列表是不全的 50 | 51 | ~~~java 52 | class Generic{} 53 | class SubGeneric extends Generic{} 54 | class Acturl extends SubGeneric{} 55 | ~~~ 56 | 57 | **情况二:** 58 | 59 | 在继承链中的某个位置传入了类型参数,然后又定义了一个新的参数,此时从继承链上最近的位置获取的参数列表和顶层类的参数已经没有关系了 60 | 61 | ~~~ java 62 | class Generic{} 63 | class SubGeneric extends Generic{} 64 | class Acturl extends SubGeneric{} 65 | ~~~ 66 | 67 | **情况三:** 68 | 69 | 这是情况一与情况二的混合版,而且参数`V`的位置都变了 70 | 71 | ~~~ java 72 | class Generic{} 73 | class SubGeneric extends Generic{} 74 | class Acturl extends SubGeneric{} 75 | ~~~ 76 | 77 | 78 | 这些问题有空再研究 79 | 80 | ~~~java 81 | //TODO 82 | ~~~ -------------------------------------------------------------------------------- /_posts/2014-06-10-ssh.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: post 3 | title: SSH 4 | tags: Linux 命令 SSH 5 | categories: Linux 6 | published: false 7 | --- 8 | 9 | * TOC 10 | {:toc} 11 | 12 | 13 | ## 登陆 14 | 15 | **`-p`指定主机的端口** 16 | 17 | `$ ssh -p port user@host` 18 | 19 | **默认端口为22** 20 | 21 | `$ ssh user@host` 22 | 23 | **默认使用本机用户名** 24 | 25 | `$ ssh host` 26 | 27 | ## 过程 28 | 29 | 1. 远程主机接收到用户的登陆请求,把自己的公钥发给用户 30 | 31 | 2. 用户使用这个公钥,将密码加密后发回来 32 | 33 | 3. 远程主机用自己的私钥,解密登陆密码,如果密码正确,就同意用户登陆 34 | 35 | >此处如果有攻击者截取了用户请求,再将自己的公钥发送给用户,然后就可以用自己的私钥解密出用户的私密信息,这就是`中间人攻击` 36 | > 37 | >应对的方法:用户首次连接远程主机时,远程主机将发送一段128位长的公钥指纹,用户需要自行与远程主机网站发布的公钥指纹进行对比以判断真伪,当用户信任了此公钥后,它将被保存在`$HOME/.ssh/known_hosts`中,下次连接时无需再次确认。 38 | 39 |
40 | 41 | ## 公钥登陆 42 | 43 | 原理:用户将自己的`公钥`保存在远程主机上,登录时,远程主机向用户发送一段随机码,用户用自己的`私钥`签名后,在发回远程主机,远程主机用实现保存的`公钥`进行验证,如果成功,表示用户的身份正确,无需输入密码 44 | 45 | 1. 用户生成一对自己的密钥 46 | 47 | 2. 将用户的`公钥`内容添加到 `$HOME/.ssh/authorized_keys`中(可以用`ssh-copy-id user@host`进行该操作) 48 | 49 | 3. 重启ssh服务 `/etc/init.d/ssh restart` 50 | 51 | >公钥登陆相关配置 /etc/ssh/sshd_config 52 | > 53 | >RSAAuthentication yes 54 | > 55 | >PubkeyAuthentication yes 56 | > 57 | >AuthorizedKeysFile .ssh/authorized_keys 58 | 59 | 60 | ## 远程操作 61 | 62 | 1. 直接操作 `$ ssh user@host command` 63 | 64 | 2. 用户和远程主机之间,建立命令和数据的传输通道 65 | 66 | >将当前目录下的src文件,复制到远程主机的$HOME目录 67 | > 68 | >`$ tar czv src | ssh user@host "tar xz"` 69 | > 70 | >将远程主机$HOME目录下面的src文件,复制到用户的当前目录 71 | > 72 | >`$ ssh user@host "tar cz src" | tar xzv` 73 | 74 | 75 | ## 绑定本地端口 76 | 77 | `ssh -D port user@host` 78 | 79 | `-D` 指定与远程host建立隧道的本地端口 80 | 81 | 82 | ## 本地端口转发 83 | 84 | 假定`localhost`是本地主机,`remotehost`是远程主机,这两台主机之间无法连通。但是,另外还有一台`boardhost`,可以同时与前面两台主机互连。 85 | 86 | 在本机键入如下命令 87 | 88 | `$ ssh -L localPort:remotehost:remotePort boardhost` 89 | 90 | `L`参数一共接受三个值,分别是"`本地端口:目标主机:目标主机端口`" 91 | 92 | 该命令的意思是指定SSH绑定本地端口`localPort`,然后指定`boardhost`将所有的数据,转发到目标主机`remotehost`的`remotePort`端口 93 | 94 | >remotehost是boardhost 的相对地址(或绝对地址),因为数据其实是由boardhost 传输到remotehost中的,与localhost无关 95 | 96 | 这样一来`localhost`与`remotehost`之间将形成私密隧道,访问`localPort`就等于访问`remotePort` 97 | 98 | 99 | ## 远程端口转发 100 | 101 | 假定`hostA`是本地主机,`hostB`是远程主机,这两台主机之间无法连通,而且,`boardhost`是一台内网主机,即`boardhost`可以访问`hostA`,但是`hostA`无法访问`boardhost` 102 | 103 | 在`boardhost`键入如下命令 104 | 105 | `$ ssh -R portA:hostB:portB hostA` 106 | 107 | `R`参数也是接受三个值,分别是"`远程主机端口:目标主机:目标主机端口`"。这条命令的意思,就是让`hostA`监听它自己的`portA`端口,然后将所有数据经由`boardhost`,转发到`hostB`的`portB`端口。 108 | 109 | 对`boardhost`来说`hostA`是远程机器,在`boardhost`机器上指定`hostA`监听某个端口,称为远程端口转发 110 | 111 | >远程端口转发的前提条件是,`hostA`和`boardhost`两台主机都有sshd和ssh客户端,其原理就是:一开始由`hostA`充当`Server`,`boardhost`充当`Client`,`boardhost`发起请求建立一个连接;连接建立完成后,`hostA`就可以使用这个连接将充当`Clinet`,将数据转发至`boardhost`充当的`Server`;`boardhost`接收到数据后又需要充当`Client`将数据转发到`hostB` 112 | 113 | ## 其他参数 114 | 115 | `N`参数,表示只连接远程主机,不打开远程shell; 116 | 117 | `T`参数,表示不为这个连接分配TTY。 118 | 119 | 这个两个参数可以放在一起用,代表这个SSH连接只用来传数据,不执行远程操作。 120 | 121 | `$ ssh -NT -D port user@host` 122 | 123 | `f`参数,表示SSH连接成功后,转入后台运行。这样一来,就可以在不中断SSH连接的情况下,在本地shell中执行其他操作。 124 | 125 | `$ ssh -f -D port user@host` 126 | 127 | 要关闭这个后台连接,就只有用kill命令去杀掉进程。 -------------------------------------------------------------------------------- /_posts/2014-07-21-the-scheme-eval.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: scheme 解释器 3 | tags: lisp scheme eval 4 | categories: lisp 5 | --- 6 | 7 | 前段时间针对 [scheme][scheme] 语言写了一个解释器,现在就 fork 一下当时想法,整理一下其中的脉络,做一个思维快照,以期下次用C语言来实现时可以顺利地进行。 8 | 成品在此:[scheme-bootstrap][scheme-bootstrap]。 9 | 10 | * TOC 11 | {:toc} 12 | 13 | # 词法作用域 14 | 15 | 一条语句,在不同的函数当中可能具备不同的含义,其中变量的值取决于具体的作用域,所以对一条语句进行解释时需要考虑当时的上下文。 16 | 然而lisp是一种函数式的编程语言,函数可以被当成数据来进行传递,这会产生一个问题,如果函数体中存在自由变量,那么这个函数的行为将产生不可控的变化, 17 | 问题重点不是变化,而是不可控,在语言的使用者不需要函数行为变化的时候,自由变量会从外部作用域取值,自动产生行为的变化。 18 | 这种作用域叫做动态作用域 (Dynamic scope )。 19 | 20 | 像java,javascript之类的语言,都是在对象内部维护一个成员变量,函数中自由的局部变量根据这个成员变量的值来确定行为,虽然函数的行为都可变,但它是可控的。 21 | 为了实现可控,我们需要让语言满足人的直觉,即作用域和手写的代码结构相同,即使一个函数会被传递,其内部变量的值也应当追溯到函数定义时的作用域中。 22 | 这种作用域就叫做词法作用域 (Lexical scope)。 23 | 24 | 显然,实现词法作用域比动态作用域复杂些。实现动态作用域时,函数传递只要传递函数文本,然后插入到调用语句中进行解释就行了; 25 | 而词法作用域需要为每个被传递的函数绑定一个定义时的作用域,通常将被传递函数体与定义时的作用域打包成一个数据结构,这个结构叫做`闭包`。 26 | javascript就是使用闭包传递函数的。 27 | 28 | # 抽象语法树 29 | 30 | 语言被写下来之后只是一个文本,其内容是对某种数据结构的形式化描述,在对语言进行解释前,必须先对这个文本进行解析,让其描述的结构出现在内存中,这种结构叫做抽象语法树 (AST)。 31 | 这个过程比较乏味,无非是对字符串进行扫描,发现嵌套括号就将其作为树中的一个节点。为了跳过这个过程,我选择用scheme来写解释器,lisp 天生就适合进行这样的表处理。 32 | 类似这样用自己来解释自己的行为叫做自举。 33 | 34 | # 语法 35 | 36 | 解释器逐行解释语句,那么碰到不同的语法关键字时应当触发不同的解释行为。 37 | 38 | - **自求值表达式** 39 | 40 | 当碰到数字和字符串时,表示这是一个自求值表达式,即写下的文本就是值。 41 | 42 | - **quote** 43 | 44 | ```scheme 45 | (quote foo) 46 | ``` 47 | 引号表达式的值是去掉引号后的内容,其中 `'foo` 等价于 `(quote foo)`。 48 | 49 | - **begin** 50 | 51 | ```scheme 52 | (begin s1 s2) 53 | ``` 54 | 55 | begin 表达式表示其内容是顺序的多条语句,某些表达式只能支持单一的语句,用begin包裹这些语句后,就能当做一条语句使用。 56 | 57 | - **if** 58 | 59 | ```scheme 60 | (if ) 61 | ``` 62 | 63 | 对if的处理是非常容易的,解释器先对 `predicate` 求值,如果值为真则对 `consequent` 求值,否则对 `alternative` 求值。 64 | 65 | - **lambda** 66 | 67 | ```scheme 68 | (lambda (x y) 69 | (+ x y)) 70 | ``` 71 | 72 | lambda 表达式表示一个函数,对其求值就是将函数体,形参列表,作用域打包。 73 | 74 | - **define** 75 | 76 | ```scheme 77 | (define var Foo) 78 | ``` 79 | 80 | define 表达式表示定义,处理方式是求出 `Foo` 的值然后与 `var` 绑定,放入到当前作用域中。这个过程中隐含了对函数的定义,因为 `Foo` 可能就是一个lambda。 81 | 82 | - **set!** 83 | 84 | ```scheme 85 | (set! var Foo) 86 | ``` 87 | 88 | set! 语句提供了修改变量值的能力,如果没有这个关键字,scheme 就是纯粹的函数式语言,其函数将不会产生任何副作用,函数的行为将不可变。 89 | 其处理方式类似于define,区别是一个新增,一个修改。需要注意 define 并不能代替 set!, define 定义的变量只能在某个作用域内屏蔽外部的同名变量; 90 | 而set!将会沿着作用域链一直向上寻找匹配的变量名,然后进行修改。 91 | 92 | - **变量** 93 | 94 | ```scheme 95 | foo 96 | ``` 97 | 98 | 对变量求值的过程与 set! 类似,解释器将沿着作用域链一直向上寻找匹配的变量名,然后取出变量的值。 99 | 100 | - **函数调用** 101 | 102 | ```scheme 103 | (plus a b) 104 | ((lambda (x y) 105 | (+ x y)) a b) 106 | ``` 107 | 108 | 函数调用看似有两种形式,其实本质是一种,对变量 `plus` 的求值和对 `lambda` 的求值都将得到一个闭包,所以函数求值的真实过程是,求参数的值, 109 | 将参数传递给闭包,然后求闭包的值。对闭包求值的过程为,扩展作用域,将参数值与对应的形参名绑定并放入作用域,这个过程类似于define, 110 | 然后返回闭包中的函数过程体,该过程体为一条或多条语句,每条语句都需要被进行解释,这便产生一个递归的解释过程。 111 | 112 | 113 | 114 | 115 | 116 | # 基本操作和值 117 | 118 | 函数并不能从无到有定义出来,其过程总会使用一些其他的函数,例如加法,那么加法从何而来?事实上,这些非常基本的操作都是无法直接定义出来的, 119 | 它们需要从解释器中直接映射的。为了实现方便,以下操作都直接从解释器中映射: 120 | - `+` `-` `*` `/` (其实减乘除可以用加法来实现,但是没这个必要), 121 | - `eq?` 122 | - `car` 123 | - `cdr` 124 | - `cons` 125 | 126 | scheme中函数与数据的界限非常模糊,car cdr cons等看似基本的操作其实可以用函数来实现。 127 | 128 | ```scheme 129 | (define (cons x y) 130 | (define (dispatch tag) 131 | (cond [(= tag 0) x] 132 | [(= tag 1) y])) 133 | dispatch) 134 | 135 | (define (car z) 136 | (z 0)) 137 | 138 | (define (cdr z) 139 | (z 1)) 140 | ``` 141 | 142 | 基本值 143 | 144 | - `true` 145 | - `false` 146 | 147 | # 语法糖 148 | 149 | 语法糖并非增加什么新功能,而是让某些以有的功能写起来更舒服。对语法糖的处理也比较简单,就是将其结构变化成解释器能够认识的形式,然后发送给解释器重新解释。 150 | 151 | - **define** 152 | 153 | ```scheme 154 | (define (foo bar) 155 | ) 156 | ``` 157 | 158 | 该表示法为函数定义的语法糖,用基本的define定义函数时,每次都要写出lambda,比较繁琐。 159 | 该表示法等价于如下形式 160 | 161 | ```scheme 162 | (define foo (lambda (bar) 163 | )) 164 | ``` 165 | 166 | - **cond** 167 | 168 | ```scheme 169 | (cond [ ] 170 | [ ] 171 | [else e ]) 172 | ``` 173 | 174 | cond 表达式类似于常见的switch结构,但是每个case自带break,所以无法进入多个case。cond 可以转换成嵌套的if,然后将转换后的表达式转发给解释函数重新解释。 175 | 176 | ```scheme 177 | (if p1 178 | e1 179 | (if p2 180 | e2 181 | e)) 182 | ``` 183 | 184 | - **and** 185 | 186 | ```scheme 187 | (and c1 c2) 188 | ``` 189 | 190 | 短路的逻辑与可以用嵌套的if来实现 191 | 192 | ```scheme 193 | (if c1 194 | (if c2 195 | true) 196 | false) 197 | ``` 198 | 199 | - **or** 200 | 201 | ```scheme 202 | (or c1 c2) 203 | ``` 204 | 205 | 短路的逻辑或也可以用嵌套的if来实现 206 | 207 | ```scheme 208 | (if c1 209 | true 210 | (if c2 211 | true)) 212 | ``` 213 | 214 | 215 | ## let,let*,letrec 216 | 217 | 这三个语法糖提供了三种不同的方式来定义作用域,这三种表示法的作用各不相同,它们都建立在lambda的基础上。 218 | 219 | - **let** 220 | 221 | ```scheme 222 | (let ([ ] 223 | [ ]) 224 | ) 225 | ``` 226 | 227 | let 表达式提供了定义一个作用域并绑定**互斥**变量的功能,var1 与 var2 在语义上没有先后之分,也不能相互访问。 228 | let 表达式等价于如下如下形式,这是一个普通的函数调用。 229 | 230 | ```scheme 231 | ((lambda ( ) 232 | ) 233 | 234 | ) 235 | ``` 236 | 237 | - **let\*** 238 | 239 | ```scheme 240 | (let* ([ ] 241 | [ ]) 242 | ) 243 | ``` 244 | 245 | let\* 表达式提供了定义一个作用域并**先后**绑定变量的功能,var1 与 var2 在语义上存在先后之分,var2 可以访问 var1,而 var1 不能访问 var2。 246 | let\* 表达式等价于如下形式 247 | 248 | ```scheme 249 | (let ([ ]) 250 | (let ([ ]) 251 | )) 252 | ``` 253 | 254 | - **letrec** 255 | 256 | ```scheme 257 | (letrec ([ ] 258 | [ ]) 259 | ) 260 | ``` 261 | 262 | letrec 表达式提供了定义一个作用域并**同时**绑定变量的功能,var1 与 var2 在语义上为同时定义,var2 可以访问 var1,且 var1 可以访问 var2, 263 | letrec 的存在意义在于屏蔽外部同名变量,假定当前作用域外部存在一个变量 `var2`,那么let和let\* 中的var1求值时如果需要访问`var2`,那么将会访问这个外部的`var2`, 264 | 而letrec不同,如果letrec的var1求值是需要访问var2,那么这个var2的值**同一作用域内**的那个`var2`的值。 265 | letrec 表达式等价于如下形式 266 | 267 | ```scheme 268 | (let ([ **undefined**] 269 | [ **undefined**]) 270 | (set! var1 ) 271 | (set! var2 272 | ) 273 | ``` 274 | 275 | 解释器在对变量求值时会检查变量的值,如果其值为一个未定义标记,则会提示未定义错误。 276 | 277 | 278 | # 内部定义 279 | 280 | 函数的内部可以嵌套地使用define语句,但是define在写成文本时是存在先后的,但是函数内部定义的语义应当是同时定义,所以在对lambda进行解释时需要一些调整, 281 | 调整内容如下, 282 | - 扫描出lambda体内的所有define语句,只扫描内部定义的第一层无需嵌套,然后将define的变量名和值表达式(无需求值)装配成`letrec`的kv绑定形式; 283 | - 扫描出lambda内部的所有非定义语句将这个序列作为`letrec`的`body`; 284 | - 用上面得到的两个部分组成一个`letrec`; 285 | - 用新得到的letrec作为body构造一个新的 lambda 来替换原来的lambda。 286 | 287 | 上文描述的含义为: 288 | 289 | ```scheme 290 | (lambda (foo) 291 | (define a ) 292 | (define b ) 293 | ) 294 | ``` 295 | 296 | 等价于如下形式 297 | 298 | ```scheme 299 | (lambda (foo) 300 | (letrec ([a ] 301 | [b ]) 302 | )) 303 | ``` 304 | 305 | # 惰性求值 306 | 307 | 为了实现惰性求值,需要提供一种延迟计算一个表达式的能力,实现方式有两种,一种是将解释器改造成惰性求值解释器, 308 | 另一种是用一对关键字 `delay` `force` 提供局部的惰性和强制求值能力。 309 | 310 | - **惰性求值解释器** 311 | 312 | 对任何一个表达式求值时,都不直接求值表达式而是创建一个代理的数据结构来包裹表达式和求值时的环境,这个代理可以作为该表达式值的许诺进行传递。 313 | 当需要使用表达式的值时,再对这个代理进求值,解释器并不会完整地求出整个表达式的值,其实这个值依旧是一个代理,解释器很懒,最多只工作到刚刚满足需求的程度。 314 | 315 | 理论上来讲,惰性求值的效率应当高过普通的求值器,但是它有个缺陷,惰性求值与赋值语句配合使用时经常会出现意料之外的情况,因此 `haskell` 这种惰性求值的语言同时也是纯函数式的语言。 316 | 317 | 因为赋值产生问题例子如下,其函数的返回值并不会发生任何变化,因为赋值表达式被惰性了,并没有真正执行。 318 | 要解决这个问题,必须提供一个机制让语言使用者主动地强制执行该表达式。 319 | 320 | ```scheme 321 | (define (f x) 322 | (set! x 998) 323 | x) 324 | ``` 325 | 326 | - **delay/force** 327 | 328 | 用这种方式提供惰性功能并没什么理论上的变化,只是让普通的解释器增加两个case, 在碰到delay时生成代理,碰到force时强制执行。其优点是简单易用,同时缺点也非常明显, 329 | 必须为高阶函数提供两个版本,一个版本普通,一个版本惰性,而且在使用惰性功能时,代码中会遍布这两个关键字。 330 | 331 | 在我写的解释器中用**delay/force**的方式提供了惰性求值的功能。 332 | 333 | # 尾调用优化 334 | 335 | scheme语言没有循环,循环的本质就是在递归时不断对一个变量进行读写,这个变量作为终止条件便可以控制递归的次数,在没有循环时,可以用在递归函数中传递这个变化的因子来代替。 336 | 这种递归比较特殊,它在栈中只占一帧,并不会在栈空间累积数据,要使递归能够代替循环,必须进行尾调用优化。 337 | 338 | 因为我的解释器是自举的,scheme语言本身提供了对尾调用的优化,在对递归的函数进行解释时,保存当前一帧中的重要数据的负担被转嫁到了解释器中, 339 | 如果编写解释器的语言恰好也支持尾调用优化,这一负担会消弭于无形。但是如果编写解释器的语言没有尾调用优化呢?那我们就需要自己处理这种优化。 340 | 341 | 处理尾调用优化比较复杂,需要将解释器写成寄存器风格,用栈来保存寄存器中的历史值。 342 | 在解释一个表达式前,将寄存器值入栈,解释一个表达式后,栈中内容弹出到寄存器中,解释一个表达式的值不会在栈中留下任何数据。 343 | 如果表达式中存在子表达式,那么这个过程也适用于子表达式,显而易见,子表达式解释前的数据入栈时将压在父表达式的数据之上。 344 | 如果子表示的嵌套层数太深,数据将有可能撑满整个栈,递归函数通常极有可能嵌套太深。 345 | 346 | 基于这种解释器模型,可以选择让表达式序列中的最后一条语句在解释之前数据不入栈,这最后一条语句执行完毕后,直接修改寄存器,数据也不必出栈。 347 | 如果最后一条语句仅仅为一个函数调用,那么这个语句将不会在栈中累积数据,即使这个函数是递归的也同样如此。 348 | 349 | 350 | # 垃圾回收 351 | 352 | 垃圾回收的目标是函数执行时扩展的作用域以及函数的闭包,判别一段内存是否可回收的方式是枚举根节点。 353 | 从寄存器中的指针出发,经过一系列car,cdr能达到的对象,有可能影响未来的计算过程,那些不能达到的都是可回收的垃圾。 354 | 355 | 如果采用**停止并复制**的算法进行回收,其流程大致如下: 356 | 357 | - GC开始之前,将所有寄存器内容保存到一个预先分配好的表里,并让root寄存器指向这个表的顶部序对。 358 | 359 | - GC时,先从root表开始,一个一个地复制序对,原内存中被复制的序对,car标记为“破碎的心”,cdr放置一个前向指针,指向序对复制后的新位置。 360 | 361 | - 状态控制,free,scan。 362 | free 指向下一段可用内存,内存分配时递增的地址就是通过它来实现,通过他可以知道当前内存的使用状态。 363 | 364 | - GC循环,scan初始指向一个新内存区的对象A,而该对象car,cdr仍指向旧内存区的对象,假定scan正在扫描A对象的car,此时需要检查car指向的对象是否已被复制。 365 | 如果未复制就将其复制到free所指的位置并更新free,同时在旧对象做标记,然后更新car使其指向复制后的对象。如果car指向的对象已被复制,则car更新为将该旧对象的cdr中的前向指针。 366 | 当scan超过free时GC结束。 367 | 368 | 369 | 370 | [scheme]:http://baike.baidu.com/link?url=wgd84RHmek_qWtVHP9uhUL97pPelbW1iiUjF39rRuIrSHeG5ekDywMoiyWXDFgkaz3sdkYS2TRXs29CzMa7paa 371 | [scheme-bootstrap]:https://github.com/dubuyuye/scheme-bootstrap -------------------------------------------------------------------------------- /_posts/2014-10-09-bitmap.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: post 3 | title: Bitmap 4 | tags: 算法 algorithm Bitmap 5 | categories: algorithm 6 | published: false 7 | --- 8 | 9 | # bitmap 10 | 11 | 所谓bitmap就是用一个bit位来标记某个元素对应的value,而key即是这个元素。由于采用bit为单位来存储数据,因此在可以大大的节省存储空间 12 | 13 | ## 算法思想 14 | 15 | 32位机器上,一个整形,比如 `int a;` 在内存中占32bit,可以用对应的32个bit位来表示十进制的0-31个数,bitmap算法利用这种思想处理大量数据的排序与查询 16 | 17 | 优点: 18 | 19 | * 效率高,不许进行比较和移位 20 | 21 | * 占用内存少,比如有N=10000000个正整数,用bit存储占用内存为N/8 = 1250000Bytes = 1.2M,如果采用int数组存储,则需要38M以上 22 | 23 | 缺点: 24 | 25 | 无法对存在重复的数据进行排序和查找,因为key唯一,value只有0/1 26 | 27 | 示例: 28 | 29 | 申请一个int型的内存空间,则有4Byte,32bit。输入 4, 2, 1, 3时: 30 | 31 | ![bitmap][bitmap] 32 | 33 | 思想比较简单,关键是十进制和二进制bit位需要一个map映射表,把10进制映射到bit位上 34 | 35 | ## map映射表 36 | 37 | 假设需要排序或者查找的总数N=10000000,那么我们需要申请的内存空间为 int a[N/32 + 1]。其中a[0]在内存中占32位,依此类推: 38 | 39 | bitmap表为: 40 | 41 | a[0] ------> 0 - 31 42 | 43 | a[1] ------> 32 - 63 44 | 45 | a[2] ------> 64 - 95 46 | 47 | a[3] ------> 96 - 127 48 | 49 | ...... 50 | 51 | ## 位移转换 52 | 53 | (1) 求十进制数0-N对应的在数组a中的下标 54 | 55 | `index_loc = N / 32`即可,index_loc即为n对应的数组下标。例如n = 76, 则loc = 76 / 32 = 2,因此76在a[2]中。 56 | 57 | (2)求十进制数0-N对应的bit位 58 | 59 | `bit_loc = N % 32`即可,例如 n = 76, bit_loc = 76 % 32 = 12 60 | 61 | (3)利用移位0-31使得对应的32bit位为1 62 | 63 | `int[index_loc] << bit_loc` 64 | 65 | [bitmap]: {{"/bitmap.jpg" | prepend: site.imgrepo }} -------------------------------------------------------------------------------- /_posts/2014-10-17-shell-trash.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: post 3 | title: 告别rm,自制回收站 4 | tags: trash shell Linux script 5 | categories: linux 6 | published: false 7 | --- 8 | 9 | 常在河边走,哪有不湿鞋?命令敲多了,总会手欠的,笔者刚在不久前尝到了`rm`命令带来的苦果,于是一怒之下决定自制一个回收脚本,从此告别rm命令。 10 | 11 | 因为rm是落子无悔的,所以网上有一种做法是自制一个shell脚本,封装一些mv操作,并用这个新脚本替换原来的rm命令。本人不太赞成这种做法,一是因为rm是系统默认的命令,已经是一种事实上的标准,若胡乱改动其功能,难免会对依赖这个命令的程序造成影响;二是因为,改变了rm的删除行为后,系统就失去了彻底删除一个文件的能力,为了弥补又必须考虑从回收站中彻底删除文件的情形,反而将简单问题复杂化了。 12 | 13 | 我的做法非常简单,回收脚本直接作为一个新命令,使用的时候想删除就删除,想回收就回收,连从回收站恢复的功能都不需要,想恢复文件,自己用mv从回收站移出来就行了。 14 | 15 | ~~~ 16 | # !/bin/bash 17 | 18 | readonly trash_home=/tmp/trash 19 | 20 | for target in "$@" 21 | do 22 | dest=${trash_home}`realpath $target | xargs dirname` 23 | 24 | if [ ! -e ${dest} ]; then 25 | mkdir -p ${dest}; 26 | fi 27 | 28 | if [ -d $target ]; then 29 | mv -i $target ${dest}; 30 | elif [ -f $target ]; then 31 | mv -i $target ${dest}; 32 | fi 33 | done 34 | ~~~ 35 | 36 | 在`/etc/profile.d`目录新建文件`trash`,将以上代码粘贴进去,然后赋予其可执行权限。 37 | 38 | 命令运行效果应该如此: 39 | 40 | ![trash][trash] 41 | 42 | [trash]: {{"/shell-trash.png" | prepend: site.imgrepo }} 43 | 44 | -------------------------------------------------------------------------------- /_posts/2015-01-11-linux-make.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: post 3 | title: make 4 | tags: make Linux script 5 | categories: Linux 6 | published: false 7 | --- 8 | 9 | 10 | * TOC 11 | {:toc} 12 | 13 | 代码变成可执行文件,叫做编译(compile);先编译这个,还是先编译那个(即编译的安排),叫做构建(build) 14 | 15 | 16 | 指定规则进行编译,默认为当前目录下的Makefile文件 17 | 18 | ~~~ 19 | $ make -f rules.txt 20 | 或者 21 | make --file=rules.txt 22 | 23 | ~~~ 24 | 25 | # Makefile 文件结构 26 | 27 | Makefile文件由一系列规则(rules)构成。每条规则的形式如下。 28 | 29 | ~~~ 30 | : 31 | [tab] 32 | 33 | ~~~ 34 | 35 | 上面第一行冒号前面的部分,叫做"目标"(target),冒号后面的部分叫做"前置条件"(prerequisites);第二行必须由一个tab键起首,后面跟着"命令"(commands)。 36 | 37 | "目标"是必需的,不可省略;"前置条件"和"命令"都是可选的,但是两者之中必须至少存在一个。 38 | 39 | ## 目标(target) 40 | 41 | 一个目标(target)就构成一条规则。目标通常是文件名,指明Make命令所要构建的对象 。目标可以是一个文件名,也可以是多个文件名,之间用空格分隔。 42 | 43 | 除了文件名,目标还可以是某个操作的名字,这称为"伪目标"(phony target)。 44 | 45 | ~~~ 46 | clean: 47 | rm *.o 48 | 49 | ~~~ 50 | 上面代码的目标是clean,它不是文件名,而是一个操作的名字,属于"伪目标 ",作用是删除对象文件。 51 | 52 | ~~~ 53 | $ make clean 54 | 55 | ~~~ 56 | 57 | 但是,如果当前目录中,正好有一个文件叫做clean,那么这个命令不会执行。因为Make发现clean文件已经存在,就认为没有必要重新构建了,就不会执行指定的rm命令。 58 | 59 | 为了避免这种情况,可以明确声明clean是"伪目标",写法如下。 60 | 61 | ~~~ 62 | .PHONY: clean 63 | clean: 64 | rm *.o temp 65 | 66 | ~~~ 67 | 68 | 声明clean是"伪目标"之后,make就不会去检查是否存在一个叫做clean的文件,而是每次运行都执行对应的命令。像.PHONY这样的内置目标名还有不少,可以查看手册。 69 | 70 | 如果Make命令运行时没有指定目标,默认会执行Makefile文件的第一个目标。 71 | 72 | ~~~ 73 | $ make 74 | 75 | ~~~ 76 | 77 | ## 前置条件(prerequisites) 78 | 79 | 前置条件通常是一组文件名,之间用空格分隔。它指定了"目标"是否重新构建的判断标准:只要有一个前置文件不存在,或者有过更新(前置文件的last-modification时间戳比目标的时间戳新),"目标"就需要重新构建。 80 | 81 | ~~~ 82 | result.txt: source.txt 83 | cp source.txt result.txt 84 | 85 | ~~~ 86 | 87 | 如果当前目录中,source.txt 已经**存在**,那么make result.txt可以正常运行,否则必须再写一条规则,来生成 source.txt 。 88 | 89 | ~~~ 90 | source.txt: 91 | echo "this is the source" > source.txt 92 | 93 | ~~~ 94 | 95 | 上面代码中,source.txt后面没有前置条件,就意味着它跟其他文件都无关,只要这个文件还**不存在**,每次调用make source.txt,它都会生成。 96 | 97 | ~~~ 98 | $ make result.txt 99 | $ make result.txt 100 | 101 | ~~~ 102 | 103 | 上面命令连续执行两次make result.txt。第一次执行会先新建 source.txt,然后再新建 result.txt。第二次执行,Make发现 source.txt **没有变动**(时间戳晚于 result.txt),就不会执行任何操作,result.txt 也不会重新生成。 104 | 105 | 如果需要生成多个文件,往往采用下面的写法。 106 | 107 | ~~~ 108 | source: file1 file2 file3 109 | 110 | ~~~ 111 | 112 | 上面代码中,source 是一个伪目标,只有三个前置文件,没有任何对应的命令。 113 | 114 | ~~~ 115 | $ make source 116 | 117 | ~~~ 118 | 119 | 执行make source命令后,就会一次性生成 file1,file2,file3 三个文件 120 | 121 | ## 命令(commands) 122 | 123 | 命令(commands)表示如何更新目标文件,由一行或多行的Shell命令组成。它是构建"目标"的具体指令,它的运行结果通常就是生成目标文件。 124 | 125 | 每行命令之前必须有一个tab键。如果想用其他键,可以用内置变量.RECIPEPREFIX声明。 126 | 127 | ~~~ 128 | .RECIPEPREFIX = > 129 | all: 130 | > echo Hello, world 131 | 132 | ~~~ 133 | 134 | 上面代码用.RECIPEPREFIX指定,大于号(>)替代tab键。所以,每一行命令的起首变成了大于号,而不是tab键。 135 | 136 | 需要注意的是,每行命令在一个单独的shell中执行。这些Shell之间没有继承关系。 137 | 138 | ~~~ 139 | var-lost: 140 | export foo=bar 141 | echo "foo=[$$foo]" 142 | 143 | ~~~ 144 | 上面代码执行后(make var-lost),取不到foo的值。因为两行命令在两个不同的进程执行。一个解决办法是将两行命令写在一行,中间用分号分隔。 145 | 146 | ~~~ 147 | var-kept: 148 | export foo=bar; echo "foo=[$$foo]" 149 | 150 | ~~~ 151 | 152 | 另一个解决办法是在换行符前加反斜杠转义。 153 | 154 | ~~~ 155 | var-kept: 156 | export foo=bar; \ 157 | echo "foo=[$$foo]" 158 | 159 | ~~~ 160 | 161 | 最后一个方法是加上.ONESHELL:命令。 162 | 163 | ~~~ 164 | .ONESHELL: 165 | var-kept: 166 | export foo=bar; 167 | echo "foo=[$$foo]" 168 | 169 | ~~~ 170 | 171 | 172 | # Makefile 文件语法 173 | 174 | **`# `号注释** 175 | 176 | ## 通配符 177 | 178 | 通配符(wildcard)用来指定一组符合条件的文件名。Makefile 的通配符与 Bash 一致,主要有星号(*)、问号(?)和 [...] 。比如, *.o 表示所有后缀名为o的文件。 179 | 180 | ## 回声(echoing) 181 | 182 | 正常情况下,make会打印每条命令,然后再执行,这就叫做回声(echoing)。 183 | 184 | 在命令的前面加上@,就可以关闭回声。 185 | 186 | ## 模式匹配 187 | 188 | Make命令允许对文件名,进行类似正则运算的匹配,主要用到的匹配符是%。比如,假定当前目录下有 f1.c 和 f2.c 两个源码文件,需要将它们编译为对应的对象文件。 189 | 190 | ~~~ 191 | %.o: %.c 192 | 193 | ~~~ 194 | 195 | 等同于下面的写法。 196 | 197 | ~~~ 198 | f1.o: f1.c 199 | f2.o: f2.c 200 | 201 | ~~~ 202 | 203 | 使用匹配符%,可以将大量同类型的文件,只用一条规则就完成构建。 204 | 205 | >待验证 206 | > 207 | >关于模式匹配有个误解区,%.o:%.c并不是直接去匹配文件目录里的文件,而是匹配上下文的东西: 208 | > 209 | >`%.o:%.c` 210 | > 211 | >`gcc main.c -o main.o` 212 | > 213 | >`main.o:main.c` 214 | > 215 | >当我们make的时候 会执行 gcc main.c -o main.o 216 | 217 | 218 | ## 变量和赋值符 219 | 220 | Makefile 允许使用等号自定义变量。 221 | 222 | 调用时,变量需要放在 $( ) 之中。 223 | 224 | ~~~ 225 | txt = Hello World 226 | test: 227 | @echo $(txt) 228 | 229 | ~~~ 230 | 231 | 调用Shell变量,需要在美元符号前,再加一个美元符号,这是因为Make命令会对美元符号转义。 232 | 233 | ~~~ 234 | test: 235 | @echo $$HOME 236 | 237 | ~~~ 238 | 239 | 有时,变量的值可能指向另一个变量。 240 | 241 | ~~~ 242 | v1 = $(v2) 243 | 244 | ~~~ 245 | 246 | 上面代码中,变量 v1 的值是另一个变量 v2。这时会产生一个问题,v1 的值到底在定义时扩展(**静态扩展**),还是在运行时扩展(**动态扩展**)?如果 v2 的值是动态的,这两种扩展方式的结果可能会差异很大。 247 | 248 | 为了解决类似问题,Makefile一共提供了四个赋值运算符 (=、:=、?=、+=),它们的区别请看[StackOverflow](http://stackoverflow.com/questions/448910/makefile-variable-assignment)。 249 | 250 | ~~~ 251 | VARIABLE = value 252 | # 在执行时扩展,允许递归扩展。 253 | 254 | VARIABLE := value 255 | # 在定义时扩展。 256 | 257 | VARIABLE ?= value 258 | # 只有在该变量为空时才设置值。 259 | 260 | VARIABLE += value 261 | # 将值追加到变量的尾端。 262 | 263 | ~~~ 264 | 265 | ## 内置变量(Implicit Variables) 266 | 267 | Make命令提供一系列内置变量,比如,$(CC) 指向当前使用的编译器,$(MAKE) 指向当前使用的Make工具。这主要是为了跨平台的兼容性,详细的内置变量清单见[手册](https://www.gnu.org/software/make/manual/html_node/Implicit-Variables.html)。 268 | 269 | ~~~ 270 | output: 271 | $(CC) -o output input.c 272 | 273 | ~~~ 274 | 275 | ## 自动变量(Automatic Variables) 276 | 277 | (1)`$@` 278 | 279 | `$@`指代当前目标,就是Make命令当前构建的那个目标。比如,make foo的 `$@` 就指代foo。 280 | 281 | ~~~ 282 | a.txt b.txt: 283 | touch $@ 284 | 285 | ~~~ 286 | 等同于下面的写法。 287 | 288 | ~~~ 289 | a.txt: 290 | touch a.txt 291 | b.txt: 292 | touch b.txt 293 | 294 | ~~~ 295 | 296 | (2)`$<` 297 | 298 | `$<` 指代第一个前置条件。比如,规则为 t: p1 p2,那么`$<` 就指代p1。 299 | 300 | ~~~ 301 | a.txt: b.txt c.txt 302 | cp $< $@ 303 | 304 | ~~~ 305 | 306 | 等同于下面的写法。 307 | 308 | ~~~ 309 | a.txt: b.txt c.txt 310 | cp b.txt a.txt 311 | 312 | ~~~ 313 | 314 | (3)`$?` 315 | 316 | `$?` 指代比目标更新的所有前置条件,之间以空格分隔。比如,规则为 t: p1 p2,其中 p2 的时间戳比 t 新,`$?`就指代p2。 317 | 318 | (4)`$^` 319 | 320 | `$^` 指代所有前置条件,之间以空格分隔。比如,规则为 t: p1 p2,那么 `$^` 就指代 p1 p2 。 321 | 322 | (5)`$*` 323 | 324 | `$*` 指代匹配符 % 匹配的部分, 比如% 匹配 f1.txt 中的f1 ,`$*` 就表示 f1。 325 | 326 | (6)`$(@D)` 和 `$(@F)` 327 | 328 | `$(@D) `和` $(@F) `分别指向 `$@` 的目录名和文件名。比如,`$@`是 src/input.c,那么`$(@D)` 的值为 src ,`$(@F) `的值为 input.c。 329 | 330 | (7)`$(闭包测试1
15 | 闭包测试2
16 | 闭包测试3
17 | ~~~ 18 | 19 | ~~~javascript 20 | function closureTest(){ 21 | for (var i = 1; i < 4; i++) { 22 | var element = document.getElementById('closureTest' + i); 23 | element.onclick = function(){ 24 | alert(i); 25 | } 26 | } 27 | } 28 | ~~~ 29 | 30 | 此时无论点击哪个超链接,弹出的都是`3`,这是因为onclick触发时,绑定函数才会去初始化`i`的值,而`i`引用自外部函数`closureTest`,在closureTest中,`i`早已递增到3。 31 | 32 | 33 | 解决办法很简单,不闭包就行了。 34 | 35 | ~~~javascript 36 | function badClosureExample(){ 37 | for (var i = 1; i <4; i++) { 38 | var element = document.getElementById('closureTest' + i); 39 | element.onclick = clickCall(i); 40 | } 41 | } 42 | 43 | function clickCall(j){ 44 | return function(){ 45 | alert('您单击的是第' + j + '个链接'); 46 | } 47 | } 48 | ~~~ 49 | 50 | 闭包也并非全然有害,有时我们也可以利用做些有趣的事,例如定时任务的传参 51 | 52 | ~~~javascript 53 | function bind(){ 54 | var element = document.getElementById('closureTest0'); 55 | element.onclick = function(){ 56 | setTimeout(function(p){ 57 | return function(){ 58 | alert(p); 59 | } 60 | }('998'), 1000); //延迟1秒弹出提示 61 | } 62 | } 63 | ~~~ 64 | -------------------------------------------------------------------------------- /_posts/2015-02-11-webtool.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: post 3 | title: DIY的 ajax 框架 4 | tags: ajax javascript 5 | categories: front-end 6 | published: false 7 | --- 8 | 9 | 话不多说,直接上源码,用了传说中的伪面向对象写法 10 | 11 | 加了个`jsonp`用来跨域,当然了,需要服务端支持才有效 12 | 13 | ~~~javascript 14 | (function(){ 15 | var WT = { 16 | 17 | ID_SEL:'# ', 18 | 19 | CLA_SEL:'.', 20 | 21 | newInstance:function(){ 22 | 23 | var instance = {}; 24 | 25 | var setAttrs = function (dom,attrs){ 26 | for(var key in attrs){ 27 | dom.setAttribute(key,attrs[key]); 28 | } 29 | } 30 | 31 | var convertParam = function(data){ 32 | var param = []; 33 | for (var key in data) { 34 | param.push(encodeURIComponent(key) + '=' + encodeURIComponent(data[key])); 35 | } 36 | return param; 37 | } 38 | 39 | instance.select = function(selector){ 40 | var h = selector.charAt(0); 41 | var o = null; 42 | var m = null; 43 | if(h==WT.ID_SEL){ 44 | m = selector.substr(1); 45 | o = document.getElementById(m); 46 | } 47 | else if(h==WT.CLA_SEL){ 48 | //TODO 49 | //m = selector.substr(1); 50 | //o = document.getElementsByClassName(m); 51 | }else { 52 | m = selector; 53 | o = document.getElementsByTagName(m); 54 | } 55 | return o; 56 | } 57 | 58 | instance.load = function(tagName, attrs, callback){ 59 | var dom = document.createElement(tagName); 60 | setAttrs(dom,attrs); 61 | dom.onload=function(){ 62 | typeof callback === 'function' && callback.call(this,dom); 63 | dom = null; callback =null; 64 | } 65 | return dom; 66 | } 67 | 68 | 69 | instance.jsonp = function(jp){ 70 | //---- attributes----// 71 | var callback = jp.callback; 72 | var url = jp.url; 73 | var data = jp.data; 74 | 75 | var cn = 'jsonp'+ Date.now(); 76 | window[cn] = callback; 77 | var param = convertParam(data); 78 | param.push('callback=' + cn); 79 | url += (url.indexOf('?')==-1 ? '?' : '') + param.join('&'); 80 | 81 | var script = document.createElement('script'); 82 | script.type = 'text/javascript'; 83 | script.onload = script.onreadystatechange = function() { 84 | this.parentNode.removeChild(script); 85 | delete window[cn]; 86 | script.onload = script.onreadystatechange = null; 87 | } 88 | script.src = url; 89 | this.select('body')[0].appendChild(script); 90 | } 91 | 92 | instance.ajax = function(jp){ 93 | //---- attributes----// 94 | var type = jp.type;type ? type : 'GET'; 95 | var url = jp.url; 96 | var data = jp.data; 97 | var success = jp.success; 98 | 99 | var xhr = XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP"); 100 | xhr.open(type,url); 101 | if (type.toUpperCase() === 'POST') { 102 | xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); 103 | } 104 | xhr.onreadystatechange = function(){ 105 | if(xhr.readyState==4){ 106 | typeof success === 'function' && success(eval('(' + xhr.responseText + ')'), xhr.status); 107 | xhr = null; 108 | } 109 | } 110 | xhr.send(convertParam(data).join('&')); 111 | } 112 | 113 | instance.loadImg = function (url, callback) { 114 | /** img的onload在图片下载完成后,dom加载前发生 **/ 115 | this.load('img',{src:url},callback); 116 | } 117 | 118 | instance.loadCss = function(url,callback){ 119 | /** css的onload 在 dom加载后发生 **/ 120 | var dom = this.load('link',{rel:'stylesheet',type:'text/css',href:url},callback); 121 | this.select('head')[0].appendChild(dom); 122 | } 123 | 124 | instance.loadJs = function(url,callback){ 125 | /** js的onload 在 dom加载后发生 **/ 126 | var js = this.load('script',{type:'text/javascript',src:url},callback); 127 | this.select('body')[0].appendChild(js); 128 | } 129 | 130 | return instance; 131 | } 132 | }; 133 | window['$'] = WT.newInstance(); 134 | })(); 135 | ~~~ -------------------------------------------------------------------------------- /_posts/2015-02-13-sqlappend.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: post 3 | title: SQL 拼接 4 | tags: sql database Java 5 | categories: database 6 | published: false 7 | --- 8 | 9 | 在java中进行SQL拼接是一件无比痛苦的工作,这是由于需要通过判断参数动态生成SQL 10 | 11 | 而且拼接时产生满屏幕的加号或者append,使SQL几乎失去了可读性,那么丢失`WHERE`,`AND`,逗号等语法错误将随之而来 12 | 13 | 最近研究mybatis时发现了一个非常好用的工具类 SQL Builder [^sqlBuilder] 14 | 15 | 16 | ## 示例 17 | 18 | ~~~java 19 | @Test 20 | public void test(){ 21 | String sql = new SQL(){ { 22 | SELECT("name"); 23 | SELECT("password"); 24 | SELECT("sex"); 25 | FROM("student s"); 26 | INNER_JOIN("info i on s.id = i.id"); 27 | WHERE(String.format("name = '%s'","HanMeiMei")); 28 | } }.toString(); 29 | 30 | System.out.println(sql); 31 | } 32 | ~~~ 33 | 34 | 运行结果为: 35 | 36 | SELECT name, password, sex 37 | 38 | FROM student s 39 | 40 | INNER JOIN info i on s.id = i.id 41 | 42 | WHERE (name = 'HanMeiMei' AND i.sex = 1) 43 | 44 | SQL的拼接词都自动生成了 45 | 46 | ## 源码 47 | 48 | 事实上不必为了使用这个类而引入Mybatis,SQL Builder非常简单,简单到没有使用任何第三方类库,只需将这个类复制粘贴一下就可以直接用了。 49 | 50 | SQL Builder 全部代码 51 | 52 | ~~~java 53 | import java.io.IOException; 54 | import java.util.ArrayList; 55 | import java.util.List; 56 | 57 | public abstract class AbstractSQL { 58 | 59 | private static final String AND = ") \nAND ("; 60 | private static final String OR = ") \nOR ("; 61 | 62 | public abstract T getSelf(); 63 | 64 | public T UPDATE(String table) { 65 | sql().statementType = SQLStatement.StatementType.UPDATE; 66 | sql().tables.add(table); 67 | return getSelf(); 68 | } 69 | 70 | public T SET(String sets) { 71 | sql().sets.add(sets); 72 | return getSelf(); 73 | } 74 | 75 | public T INSERT_INTO(String tableName) { 76 | sql().statementType = SQLStatement.StatementType.INSERT; 77 | sql().tables.add(tableName); 78 | return getSelf(); 79 | } 80 | 81 | public T VALUES(String columns, String values) { 82 | sql().columns.add(columns); 83 | sql().values.add(values); 84 | return getSelf(); 85 | } 86 | 87 | public T SELECT(String columns) { 88 | sql().statementType = SQLStatement.StatementType.SELECT; 89 | sql().select.add(columns); 90 | return getSelf(); 91 | } 92 | 93 | public T SELECT_DISTINCT(String columns) { 94 | sql().distinct = true; 95 | SELECT(columns); 96 | return getSelf(); 97 | } 98 | 99 | public T DELETE_FROM(String table) { 100 | sql().statementType = SQLStatement.StatementType.DELETE; 101 | sql().tables.add(table); 102 | return getSelf(); 103 | } 104 | 105 | public T FROM(String table) { 106 | sql().tables.add(table); 107 | return getSelf(); 108 | } 109 | 110 | public T JOIN(String join) { 111 | sql().join.add(join); 112 | return getSelf(); 113 | } 114 | 115 | public T INNER_JOIN(String join) { 116 | sql().innerJoin.add(join); 117 | return getSelf(); 118 | } 119 | 120 | public T LEFT_OUTER_JOIN(String join) { 121 | sql().leftOuterJoin.add(join); 122 | return getSelf(); 123 | } 124 | 125 | public T RIGHT_OUTER_JOIN(String join) { 126 | sql().rightOuterJoin.add(join); 127 | return getSelf(); 128 | } 129 | 130 | public T OUTER_JOIN(String join) { 131 | sql().outerJoin.add(join); 132 | return getSelf(); 133 | } 134 | 135 | public T WHERE(String conditions) { 136 | sql().where.add(conditions); 137 | sql().lastList = sql().where; 138 | return getSelf(); 139 | } 140 | 141 | public T OR() { 142 | sql().lastList.add(OR); 143 | return getSelf(); 144 | } 145 | 146 | public T AND() { 147 | sql().lastList.add(AND); 148 | return getSelf(); 149 | } 150 | 151 | public T GROUP_BY(String columns) { 152 | sql().groupBy.add(columns); 153 | return getSelf(); 154 | } 155 | 156 | public T HAVING(String conditions) { 157 | sql().having.add(conditions); 158 | sql().lastList = sql().having; 159 | return getSelf(); 160 | } 161 | 162 | public T ORDER_BY(String columns) { 163 | sql().orderBy.add(columns); 164 | return getSelf(); 165 | } 166 | 167 | private SQLStatement sql = new SQLStatement(); 168 | 169 | private SQLStatement sql() { 170 | return sql; 171 | } 172 | 173 | public A usingAppender(A a) { 174 | sql().sql(a); 175 | return a; 176 | } 177 | 178 | @Override 179 | public String toString() { 180 | StringBuilder sb = new StringBuilder(); 181 | sql().sql(sb); 182 | return sb.toString(); 183 | } 184 | 185 | private static class SafeAppendable { 186 | private final Appendable a; 187 | private boolean empty = true; 188 | 189 | public SafeAppendable(Appendable a) { 190 | super(); 191 | this.a = a; 192 | } 193 | 194 | public SafeAppendable append(CharSequence s) { 195 | try { 196 | if (empty && s.length() > 0) empty = false; 197 | a.append(s); 198 | } catch (IOException e) { 199 | throw new RuntimeException(e); 200 | } 201 | return this; 202 | } 203 | 204 | public boolean isEmpty() { 205 | return empty; 206 | } 207 | 208 | } 209 | 210 | private static class SQLStatement { 211 | 212 | public enum StatementType { 213 | DELETE, INSERT, SELECT, UPDATE 214 | } 215 | 216 | StatementType statementType; 217 | List sets = new ArrayList(); 218 | List select = new ArrayList(); 219 | List tables = new ArrayList(); 220 | List join = new ArrayList(); 221 | List innerJoin = new ArrayList(); 222 | List outerJoin = new ArrayList(); 223 | List leftOuterJoin = new ArrayList(); 224 | List rightOuterJoin = new ArrayList(); 225 | List where = new ArrayList(); 226 | List having = new ArrayList(); 227 | List groupBy = new ArrayList(); 228 | List orderBy = new ArrayList(); 229 | List lastList = new ArrayList(); 230 | List columns = new ArrayList(); 231 | List values = new ArrayList(); 232 | boolean distinct; 233 | 234 | private void sqlClause(SafeAppendable builder, String keyword, List parts, String open, String close, 235 | String conjunction) { 236 | if (!parts.isEmpty()) { 237 | if (!builder.isEmpty()) 238 | builder.append("\n"); 239 | builder.append(keyword); 240 | builder.append(" "); 241 | builder.append(open); 242 | String last = "________"; 243 | for (int i = 0, n = parts.size(); i < n; i++) { 244 | String part = parts.get(i); 245 | if (i > 0 && !part.equals(AND) && !part.equals(OR) && !last.equals(AND) && !last.equals(OR)) { 246 | builder.append(conjunction); 247 | } 248 | builder.append(part); 249 | last = part; 250 | } 251 | builder.append(close); 252 | } 253 | } 254 | 255 | private String selectSQL(SafeAppendable builder) { 256 | if (distinct) { 257 | sqlClause(builder, "SELECT DISTINCT", select, "", "", ", "); 258 | } else { 259 | sqlClause(builder, "SELECT", select, "", "", ", "); 260 | } 261 | 262 | sqlClause(builder, "FROM", tables, "", "", ", "); 263 | sqlClause(builder, "JOIN", join, "", "", "\nJOIN "); 264 | sqlClause(builder, "INNER JOIN", innerJoin, "", "", "\nINNER JOIN "); 265 | sqlClause(builder, "OUTER JOIN", outerJoin, "", "", "\nOUTER JOIN "); 266 | sqlClause(builder, "LEFT OUTER JOIN", leftOuterJoin, "", "", "\nLEFT OUTER JOIN "); 267 | sqlClause(builder, "RIGHT OUTER JOIN", rightOuterJoin, "", "", "\nRIGHT OUTER JOIN "); 268 | sqlClause(builder, "WHERE", where, "(", ")", " AND "); 269 | sqlClause(builder, "GROUP BY", groupBy, "", "", ", "); 270 | sqlClause(builder, "HAVING", having, "(", ")", " AND "); 271 | sqlClause(builder, "ORDER BY", orderBy, "", "", ", "); 272 | return builder.toString(); 273 | } 274 | 275 | private String insertSQL(SafeAppendable builder) { 276 | sqlClause(builder, "INSERT INTO", tables, "", "", ""); 277 | sqlClause(builder, "", columns, "(", ")", ", "); 278 | sqlClause(builder, "VALUES", values, "(", ")", ", "); 279 | return builder.toString(); 280 | } 281 | 282 | private String deleteSQL(SafeAppendable builder) { 283 | sqlClause(builder, "DELETE FROM", tables, "", "", ""); 284 | sqlClause(builder, "WHERE", where, "(", ")", " AND "); 285 | return builder.toString(); 286 | } 287 | 288 | private String updateSQL(SafeAppendable builder) { 289 | 290 | sqlClause(builder, "UPDATE", tables, "", "", ""); 291 | sqlClause(builder, "SET", sets, "", "", ", "); 292 | sqlClause(builder, "WHERE", where, "(", ")", " AND "); 293 | return builder.toString(); 294 | } 295 | 296 | public String sql(Appendable a) { 297 | SafeAppendable builder = new SafeAppendable(a); 298 | if (statementType == null) { 299 | return null; 300 | } 301 | 302 | String answer; 303 | 304 | switch (statementType) { 305 | case DELETE: 306 | answer = deleteSQL(builder); 307 | break; 308 | 309 | case INSERT: 310 | answer = insertSQL(builder); 311 | break; 312 | 313 | case SELECT: 314 | answer = selectSQL(builder); 315 | break; 316 | 317 | case UPDATE: 318 | answer = updateSQL(builder); 319 | break; 320 | 321 | default: 322 | answer = null; 323 | } 324 | 325 | return answer; 326 | } 327 | } 328 | } 329 | 330 | 331 | public class SQL extends AbstractSQL { 332 | 333 | @Override 334 | public SQL getSelf() { 335 | return this; 336 | } 337 | 338 | } 339 | ~~~ 340 | 341 | [^sqlBuilder]: [http://mybatis.github.io/mybatis-3/statement-builders.html](http://mybatis.github.io/mybatis-3/statement-builders.html) -------------------------------------------------------------------------------- /_posts/2015-02-22-javajs.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: post 3 | title: Java中用js解析json 4 | tags: ScriptEngineManager Java javascript 5 | categories: java 6 | published: false 7 | --- 8 | 9 | 在java中如何解析json?fastjson?jackson?那未免太无趣了 10 | 11 | 其实我们可以试试ScriptEngine 12 | 13 | ~~~java 14 | public class NashornTest { 15 | 16 | private static String json = "[{name:'A',age:'18'},{name:'B',age:'19'},{name:'C',age:'30'}]"; 17 | 18 | private static String script = 19 | "function parse(json){" + 20 | " var names = new Array();" + 21 | " for(var i in json){" + 22 | " names.push(json[i].name);" + 23 | " }" + 24 | " return names;" + 25 | "};" + 26 | "parse(" + json + ");"; 27 | 28 | public static void main(String[] args) throws ScriptException { 29 | ScriptEngineManager manager = new ScriptEngineManager(); 30 | ScriptEngine engine = manager.getEngineByName( "JavaScript" ); 31 | 32 | Map result = (Map)engine.eval(script); 33 | result.forEach((k,v) -> System.out.println(v)); 34 | } 35 | } 36 | ~~~ 37 | 38 | 输出: 39 | 40 | A 41 | 42 | B 43 | 44 | C 45 | 46 | > 代码中使用了 [lambda](http://blog.rainynight.top/2014-12-13/java-newfeature/# lambda) 47 | 48 | -------------------------------------------------------------------------------- /_posts/2015-03-05-validator.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: post 3 | title: Bean Validation 4 | tags: Bean Validation Java 5 | categories: java 6 | published: false 7 | --- 8 | 9 | [BeanValidation][BeanValidation] 可以帮助开发者方便地对数据进行校验,但它只是一个标准,只有一套接口,要想使用它的功能必须选择一种实现,`hibernate-validator`是个不错的选择 10 | 11 | ~~~xml 12 | 13 | org.hibernate 14 | hibernate-validator 15 | 5.0.2.Final 16 | 17 | ~~~ 18 | 19 | BeanValidator 可以自动扫描到hibernate-validator,而不用进行任何配置,前提是需要将hibernate-validator放到classpath下 20 | 21 | 在JAVA类中可以直接得到可用的检验器实现: 22 | 23 | ~~~ 24 | private Validator validator = Validation.buildDefaultValidatorFactory().getValidator(); 25 | ~~~ 26 | 27 | 它是怎么做到的?笔者和你具有一样强的好奇心,深入源码后,笔者发现了这样一段代码 28 | 29 | ~~~ 30 | private List> loadProviders(ClassLoader classloader) { 31 | ServiceLoader loader = ServiceLoader.load( ValidationProvider.class, classloader ); 32 | Iterator providerIterator = loader.iterator(); 33 | List> validationProviderList = new ArrayList>(); 34 | while ( providerIterator.hasNext() ) { 35 | try { 36 | validationProviderList.add( providerIterator.next() ); 37 | } 38 | catch ( ServiceConfigurationError e ) { 39 | // ignore, because it can happen when multiple 40 | // providers are present and some of them are not class loader 41 | // compatible with our API. 42 | } 43 | } 44 | return validationProviderList; 45 | } 46 | ~~~ 47 | 48 | 显而易见,通过`ClassLoader`可以找出所有实现了`ValidationProvider`的接口的类,这些类即为BeanValidator的实现,然后从这个列表中取第一个就行了。 49 | 50 | BeanValidation 使用方法也非常简单,调用`validate`方法后会返回一个校验结果集,如果结果集长度为0则表示校验完全通过;否则将可以从`ConstraintViolation`中获取失败信息 51 | 52 | ~~~ 53 | Set> constraintViolations = validator.validate(new Article()); 54 | ~~~ 55 | 56 | 当然,前提是先要定义字段的约束,`@NotNull`表示此字段的值不可为空,注解也可以在字段上,更多预置的注解在`javax.validation.constraints`包中 57 | 58 | ~~~ 59 | @NotNull 60 | public String getTitle() { 61 | return title; 62 | } 63 | ~~~ 64 | 65 | # 与Spring集成 66 | 67 | 现在Spring4已经集成了BeanValidation,并增加了国际化支持,这个LocalValidatorFactoryBean可以注入到任何类中使用 68 | 69 | ~~~ 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | classpath:messages/validationMessages 82 | classpath:org/hibernate/validator/ValidationMessages 83 | 84 | 85 | 86 | 87 | 88 | 89 | ~~~ 90 | 91 | 如果你正在使用 SpringMVC 集成就更简单了 92 | 93 | ~~~ 94 | 95 | ~~~ 96 | 97 | 然后在需要校验的Bean上注解`@Valid`,并紧随其后添加`BindingResult` 98 | 99 | ~~~ 100 | @RequestMapping(value = "release", method = RequestMethod.POST) 101 | public String release(@Valid Article article, BindingResult bindingResult, HttpSession session, ModelMap modelMap){ 102 | ... 103 | ... 104 | } 105 | ~~~ 106 | 107 | 如果想要将校验失败的信息展示在页面上可以使用el表达式, 注意el版本必须在2.2以上, s前缀指的是spring标签 108 | 109 | ~~~ 110 | <%@taglib prefix="s" uri="http://www.springframework.org/tags" %> 111 | ~~~ 112 | 113 | ~~~ 114 | 115 | 116 | 字段错误:
117 | 118 | 119 | ${error.field}------${message}
120 |
121 |
122 | 123 | 124 | 全局错误:
125 | 126 | 127 | 128 | ${message}
129 |
130 |
131 |
132 |
133 | ~~~ 134 | 135 | --- 136 | 137 | 其实 BeanValidation的内容远不止于此,它还有很多更高级的特性,如分组校验等,详情请[点我][more] 138 | 139 | 140 | 141 | [BeanValidation]:http://beanvalidation.org/ 142 | [more]:http://www.ibm.com/developerworks/cn/java/j-lo-beanvalid/ -------------------------------------------------------------------------------- /_posts/2015-03-15-FullGc.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 大对象引起的频繁FULL GC 3 | tags: java jvm 4 | categories: java 5 | --- 6 | 7 | 最近发现了频繁FULL GC的情况,查看GC日志后,发现每次FULL GC后,老年代都能回收大半以上的空间,这意味着有很多临时对象被分配到了老年代。 8 | 9 | 用`jmap`命令导出堆转储文件,然后用jvisualvm导入后进行分析,发现char[]占据了最多了内存。 10 | 11 | 其中有大量的对象达到了3M,被直接担保分配进了老年代,因为暂时不知道这些对象是怎么产生的,最快的解决办法就是增大担保分配的阈值。 12 | 13 | 下面就是测试过程 14 | 15 | ~~~java 16 | /** 17 | -server 18 | -XX:+UseConcMarkSweepGC 19 | -Xms20M 20 | -Xmx20M 21 | -Xmn10M 22 | -XX:SurvivorRatio=8 23 | -XX:PretenureSizeThreshold=2000000 24 | -XX:+PrintGC 25 | -XX:+PrintGCDetails 26 | -XX:+PrintGCDateStamps 27 | -XX:+PrintHeapAtGC 28 | -Xloggc:/var/logs/PretenureSizeThreshold.log 29 | -XX:+HeapDumpOnOutOfMemoryError 30 | -XX:HeapDumpPath=/var/dumps/PretenureSizeThreshold.hprof 31 | */ 32 | public class PretenureSizeThreshold { 33 | 34 | private static final int _1MB = 1000 * 1000; 35 | 36 | public static void main(String[] args) throws Exception{ 37 | RuntimeMXBean mxBean = ManagementFactory.getRuntimeMXBean(); 38 | System.out.println(mxBean.getName()); 39 | String pid = mxBean.getName().split("@")[0]; 40 | 41 | byte[] allocation = new byte[3*_1MB]; 42 | 43 | while (true){ 44 | Thread.sleep(1000); 45 | } 46 | 47 | } 48 | } 49 | ~~~ 50 | 51 | ~~~text 52 | D:\Doc\MyRepo\learning-java>jmap -heap 14000 53 | Attaching to process ID 14000, please wait... 54 | Debugger attached successfully. 55 | Server compiler detected. 56 | JVM version is 25.121-b13 57 | 58 | using parallel threads in the new generation. 59 | using thread-local object allocation. 60 | Concurrent Mark-Sweep GC 61 | 62 | Heap Configuration: 63 | MinHeapFreeRatio = 40 64 | MaxHeapFreeRatio = 70 65 | MaxHeapSize = 20971520 (20.0MB) 66 | NewSize = 10485760 (10.0MB) 67 | MaxNewSize = 10485760 (10.0MB) 68 | OldSize = 10485760 (10.0MB) 69 | NewRatio = 2 70 | SurvivorRatio = 8 71 | MetaspaceSize = 21807104 (20.796875MB) 72 | CompressedClassSpaceSize = 1073741824 (1024.0MB) 73 | MaxMetaspaceSize = 17592186044415 MB 74 | G1HeapRegionSize = 0 (0.0MB) 75 | 76 | Heap Usage: 77 | New Generation (Eden + 1 Survivor Space): 78 | capacity = 9437184 (9.0MB) 79 | used = 2748888 (2.6215438842773438MB) 80 | free = 6688296 (6.378456115722656MB) 81 | 29.128265380859375% used 82 | Eden Space: 83 | capacity = 8388608 (8.0MB) 84 | used = 2748888 (2.6215438842773438MB) 85 | free = 5639720 (5.378456115722656MB) 86 | 32.7692985534668% used 87 | From Space: 88 | capacity = 1048576 (1.0MB) 89 | used = 0 (0.0MB) 90 | free = 1048576 (1.0MB) 91 | 0.0% used 92 | To Space: 93 | capacity = 1048576 (1.0MB) 94 | used = 0 (0.0MB) 95 | free = 1048576 (1.0MB) 96 | 0.0% used 97 | concurrent mark-sweep generation: 98 | capacity = 10485760 (10.0MB) 99 | used = 3000016 (2.8610382080078125MB) 100 | free = 7485744 (7.1389617919921875MB) 101 | 28.610382080078125% used 102 | 103 | 1815 interned Strings occupying 162040 bytes. 104 | ~~~ 105 | 老年代占用的空间为3000016,这表示数组通过担保分配进入了老年代,多出来的16是对象头的体积 106 | 107 | 修改启动参数`-XX:PretenureSizeThreshold=4000000` 108 | 109 | ~~~text 110 | D:\Doc\MyRepo\learning-java>jmap -heap 2356 111 | Attaching to process ID 2356, please wait... 112 | Debugger attached successfully. 113 | Server compiler detected. 114 | JVM version is 25.121-b13 115 | 116 | using parallel threads in the new generation. 117 | using thread-local object allocation. 118 | Concurrent Mark-Sweep GC 119 | 120 | Heap Configuration: 121 | MinHeapFreeRatio = 40 122 | MaxHeapFreeRatio = 70 123 | MaxHeapSize = 20971520 (20.0MB) 124 | NewSize = 10485760 (10.0MB) 125 | MaxNewSize = 10485760 (10.0MB) 126 | OldSize = 10485760 (10.0MB) 127 | NewRatio = 2 128 | SurvivorRatio = 8 129 | MetaspaceSize = 21807104 (20.796875MB) 130 | CompressedClassSpaceSize = 1073741824 (1024.0MB) 131 | MaxMetaspaceSize = 17592186044415 MB 132 | G1HeapRegionSize = 0 (0.0MB) 133 | 134 | Heap Usage: 135 | New Generation (Eden + 1 Survivor Space): 136 | capacity = 9437184 (9.0MB) 137 | used = 5748624 (5.4823150634765625MB) 138 | free = 3688560 (3.5176849365234375MB) 139 | 60.91461181640625% used 140 | Eden Space: 141 | capacity = 8388608 (8.0MB) 142 | used = 5748624 (5.4823150634765625MB) 143 | free = 2639984 (2.5176849365234375MB) 144 | 68.52893829345703% used 145 | From Space: 146 | capacity = 1048576 (1.0MB) 147 | used = 0 (0.0MB) 148 | free = 1048576 (1.0MB) 149 | 0.0% used 150 | To Space: 151 | capacity = 1048576 (1.0MB) 152 | used = 0 (0.0MB) 153 | free = 1048576 (1.0MB) 154 | 0.0% used 155 | concurrent mark-sweep generation: 156 | capacity = 10485760 (10.0MB) 157 | used = 0 (0.0MB) 158 | free = 10485760 (10.0MB) 159 | 0.0% used 160 | 161 | 1815 interned Strings occupying 162040 bytes. 162 | 163 | ~~~ 164 | 此时,老年代占用0,这表示数组没有达到大对象的标准,直接分配在了新生代 165 | -------------------------------------------------------------------------------- /_posts/2015-03-26-large-file-diff.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 大文件内容对比 3 | tags: java 算法 algorithm 排序 4 | categories: algorithm 5 | --- 6 | 7 | * TOC 8 | {:toc} 9 | 10 | 最近接到一个需求,要对两个系统的订单进行对比,找出其中的差异,对比结果有4种:一致、不一致、丢失、多余。 11 | 12 | 如果数据量少,处理起来就很简单,例如要对A,B两份数据进行对比: 13 | 1. 将数据A放入哈希表 14 | 2. 遍历数据B,依据id从A中查找对应的订单 15 | 3. 若从A中找到了对应的订单,则比较是否一致,并将此订单标记为已匹配 16 | 4. 若从A中找不到对应的订单,则表示A丢失此订单 17 | 5. 遍历A,筛选出所有未匹配的订单,这些订单都是A中多余的 18 | 19 | 但是如果数据量超大,在内存中处理就不合适了,数据需要放在磁盘上。 20 | 基于哈希的对比算法,无法避免大量对磁盘的随机访问,因而无法使用buffer,完全的io读写性能太差,所以这个方案是不行的。 21 | 22 | 如果AB两个集合是有序的,那么对比将会变得容易,一次遍历就可以对比完成,例如以id排序 23 | 1. 读取A指针对应的订单,读取B指针对应的订单 24 | 2. 若两个订单id相等,则对比内容是否一致 25 | 3. 若A指针订单id>B指针订单id,代表A中缺少B指针订单,记录结果,B指针移动到后一个 26 | 4. 若A指针订单id排序->合并的思路 33 | 1. 按行读取文件,将读到的行放入list 34 | 2. 如果已读取的行达到了1万行,对list进行排序,将排序后的list逐行写入临时文件 35 | 3. 打开多个临时文件,从每个文件的中逐行读取,选取其中订单id最小的行,写入新的临时文件 36 | 4. 循环以上步骤,直至剩下一个文件 37 | 38 | 示例代码 39 | ~~~java 40 | while (true){ 41 | line = br.readLine(); 42 | if(line != null){ 43 | chunkRows.add(line); 44 | } 45 | if(line == null || chunkRows.size() >= initialChunkSize){ 46 | if(chunkRows.size() > 0){ 47 | chunkRows.sort(comparator); 48 | Chunk chunk = initialChunk(rowNum, chunkRows, file); 49 | chunkList.add(chunk); 50 | rowNum += chunkRows.size(); 51 | chunkRows.clear(); 52 | } 53 | } 54 | if(line == null){ 55 | break; 56 | } 57 | } 58 | ~~~ 59 | 60 | ~~~java 61 | while (true){ 62 | List pollChunks = pollChunks(chunkQueue, currentLevel); 63 | if(CollectionUtils.isEmpty(pollChunks)){ 64 | currentLevel++; 65 | continue; 66 | } 67 | //合并 68 | Chunk mergedChunk = merge(pollChunks, original); 69 | //chunkQueue 中没有后续的元素,表示此次合并是最终合并 70 | if(chunkQueue.size() == 0){ 71 | chunkQueue.add(mergedChunk); 72 | break; 73 | } else { 74 | chunkQueue.add(mergedChunk); 75 | } 76 | } 77 | ~~~ 78 | 79 | [完整代码](完整代码) 80 | 81 | [完整代码]:https://github.com/bit-ranger/architecture/blob/93c189b0cc69f41dc9b030f75c812388e2e20d61/core/src/main/java/com/rainyalley/architecture/core/arithmetic/sort/FileSorter.java 82 | 83 | 84 | # 测试 85 | 86 | 在最后的多路合并中要考虑使用多少路进行合并,更少的路数,将产生更多的临时文件 87 | 88 | 拆分时需要考虑每个块的大小,过大的块将造成频繁的FULL GC 89 | 90 | IO的缓冲容量区也要考虑,这将影响IO读写的速度,以及FULL GC的频率 91 | 92 | 以如下配置为例,在我的个人电脑上, 93 | cpu频率3.40GHZ,磁盘顺序读450MB/s,随机读190MB/s,顺序写250MB/s,数据宽度20为20字符 94 | 对1千万条数据进行排序,耗时13秒。 95 | 96 | ~~~java 97 | FileSorter sorter = new FileSorter((p,n) -> p.compareTo(n), 98 | 8, 50000, 1024*1024*2, new File("/var/tmp/fileSorter"), false); 99 | long start = System.currentTimeMillis(); 100 | sorter.sort(file, dest); 101 | long end = System.currentTimeMillis(); 102 | System.out.println(end - start); 103 | ~~~ 104 | 105 | 该排序算法既有CPU计算又有IO,CPU计算时IO空闲,IO时CPU空间,如果把该方法改写成多线程版本,理论上可以提升不少速度。 106 | -------------------------------------------------------------------------------- /_posts/2015-04-09-rmi.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: post 3 | title: RMI 4 | tags: rmi Java 5 | categories: java 6 | published: false 7 | --- 8 | 9 | Java RMI 指的是远程方法调用 (Remote Method Invocation)。RMI能够让在某个 Java 虚拟机上的对象调用另一个 Java 虚拟机中的对象上的方法, 其威力体现在它强大的开发分布式网络应用的能力上,它可以被看作是RPC的Java版本。 10 | 11 | 与WebService相比,RMI编写代码更加简单,在小型应用开发上更加合适,相对地,RMI只能在java中使用,而WebSerce可以跨平台。 12 | 13 | RMI示意图: 14 | 15 | ![rmi][rmi] 16 | 17 | # Demo 18 | 19 | 定义接口,必须继承Remote 20 | 21 | ~~~java 22 | import java.rmi.Remote; 23 | import java.rmi.RemoteException; 24 | 25 | public interface HelloService extends Remote{ 26 | String sayHello() throws RemoteException; 27 | } 28 | ~~~ 29 | 30 | 实现接口,必须继承UnicastRemoteObject并在空构造中抛出RemoteException, 方法的返回值必须为原语类型或者序列化类型. 31 | 32 | ~~~java 33 | import java.rmi.RemoteException; 34 | import java.rmi.server.UnicastRemoteObject; 35 | 36 | public class HelloServiceImpl extends UnicastRemoteObject implements HelloService { 37 | 38 | protected HelloServiceImpl() throws RemoteException {} 39 | 40 | @Override 41 | public String sayHello() throws RemoteException { 42 | return "server says. 'Hey'"; 43 | } 44 | } 45 | ~~~ 46 | 47 | 注册服务 48 | 49 | ~~~java 50 | import java.net.MalformedURLException; 51 | import java.rmi.RemoteException; 52 | import java.rmi.registry.LocateRegistry; 53 | import java.rmi.registry.Registry; 54 | 55 | public class RegistryBook { 56 | public static void main(String[] args) throws RemoteException, MalformedURLException { 57 | Registry r = LocateRegistry.createRegistry(Registry.REGISTRY_PORT); 58 | r.rebind("HelloService", new HelloServiceImpl()); 59 | } 60 | } 61 | ~~~ 62 | 63 | 在另一台虚拟机上运行客户端代码,需要获得服务接口文件 64 | 65 | ~~~java 66 | import java.net.MalformedURLException; 67 | import java.rmi.Naming; 68 | import java.rmi.NotBoundException; 69 | import java.rmi.RemoteException; 70 | 71 | public class Client { 72 | public static void main(String args[]) throws RemoteException, MalformedURLException, NotBoundException { 73 | HelloService helloService = (HelloService) Naming.lookup("rmi://localhost:1099/HelloService"); 74 | System.out.println(helloService.sayHello()); 75 | } 76 | } 77 | ~~~ 78 | 79 | 注意,如果你使用的较为古老的jdk,你可能需要通过jdk内置的`rmic`命令生成`stub`以及`skeleton`文件,并将`stub`文件提供给客户端使用。 80 | 81 | [rmi]: {{"/rmi.jpg" | prepend: site.imgrepo }} 82 | -------------------------------------------------------------------------------- /_posts/2015-05-07-js-summary.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: post 3 | title: Javascript 总结 4 | tags: Javascript script 5 | categories: front-end 6 | published: false 7 | --- 8 | 9 | 超级长图,小心流量。 10 | 11 | ![javascript] [javascript] 12 | 13 | [javascript]: {{"/JavaScriptMindMap.jpg" | prepend: site.imgrepo }} -------------------------------------------------------------------------------- /_posts/2015-05-27-mvc-validator.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 记一次在spring-mvc中踩坑的经历 3 | tags: spring mvc java 4 | categories: java 5 | published: false 6 | --- 7 | 8 | 今天打算在项目中加入数据验证功能,具体可参考 [数据验证][validator] 9 | 10 | 过程无须赘述 11 | 12 | 配置完成后,奇怪的现象发生了,`BindingResult.hasErrors()`永远返回false 13 | ![hasErrors][hasErrors] 14 | 15 | 这是什么情况? validator 未指定?我赶紧检查配置: 16 | ![notNull][notNull] 17 | 18 | ![driven][driven] 19 | 配置没有问题,日志能够正常打印,且没有错误,排除类加载失败的可能。 20 | 21 | 上google搜索一下! 22 | ![google][google] 23 | 翻看了第一页的回复,大多来自stackoverflow,无非是让你引入包, 加配置, 我项目里配置符合这些回复的要求, 所以需要另想办法 24 | 25 | 自己动手吧!我加入如下代码,以验证validator是否被正确加载 26 | ![initBinder][initBinder] 27 | validator果然为null, 且如果拿到validator对象并set进去,就可以解决`BindingResult.hasErrors()`永远返回false的问题, 28 | 然而,这种处理方式需要在每个Controller中写一遍initBinder, 这种方式并不完美。 29 | 30 | 是什么原因造成validator丢失呢? 31 | 我们知道`annotation-driven`会在spring中自动注册多个bean, 详情请移步[自动注册][自动注册] 32 | 会不会是项目中存在某些配置覆盖了其中的bean,造成了validator丢失? 33 | 仔细扫描一下配置文件, 发现果然如此, 项目中为了更换json转换器, 自定义了一个bean 34 | ![adapter][adapter] 35 | 36 | 到了这个地步基本上找到原因了,解决方式很简单,将validator绑定起来就可以了 37 | ![validator-config][validator-config] 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | [validator]:http://jinnianshilongnian.iteye.com/blog/1733708 46 | [自动注册]:https://my.oschina.net/HeliosFly/blog/205343 47 | [hasErrors]:{{"/mvc-validator/hasErrors.jpg" | prepend: site.imgrepo }} 48 | [validator-config]:{{"/mvc-validator/validator-config.jpg" | prepend: site.imgrepo }} 49 | [google]:{{"/mvc-validator/google.jpg" | prepend: site.imgrepo }} 50 | [initBinder]:{{"/mvc-validator/initBinder.jpg" | prepend: site.imgrepo }} 51 | [adapter]:{{"/mvc-validator/adapter.jpg" | prepend: site.imgrepo }} 52 | [driven]:{{"/mvc-validator/driven.jpg" | prepend: site.imgrepo }} 53 | [notNull]:{{"/mvc-validator/notNull.jpg" | prepend: site.imgrepo }} 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /_posts/2015-05-28-large-file-diff-concurrent.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 大文件内容对比多线程版本 3 | tags: java 算法 algorithm 排序 concurrent 4 | categories: algorithm 5 | --- 6 | 7 | * TOC 8 | {:toc} 9 | 10 | 这是[上一篇][上一篇]的续作,对于这个算法,其中可以同时进行的部分有 11 | 1. 拆分后对每一个块的排序可以同时进行 12 | 2. 合并时的不同范围之间可以同时进行,例如拆分为10个小块,那么1-5小块的合并跟6-10小块的合并过程可以同时进行 13 | 3. 合并的不同阶段之间不可以同时进行,因为不同阶段之间有先后顺序 14 | 4. 不存在对同一条数据的修改,所以无需进行并发控制 15 | 16 | 17 | # 线程池 18 | 19 | ~~~java 20 | this.threadPoolExecutor = new ThreadPoolExecutor(8, 8, 10L, TimeUnit.SECONDS, 21 | new LinkedBlockingQueue<>(8), 22 | new CustomizableThreadFactory("fileSorterTPE-"), 23 | new ThreadPoolExecutor.CallerRunsPolicy()); 24 | ~~~ 25 | 26 | # 线程池消费拆分任务 27 | 28 | ~~~java 29 | List> splitFutureList = new ArrayList<>(); 30 | while (true){ 31 | line = br.readLine(); 32 | if(line != null){ 33 | chunkRows.add(line); 34 | } 35 | if(line == null || chunkRows.size() >= initialChunkSize){ 36 | if(chunkRows.size() > 0){ 37 | final int rn = rowNum; 38 | final List cr = chunkRows; 39 | rowNum += chunkRows.size(); 40 | chunkRows = new ArrayList<>(); 41 | Future chunk = threadPoolExecutor.submit(() -> { 42 | cr.sort(comparator); 43 | return initialChunk(rn, cr, file); 44 | }); 45 | splitFutureList.add(chunk); 46 | } 47 | } 48 | if(line == null){ 49 | break; 50 | } 51 | } 52 | chunkList = splitFutureList.stream().map(this::get).collect(Collectors.toList()); 53 | ~~~ 54 | 55 | 56 | # 线程池消费合并任务 57 | 58 | ~~~java 59 | int currentLevel = INITIAL_CHUNK_LEVEL; 60 | List> mergeFutureList = new ArrayList<>(); 61 | while (true) { 62 | //从队列中获取一组chunk 63 | List pollChunks = pollChunks(chunkQueue, currentLevel); 64 | //未取到同级chunk, 表示此级别应合并完成 65 | if (CollectionUtils.isEmpty(pollChunks)) { 66 | mergeFutureList.stream().map(this::get).forEach(chunkQueue::add); 67 | mergeFutureList.clear(); 68 | //chunkQueue 中只有一个元素,表示此次合并是最终合并 69 | if (chunkQueue.size() == 1) { 70 | break; 71 | } else { 72 | currentLevel++; 73 | continue; 74 | } 75 | } 76 | Future chunk = threadPoolExecutor.submit(() -> merge(pollChunks, original)); 77 | mergeFutureList.add(chunk); 78 | } 79 | ~~~ 80 | 81 | 可以看到合并任务与拆分任务有些不同,拆分任务是在循环退出后才执行`Future.get`,因为拆分不用考虑先后; 82 | 而合并任务在每次获取当前阶段的chunk结束时执行`Future.get`,这样才能避免不同的阶段之间产生混乱。 83 | 84 | [完整代码][完整代码] 85 | 86 | [上一篇]:https://bit-ranger.github.io/blog/algorithm/large-file-diff/ 87 | [完整代码]:https://github.com/bit-ranger/architecture/blob/d9083d2fb71763557e6d4eb6875f9c001fd41596/core/src/main/java/com/rainyalley/architecture/core/arithmetic/sort/FileSorter.java 88 | 89 | # 测试 90 | 91 | 在上一篇中,使用单线程,1千万条数据排序耗时13秒; 92 | 在同一台电脑上,使用多线程后,耗时6秒,时间减少了一半。 93 | 94 | 在拆分过程中,每个线程都要在内存中进行排序, 95 | 在拆分和合并过程中,每个线程都要持有自己的读写缓冲区,这无疑会增大内存的使用量。 96 | 97 | 究竟消耗了多少内存,我们可以使用`Java Mission Control`来观察,jdk8的bin目录下`jmc.exe`即为此工具。 98 | -------------------------------------------------------------------------------- /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bit-ranger/blog/04d656f78632289b7e09da4d4b9ec8d1b349fab3/favicon.ico -------------------------------------------------------------------------------- /feed.xml: -------------------------------------------------------------------------------- 1 | --- 2 | layout: null 3 | --- 4 | 5 | 6 | 7 | {{ site.title | xml_escape }} 8 | {{ site.description | xml_escape }} 9 | {{ site.url }}{{ site.baseurl }}/ 10 | 11 | {{ site.time | date_to_rfc822 }} 12 | {{ site.time | date_to_rfc822 }} 13 | Jekyll v{{ jekyll.version }} 14 | {% for post in site.posts limit:10 %} 15 | 16 | {{ post.title | xml_escape }} 17 | {{ post.content | xml_escape }} 18 | {{ post.date | date_to_rfc822 }} 19 | {{ post.url | prepend: site.baseurl | prepend: site.url }} 20 | {{ post.url | prepend: site.baseurl | prepend: site.url }} 21 | {% for tag in post.tags %} 22 | {{ tag | xml_escape }} 23 | {% endfor %} 24 | {% for cat in post.categories %} 25 | {{ cat | xml_escape }} 26 | {% endfor %} 27 | 28 | {% endfor %} 29 | 30 | 31 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | --- 4 | 5 | 6 |
7 |
8 |
9 |
10 | {% for post in paginator.posts %} 11 | 32 | {% endfor %} 33 |
34 | 35 |
36 | 37 | 58 |
59 |
60 |
-------------------------------------------------------------------------------- /pages/1archive.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: page 3 | title: Archive 4 | permalink: /archive/ 5 | icon: octicon-repo 6 | isNavItem: true 7 | styles: archive.css 8 | --- 9 | 10 |
11 | {% for post in site.posts %} 12 |
13 |
14 | Picture 15 |
16 | 17 |
18 |

{{ post.title }}

19 | {{ post.date | date: '%Y-%m-%d' }} 20 |
21 |
22 | {% endfor %} 23 |
-------------------------------------------------------------------------------- /pages/2category.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Category 4 | permalink: /category/ 5 | icon: octicon-list-unordered 6 | isNavItem: true 7 | styles : [article-list.css,category.css] 8 | scripts: [category.js] 9 | --- 10 | 11 |
12 | 13 | 14 |
15 | 16 | 37 | 38 | 39 |
40 |
41 | {% for post in site.posts %} 42 | 63 | {% endfor %} 64 |
65 | 66 | 67 | {% for category in site.categories %} 68 |
69 | {% for posts in category %} 70 | {% for post in posts %} 71 | {% if post.url %} 72 | 93 | {% endif %} 94 | {% endfor %} 95 | {% endfor %} 96 |
97 | {% endfor %} 98 |
99 | 100 |
101 |
-------------------------------------------------------------------------------- /pages/3tag.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: page 3 | title: Tag 4 | permalink: /tags/ 5 | icon: octicon-tag 6 | isNavItem: true 7 | styles: [tagCloud.css,tags.css] 8 | scripts: [tagCloud.js,tags.js] 9 | --- 10 | 11 |
12 | {% for tag in site.tags %} 13 | {{ tag | first }} 14 | {% endfor %} 15 |
16 | 17 |
18 | 23 |
24 | 25 |
26 | {% for tag in site.tags %} 27 |

{{ tag | first }}

28 |
    29 | {% for posts in tag %}{% for post in posts %}{% if post.title != null %} 30 |
  • » {{ post.title }}
  • 31 | {% endif %}{% endfor %}{% endfor %} 32 |
33 | {% endfor %} 34 |
35 | 36 | 37 | -------------------------------------------------------------------------------- /pages/4about.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: page 3 | title: About 4 | permalink: /about/ 5 | icon: octicon-heart 6 | --- 7 | 8 | 自我介绍什么的,最讨厌了! -------------------------------------------------------------------------------- /pages/5link-external.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: page 3 | title: Link 4 | permalink: /link/ 5 | icon: octicon-link-external 6 | 7 | --- 8 | 9 | ### [github](https://github.com/bit-ranger/blog) -------------------------------------------------------------------------------- /sitemap.xml: -------------------------------------------------------------------------------- 1 | --- 2 | layout: null 3 | --- 4 | 5 | 9 | {% for post in site.posts %} 10 | 11 | {{ post.url | prepend: site.baseurl | prepend: site.url }} 12 | {{ site.time | date_to_xmlschema }} 13 | weekly 14 | 15 | {% endfor %} 16 | {% for post in site.pages %}{% if post.isNavItem %} 17 | 18 | {{ post.url | prepend: site.baseurl | prepend: site.url }} 19 | {{ site.time | date_to_xmlschema }} 20 | weekly 21 | 22 | {% endif %} 23 | {% endfor %} 24 | -------------------------------------------------------------------------------- /static/bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bit-ranger/blog/04d656f78632289b7e09da4d4b9ec8d1b349fab3/static/bg.jpg -------------------------------------------------------------------------------- /static/css/archive.css: -------------------------------------------------------------------------------- 1 | 2 | /* -------------------------------- 3 | 4 | Modules - reusable parts of our design 5 | 6 | -------------------------------- */ 7 | .cd-container { 8 | /* this class is used to give a max-width to the element it is applied to, and center it horizontally when it reaches that max-width */ 9 | width: 90%; 10 | max-width: 1170px; 11 | margin: 0 auto; 12 | } 13 | .cd-container::after { 14 | /* clearfix */ 15 | content: ''; 16 | display: table; 17 | clear: both; 18 | } 19 | 20 | /* -------------------------------- 21 | 22 | Main components 23 | 24 | -------------------------------- */ 25 | 26 | #cd-timeline { 27 | position: relative; 28 | padding: 2em 0; 29 | margin-top: 2em; 30 | margin-bottom: 2em; 31 | } 32 | #cd-timeline::before { 33 | /* this is the vertical line */ 34 | content: ''; 35 | position: absolute; 36 | top: 0; 37 | left: 18px; 38 | height: 100%; 39 | width: 4px; 40 | background: #d7e4ed; 41 | } 42 | @media only screen and (min-width: 1170px) { 43 | #cd-timeline { 44 | margin-top: 3em; 45 | margin-bottom: 3em; 46 | } 47 | #cd-timeline::before { 48 | left: 50%; 49 | margin-left: -2px; 50 | } 51 | } 52 | 53 | .cd-timeline-block { 54 | position: relative; 55 | margin: 2em 0; 56 | } 57 | .cd-timeline-block:after { 58 | content: ""; 59 | display: table; 60 | clear: both; 61 | } 62 | .cd-timeline-block:first-child { 63 | margin-top: 0; 64 | } 65 | .cd-timeline-block:last-child { 66 | margin-bottom: 0; 67 | } 68 | @media only screen and (min-width: 1170px) { 69 | .cd-timeline-block { 70 | margin: 4em 0; 71 | } 72 | .cd-timeline-block:first-child { 73 | margin-top: 0; 74 | } 75 | .cd-timeline-block:last-child { 76 | margin-bottom: 0; 77 | } 78 | } 79 | 80 | .cd-timeline-img { 81 | position: absolute; 82 | top: 0; 83 | left: 0; 84 | width: 40px; 85 | height: 40px; 86 | border-radius: 50%; 87 | box-shadow: 0 0 0 4px white, inset 0 2px 0 rgba(0, 0, 0, 0.08), 0 3px 0 4px rgba(0, 0, 0, 0.05); 88 | } 89 | .cd-timeline-img img { 90 | display: block; 91 | width: 24px; 92 | height: 24px; 93 | position: relative; 94 | left: 50%; 95 | top: 50%; 96 | margin-left: -12px; 97 | margin-top: -12px; 98 | } 99 | .cd-timeline-img.cd-picture { 100 | background: #75ce66; 101 | } 102 | .cd-timeline-img.cd-movie { 103 | background: #c03b44; 104 | } 105 | .cd-timeline-img.cd-location { 106 | background: #f0ca45; 107 | } 108 | @media only screen and (min-width: 1170px) { 109 | .cd-timeline-img { 110 | width: 40px; 111 | height: 40px; 112 | left: 50%; 113 | margin-left: -20px; 114 | /* Force Hardware Acceleration in WebKit */ 115 | -webkit-transform: translateZ(0); 116 | -webkit-backface-visibility: hidden; 117 | } 118 | .cssanimations .cd-timeline-img.is-hidden { 119 | visibility: hidden; 120 | } 121 | .cssanimations .cd-timeline-img.bounce-in { 122 | visibility: visible; 123 | -webkit-animation: cd-bounce-1 0.6s; 124 | -moz-animation: cd-bounce-1 0.6s; 125 | animation: cd-bounce-1 0.6s; 126 | } 127 | } 128 | 129 | .cd-timeline-content { 130 | position: relative; 131 | margin-left: 60px; 132 | background: white; 133 | border-radius: 0.25em; 134 | padding: 1em; 135 | box-shadow: 0 3px 0 #d7e4ed; 136 | } 137 | .cd-timeline-content:after { 138 | content: ""; 139 | display: table; 140 | clear: both; 141 | } 142 | .cd-timeline-content a { 143 | color: #303e49; 144 | } 145 | .cd-timeline-content p, .cd-timeline-content .cd-read-more, .cd-timeline-content .cd-date { 146 | font-size: 13px; 147 | } 148 | .cd-timeline-content .cd-read-more, .cd-timeline-content .cd-date { 149 | display: inline-block; 150 | } 151 | .cd-timeline-content p { 152 | margin: 1em 0; 153 | line-height: 1.6; 154 | } 155 | .cd-timeline-content .cd-read-more { 156 | float: right; 157 | padding: .8em 1em; 158 | background: #acb7c0; 159 | color: white; 160 | border-radius: 0.25em; 161 | } 162 | .no-touch .cd-timeline-content .cd-read-more:hover { 163 | background-color: #bac4cb; 164 | } 165 | a.cd-read-more:hover{text-decoration:none; background-color: #424242; } 166 | .cd-timeline-content .cd-date { 167 | float: left; 168 | padding: .8em 0; 169 | opacity: .7; 170 | } 171 | .cd-timeline-content::before { 172 | content: ''; 173 | position: absolute; 174 | top: 16px; 175 | right: 100%; 176 | height: 0; 177 | width: 0; 178 | border: 7px solid transparent; 179 | border-right: 7px solid white; 180 | } 181 | @media only screen and (min-width: 768px) { 182 | .cd-timeline-content p { 183 | font-size: 16px; 184 | } 185 | .cd-timeline-content .cd-read-more, .cd-timeline-content .cd-date { 186 | font-size: 14px; 187 | } 188 | } 189 | @media only screen and (min-width: 1170px) { 190 | .cd-timeline-content { 191 | margin-left: 0; 192 | padding: 1.6em; 193 | width: 45%; 194 | } 195 | .cd-timeline-content::before { 196 | top: 24px; 197 | left: 100%; 198 | border-color: transparent; 199 | border-left-color: white; 200 | } 201 | .cd-timeline-content .cd-read-more { 202 | float: left; 203 | } 204 | .cd-timeline-content .cd-date { 205 | position: absolute; 206 | width: 100%; 207 | left: 122%; 208 | top: 6px; 209 | font-size: 16px; 210 | } 211 | .cd-timeline-block:nth-child(even) .cd-timeline-content { 212 | float: right; 213 | } 214 | .cd-timeline-block:nth-child(even) .cd-timeline-content::before { 215 | top: 24px; 216 | left: auto; 217 | right: 100%; 218 | border-color: transparent; 219 | border-right-color: white; 220 | } 221 | .cd-timeline-block:nth-child(even) .cd-timeline-content .cd-read-more { 222 | float: right; 223 | } 224 | .cd-timeline-block:nth-child(even) .cd-timeline-content .cd-date { 225 | left: auto; 226 | right: 122%; 227 | text-align: right; 228 | } 229 | .cssanimations .cd-timeline-content.is-hidden { 230 | visibility: hidden; 231 | } 232 | .cssanimations .cd-timeline-content.bounce-in { 233 | visibility: visible; 234 | -webkit-animation: cd-bounce-2 0.6s; 235 | -moz-animation: cd-bounce-2 0.6s; 236 | animation: cd-bounce-2 0.6s; 237 | } 238 | } 239 | 240 | @media only screen and (min-width: 1170px) { 241 | /* inverse bounce effect on even content blocks */ 242 | .cssanimations .cd-timeline-block:nth-child(even) .cd-timeline-content.bounce-in { 243 | -webkit-animation: cd-bounce-2-inverse 0.6s; 244 | -moz-animation: cd-bounce-2-inverse 0.6s; 245 | animation: cd-bounce-2-inverse 0.6s; 246 | } 247 | } 248 | -------------------------------------------------------------------------------- /static/css/article-list.css: -------------------------------------------------------------------------------- 1 | article{ 2 | padding:6px 0px 8px 0px; 3 | margin-top: 3em; 4 | } 5 | article header{ 6 | text-decoration: none; 7 | text-transform: uppercase; 8 | letter-spacing: 2px; 9 | margin-bottom: 1em; 10 | margin-left: 5px; 11 | line-height: 1em; 12 | text-shadow: 0 1px #fff; 13 | font-weight: bold; 14 | display: block; 15 | } 16 | 17 | article header a{ 18 | color: #666; 19 | } 20 | 21 | article .module { 22 | position: relative; 23 | width: 100%; 24 | overflow: hidden; 25 | padding: 25px; 26 | border-bottom: 1px solid #b1b1b1; 27 | background: #f8f8fd; 28 | 29 | -webkit-box-shadow: 0 1px 2px rgba(0,0,0,0.4),0 0 30px rgba(10,10,0,0.1) inset; 30 | -moz-box-shadow: 0 1px 2px rgba(0,0,0,0.4),0 0 30px rgba(10,10,0,0.1) inset; 31 | -o-box-shadow: 0 1px 2px rgba(0,0,0,0.4),0 0 30px rgba(10,10,0,0.1) inset; 32 | box-shadow: 0 1px 2px rgba(0,0,0,0.4),0 0 30px rgba(10,10,0,0.1) inset; 33 | } 34 | article .title{ 35 | font-size: 1.6em; 36 | font-weight: 500; 37 | display: block; 38 | margin-bottom: 10px; 39 | 40 | margin-left: -25px; 41 | padding-left: 20px; 42 | border-left: 5px solid #2ca6cb; 43 | } 44 | 45 | article .readmore{ 46 | display: inline-block; 47 | line-height: 1em; 48 | padding: 6px 15px; 49 | -webkit-border-radius: 15px; 50 | border-radius: 15px; 51 | background: #eee; 52 | color: #999; 53 | text-shadow: 0 1px #fff; 54 | text-decoration: none; 55 | margin-top: 10px; 56 | margin-bottom: 20px; 57 | } 58 | article .readmore:hover{ 59 | background: #258fb8; 60 | color: #fff; 61 | text-decoration: none; 62 | text-shadow: 0 1px #1e7293; 63 | } 64 | 65 | article footer{ 66 | font-size: .85em; 67 | line-height: 1.6em; 68 | border-top: 1px solid #ddd; 69 | padding-top: 1.6em; 70 | margin: 0; 71 | } 72 | article footer a{ 73 | color: #999; 74 | margin-right: 15px; 75 | } -------------------------------------------------------------------------------- /static/css/category.css: -------------------------------------------------------------------------------- 1 | section.category-slice{ 2 | display: none; 3 | } 4 | .categories-item{ 5 | margin-top: 0.5em; 6 | } 7 | .categories-item .categories-badge{ 8 | font-size: 10px; 9 | color: #fff; 10 | background-color: #999; 11 | padding: 0 7px 1px 7px; 12 | border-radius: 9px; 13 | float: right; 14 | transition:0.4s ease; 15 | -webkit-transition:0.4s ease; 16 | -moz-transition:0.4s ease; 17 | -o-transition:0.4s ease; 18 | } 19 | .categories-item:hover .categories-badge{ 20 | -webkit-transform:rotate(360deg) scale(1.2); 21 | -moz-transform:rotate(360deg) scale(1.2); 22 | -o-transform:rotate(360deg) scale(1.2); 23 | -ms-transform:rotate(360deg) scale(1.2); 24 | transform:rotate(360deg) scale(1.2); 25 | } 26 | 27 | .dropdown .btn, 28 | .dropdown .dropdown-menu{ 29 | font-size: 17px; 30 | } 31 | -------------------------------------------------------------------------------- /static/css/highlight.css: -------------------------------------------------------------------------------- 1 | .highlight .c { color: #999988; font-style: italic } /* Comment */ 2 | .highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */ 3 | .highlight .k { color: #000000; font-weight: bold } /* Keyword */ 4 | .highlight .o { color: #000000; font-weight: bold } /* Operator */ 5 | .highlight .cm { color: #999988; font-style: italic } /* Comment.Multiline */ 6 | .highlight .cp { color: #999999; font-weight: bold; font-style: italic } /* Comment.Preproc */ 7 | .highlight .c1 { color: #999988; font-style: italic } /* Comment.Single */ 8 | .highlight .cs { color: #999999; font-weight: bold; font-style: italic } /* Comment.Special */ 9 | .highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */ 10 | .highlight .ge { color: #000000; font-style: italic } /* Generic.Emph */ 11 | .highlight .gr { color: #aa0000 } /* Generic.Error */ 12 | .highlight .gh { color: #999999 } /* Generic.Heading */ 13 | .highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */ 14 | .highlight .go { color: #888888 } /* Generic.Output */ 15 | .highlight .gp { color: #555555 } /* Generic.Prompt */ 16 | .highlight .gs { font-weight: bold } /* Generic.Strong */ 17 | .highlight .gu { color: #aaaaaa } /* Generic.Subheading */ 18 | .highlight .gt { color: #aa0000 } /* Generic.Traceback */ 19 | .highlight .kc { color: #000000; font-weight: bold } /* Keyword.Constant */ 20 | .highlight .kd { color: #000000; font-weight: bold } /* Keyword.Declaration */ 21 | .highlight .kn { color: #000000; font-weight: bold } /* Keyword.Namespace */ 22 | .highlight .kp { color: #000000; font-weight: bold } /* Keyword.Pseudo */ 23 | .highlight .kr { color: #000000; font-weight: bold } /* Keyword.Reserved */ 24 | .highlight .kt { color: #445588; font-weight: bold } /* Keyword.Type */ 25 | .highlight .m { color: #009999 } /* Literal.Number */ 26 | .highlight .s { color: #d01040 } /* Literal.String */ 27 | .highlight .na { color: #008080 } /* Name.Attribute */ 28 | .highlight .nb { color: #0086B3 } /* Name.Builtin */ 29 | .highlight .nc { color: #445588; font-weight: bold } /* Name.Class */ 30 | .highlight .no { color: #008080 } /* Name.Constant */ 31 | .highlight .nd { color: #3c5d5d; font-weight: bold } /* Name.Decorator */ 32 | .highlight .ni { color: #800080 } /* Name.Entity */ 33 | .highlight .ne { color: #990000; font-weight: bold } /* Name.Exception */ 34 | .highlight .nf { color: #990000; font-weight: bold } /* Name.Function */ 35 | .highlight .nl { color: #990000; font-weight: bold } /* Name.Label */ 36 | .highlight .nn { color: #555555 } /* Name.Namespace */ 37 | .highlight .nt { color: #000080 } /* Name.Tag */ 38 | .highlight .nv { color: #008080 } /* Name.Variable */ 39 | .highlight .ow { color: #000000; font-weight: bold } /* Operator.Word */ 40 | .highlight .w { color: #bbbbbb } /* Text.Whitespace */ 41 | .highlight .mf { color: #009999 } /* Literal.Number.Float */ 42 | .highlight .mh { color: #009999 } /* Literal.Number.Hex */ 43 | .highlight .mi { color: #009999 } /* Literal.Number.Integer */ 44 | .highlight .mo { color: #009999 } /* Literal.Number.Oct */ 45 | .highlight .sb { color: #d01040 } /* Literal.String.Backtick */ 46 | .highlight .sc { color: #d01040 } /* Literal.String.Char */ 47 | .highlight .sd { color: #d01040 } /* Literal.String.Doc */ 48 | .highlight .s2 { color: #d01040 } /* Literal.String.Double */ 49 | .highlight .se { color: #d01040 } /* Literal.String.Escape */ 50 | .highlight .sh { color: #d01040 } /* Literal.String.Heredoc */ 51 | .highlight .si { color: #d01040 } /* Literal.String.Interpol */ 52 | .highlight .sx { color: #d01040 } /* Literal.String.Other */ 53 | .highlight .sr { color: #009926 } /* Literal.String.Regex */ 54 | .highlight .s1 { color: #d01040 } /* Literal.String.Single */ 55 | .highlight .ss { color: #990073 } /* Literal.String.Symbol */ 56 | .highlight .bp { color: #999999 } /* Name.Builtin.Pseudo */ 57 | .highlight .vc { color: #008080 } /* Name.Variable.Class */ 58 | .highlight .vg { color: #008080 } /* Name.Variable.Global */ 59 | .highlight .vi { color: #008080 } /* Name.Variable.Instance */ 60 | .highlight .il { color: #009999 } /* Literal.Number.Integer.Long */ 61 | .highlight .lineno{ color: rgba(0,0,0,0.3); border-right: solid 1px rgba(0,0,0,0.3); padding-right: 5px;} 62 | 63 | 64 | -------------------------------------------------------------------------------- /static/css/post.css: -------------------------------------------------------------------------------- 1 | .post table{ 2 | border-top:2px solid #777; 3 | border-bottom: 2px solid #777; 4 | margin: 8px 0; 5 | } 6 | .post table thead{ 7 | border-bottom: 1px dashed #777; 8 | background-color: #aaa; 9 | color:#fff; 10 | } 11 | .post table th{ 12 | padding: 2px 10px; 13 | } 14 | .post table tr:nth-child(2n){ 15 | background-color: #E5EAED; 16 | } 17 | .post table td{ 18 | padding: 2px 10px; 19 | vertical-align: top; 20 | } 21 | 22 | /* post 23 | * post articles area 24 | */ 25 | .post header{ 26 | margin-left: -30px; 27 | padding-left: 25px; 28 | border-left: 5px solid #2ca6cb; 29 | line-height: 1.5; 30 | } 31 | .post header h2{ 32 | color: #2ca6cb; 33 | } 34 | .post header p.post-meta{ 35 | color: #817c7c; 36 | } 37 | 38 | .post header p.post-tag a{ 39 | display: inline-block; 40 | background: #2ca6cb; 41 | color: #fff; 42 | padding: 2px 5px 0px 5px; 43 | border: 1px solid #2ca6cb; 44 | margin-top: 2px; 45 | } 46 | 47 | .post img{ 48 | max-width: 100%; 49 | vertical-align: middle; 50 | 51 | -webkit-box-shadow: 0 0 5px 2px rgba(0,0,0,0.1); 52 | -moz-box-shadow: 0 0 5px 2px rgba(0,0,0,0.1); 53 | -o-box-shadow: 0 0 5px 2px rgba(0,0,0,0.1); 54 | box-shadow: 0 0 5px 2px rgba(0,0,0,0.1); 55 | } 56 | /** 57 | * Blockquotes 58 | */ 59 | .post blockquote { 60 | border-left: 5px solid #D6DBDF; 61 | background: rgba(112,138,153,.1); 62 | font-size: 100%; 63 | } 64 | .post blockquote:last-child { 65 | margin-bottom: 0; 66 | } 67 | 68 | .post pre { 69 | overflow: auto; 70 | word-wrap: normal; 71 | white-space: pre; 72 | } 73 | 74 | .post pre code{ 75 | white-space: pre; 76 | } 77 | 78 | @media screen and (min-width: 1200px){ 79 | /** 80 | * 文章目录相关 81 | */ 82 | .post ul#markdown-toc { 83 | display: none!important; 84 | } 85 | } 86 | 87 | @media screen and (max-width: 1199px){ 88 | .content-navigation{ 89 | display: none!important; 90 | } 91 | 92 | .post pre code{ 93 | font-size: 85%; 94 | } 95 | } 96 | 97 | @media screen and (max-width: 991px){ 98 | .post pre code{ 99 | font-size: 70%; 100 | } 101 | } 102 | 103 | @media screen and (max-width: 767px){ 104 | .post header{ 105 | margin-left: -10px; 106 | padding-left: 5px; 107 | } 108 | .post pre code{ 109 | font-size: 55%; 110 | } 111 | } 112 | 113 | 114 | .content-navigation a:link {color: #333;text-decoration: none;} /* 未访问的链接 */ 115 | .content-navigation a:visited {color: #99b } /* 已访问的链接 */ 116 | .content-navigation a:hover {color: #99b;} /* 鼠标移动到链接上 */ 117 | .content-navigation a:active {color: #99b} /* 选定的链接 */ 118 | 119 | .content-navigation{ 120 | max-width: 292px; 121 | padding-left: 30px; 122 | } 123 | .content-navigation-header { 124 | display: block; 125 | border-bottom: .1875em solid #ccc; 126 | font-size: 20px; 127 | color: #2ca6cb;; 128 | margin-left: 15px; 129 | margin-right: 15px; 130 | padding-top: 15px; 131 | padding-right: 15px; 132 | } 133 | .content-navigation .content-navigation-list{ 134 | padding: 10px 0; 135 | } 136 | 137 | .content-navigation .content-navigation-list ul li{ 138 | line-height: 25px; 139 | word-break: break-all; 140 | } 141 | 142 | .boundary{ 143 | border-bottom: .1875em solid #ccc 144 | } 145 | 146 | 147 | .clickable-header { 148 | cursor:pointer; 149 | } 150 | .clickable-header:hover:after { 151 | content: '\f05c'; 152 | margin-left: 10px; 153 | 154 | font: normal normal normal smaller/1 octicons; 155 | display: inline-block; 156 | text-decoration: none; 157 | text-rendering: auto; 158 | -webkit-font-smoothing: antialiased; 159 | -moz-osx-font-smoothing: grayscale; 160 | -webkit-user-select: none; 161 | -moz-user-select: none; 162 | -ms-user-select: none; 163 | user-select: none; 164 | } -------------------------------------------------------------------------------- /static/css/style.css: -------------------------------------------------------------------------------- 1 | --- 2 | layout: null 3 | --- 4 | 5 | .navbar-tiffany, 6 | .banner{ 7 | background: -webkit-linear-gradient(45deg, rgba(0 , 0, 0, 0) 48%, rgba(0 , 0, 0, 0.1) 50%, rgba(0 , 0, 0, 0) 52%), 8 | -webkit-linear-gradient(135deg, rgba(0 , 0, 0, 0) 48%, rgba(0 , 0, 0, 0.1) 50%, rgba(0 , 0, 0, 0) 52%); 9 | background: linear-gradient(45deg, rgba(0 , 0, 0, 0) 48%, rgba(0 , 0, 0, 0.1) 50%, rgba(0 , 0, 0, 0) 52%), 10 | linear-gradient(-45deg, rgba(0 , 0, 0, 0) 48%, rgba(0 , 0, 0, 0.1) 50%, rgba(0 , 0, 0, 0) 52%); 11 | -webkit-background-size: 1em 1em; 12 | background-size: 1em 1em; 13 | } 14 | 15 | .navbar-tiffany{ 16 | background-color: #2ca6cb; 17 | border-top: 1px solid rgba(255,255,255,0.2); 18 | border-bottom: 1px solid rgba(0,0,0,0.1); 19 | box-shadow: 2px 4px 5px rgba(3,3,3,0.2); 20 | } 21 | 22 | .footnote-tiffany{ 23 | background: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0) 48%, rgba(255, 255, 255, 0.03) 50%, rgba(255, 255, 255, 0) 52%), 24 | -webkit-linear-gradient(135deg, rgba(255, 255, 255, 0) 48%, rgba(255, 255, 255, 0.03) 50%, rgba(255, 255, 255, 0) 52%); 25 | background: linear-gradient(45deg, rgba(255, 255, 255, 0) 48%, rgba(255, 255, 255, 0.03) 50%, rgba(255, 255, 255, 0) 52%), 26 | linear-gradient(-45deg, rgba(255, 255, 255, 0) 48%, rgba(255, 255, 255, 0.03) 50%, rgba(255, 255, 255, 0) 52%); 27 | -webkit-background-size: 1em 1em; 28 | background-size: 1em 1em; 29 | } 30 | .footnote-tiffany{ 31 | background-color: #333333; 32 | border-color: #2ca6cb; 33 | box-shadow: 2px -4px 5px rgba(3,-3,3,0.2); 34 | } 35 | 36 | 37 | .navbar-tiffany .navbar-brand, 38 | .navbar-tiffany .navbar-text, 39 | .navbar-tiffany .nav>li>a{ 40 | color: #fff; 41 | } 42 | 43 | 44 | .navbar-tiffany .nav>li>a:hover, 45 | .navbar-tiffany .nav>li>a:focus, 46 | .navbar-tiffany .nav>li.active>a { 47 | background: rgba(0, 0, 0, 0.2); 48 | color: #fff; 49 | } 50 | .navbar-tiffany .navbar-toggle{ 51 | border-color: #fff; 52 | } 53 | 54 | .navbar-tiffany .navbar-toggle .icon-bar{ 55 | background-color: #fff; 56 | } 57 | 58 | .navbar-nav>li>a{ 59 | line-height: 1.42857143; 60 | } 61 | 62 | .footnote-tiffany a, 63 | .footnote-tiffany span{ 64 | color:#fff; 65 | 66 | } 67 | .footnote .foot-item{ 68 | display: inline-block; 69 | width: 28px; 70 | height: 28px; 71 | line-height: 28px; 72 | font-size: 18px; 73 | text-align: center; 74 | background: rgba(255,255,255,0.1); 75 | border-radius: 3px 3px; 76 | -webkit-transition: background .4s ease-in-out; 77 | -moz-transition: background .4s ease-in-out; 78 | -ms-transition: background .4s ease-in-out; 79 | -o-transition: background .4s ease-in-out; 80 | transition: background .4s ease-in-out; 81 | } 82 | 83 | .footnote .foot-item:hover{ 84 | background-color: #2ca6cb; 85 | -webkit-transition: background .4s ease-in-out; 86 | -moz-transition: background .4s ease-in-out; 87 | -ms-transition: background .4s ease-in-out; 88 | -o-transition: background .4s ease-in-out; 89 | transition: background .4s ease-in-out; 90 | } 91 | 92 | /* footer */ 93 | .footnote{ 94 | min-height: 50px; 95 | line-height: 50px; 96 | margin-top: -50px; 97 | text-align: center; 98 | } 99 | 100 | html{ 101 | height: 100%; 102 | padding: 0px 0px 0px 0px; 103 | margin: 0px 0px 0px 0px; 104 | } 105 | body { 106 | background-image: url({{ "/static/bg.jpg" | prepend: site.baseurl | prepend: site.url }}); 107 | height: 100%; 108 | padding: 0px 0px 0px 0px; 109 | margin: 0px 0px 0px 0px; 110 | } 111 | 112 | body,code,pre{ 113 | font-family: "Menlo", "Monaco", "Consolas", "Courier New", "monospace", "Helvetica", "Tahoma", "Arial", 114 | "WenQuanYi Micro Hei", "文泉驿微米黑", "STXihei", "华文细黑", "Microsoft YaHei", "微软雅黑", "SimSun", "宋体", "Heiti", "黑体", sans-serif; 115 | } 116 | 117 | body,.sheet,pre,code{ 118 | font-size: 17px; 119 | } 120 | 121 | 122 | a:link {text-decoration: none} /* 未访问的链接 */ 123 | a:visited {text-decoration: none} /* 已访问的链接 */ 124 | a:hover {text-decoration: none} /* 鼠标移动到链接上 */ 125 | a:active {text-decoration: none} /* 选定的链接 */ 126 | 127 | .main{ 128 | min-height: 100%; 129 | padding-bottom: 130px; 130 | } 131 | 132 | .content{ 133 | padding-right: 0px; 134 | padding-left: 0px; 135 | } 136 | 137 | .sheet{ 138 | word-break: break-all; 139 | background-color:#f8f8fd; 140 | -webkit-box-shadow: 0 1px 2px rgba(0,0,0,0.4),0 0 30px rgba(10,10,0,0.1) inset; 141 | -moz-box-shadow: 0 1px 2px rgba(0,0,0,0.4),0 0 30px rgba(10,10,0,0.1) inset; 142 | -o-box-shadow: 0 1px 2px rgba(0,0,0,0.4),0 0 30px rgba(10,10,0,0.1) inset; 143 | box-shadow: 0 1px 2px rgba(0,0,0,0.4),0 0 30px rgba(10,10,0,0.1) inset; 144 | } 145 | 146 | .sheet{ 147 | padding:10px 30px; 148 | } 149 | 150 | @media screen and (max-width: 767px) { 151 | .sheet{ 152 | padding-left: 10px; 153 | padding-right: 10px; 154 | } 155 | } 156 | 157 | /** 158 | * back to top 159 | */ 160 | .page-scrollTop { 161 | position: fixed; 162 | right: 10px; 163 | bottom: 10px; 164 | width: 60px; 165 | height: 60px; 166 | background-color: #bbb; 167 | border-radius:7px; 168 | opacity: 0.7; 169 | display: none; 170 | z-index: 888; 171 | } 172 | .page-scrollTop .arrow{ 173 | margin:0 auto; 174 | padding-top:11px; 175 | width:0; 176 | height: 0; 177 | border-right: 15px solid transparent; 178 | border-left: 15px solid transparent; 179 | border-bottom: 15px solid #7f7f7f; 180 | 181 | } 182 | .page-scrollTop .stick{ 183 | margin:0 auto; 184 | padding-bottom:21px; 185 | width: 13px; 186 | border-bottom: 13px solid #bbb; 187 | background-color: #7f7f7f; 188 | } 189 | 190 | .shadow-bottom-center{ 191 | background-color:#f8f8fd; 192 | -webkit-box-shadow: 0 1px 5px rgba(0,0,0,0.4),0 0 20px rgba(0,0,0,0.1) inset; 193 | -moz-box-shadow: 0 1px 5px rgba(0,0,0,0.4),0 0 20px rgba(0,0,0,0.1) inset; 194 | -o-box-shadow: 0 1px 5px rgba(0,0,0,0.4),0 0 20px rgba(0,0,0,0.1) inset; 195 | box-shadow: 0 1px 5px rgba(0,0,0,0.4),0 0 20px rgba(0,0,0,0.1) inset; 196 | } 197 | 198 | .word-keep{ 199 | display: inline-block; 200 | word-break: keep-all; 201 | } 202 | 203 | .rectangle{ 204 | border-radius: 0px; 205 | } 206 | 207 | 208 | @media (min-width: 1200px){ 209 | .col-lg-offset-1_5 { 210 | margin-left: 12.5%; 211 | } 212 | } 213 | 214 | .pad-min, 215 | .pad-mid, 216 | .pad-lar{ 217 | display: block; 218 | } 219 | .pad-min{ 220 | height: 20px; 221 | } 222 | .pad-mid{ 223 | height: 50px; 224 | } 225 | .pad-lar{ 226 | height: 80px; 227 | } 228 | .scriptHub{ 229 | display: none!important; 230 | } 231 | 232 | .st-ui-stamp{ 233 | display: none!important; 234 | } 235 | 236 | /*** banner ***/ 237 | .banner { 238 | width: 100%; 239 | padding-top: 15px; 240 | padding-bottom: 15px; 241 | background-color: #2ca6cb; 242 | display: none; 243 | } 244 | 245 | .banner h1{ 246 | font-size: 50px; 247 | font-weight: bold; 248 | } 249 | 250 | 251 | .banner h1, 252 | .banner h3{ 253 | color: #ffffff; 254 | text-align: center; 255 | } 256 | .banner h3{ 257 | line-height: 1.5; 258 | } 259 | 260 | @media screen and (max-width: 767px) { 261 | .banner h1{ 262 | font-size: 36px; 263 | } 264 | } 265 | -------------------------------------------------------------------------------- /static/css/tagCloud.css: -------------------------------------------------------------------------------- 1 | .tagCloud { 2 | position:relative; 3 | display: block; 4 | width: 400px; 5 | height: 400px; 6 | margin: 20px auto 0; 7 | } 8 | .tagCloud a { 9 | position:absolute; 10 | top:0px; 11 | left:0px; 12 | text-decoration:none; 13 | padding: 3px 6px; 14 | color: #2ca6cb; 15 | } 16 | .tagCloud a:hover{ 17 | color: orchid; 18 | } 19 | 20 | @media screen and (max-width: 768px){ 21 | .tagCloud { 22 | display: none; 23 | } 24 | } -------------------------------------------------------------------------------- /static/css/tags.css: -------------------------------------------------------------------------------- 1 | .tag-box { 2 | list-style: none; 3 | margin: 0; 4 | padding: 4px 0; 5 | overflow: hidden; 6 | *zoom: 1; 7 | } 8 | 9 | .tag-box:before, .tag-box:after { 10 | display: table; 11 | content: ""; 12 | line-height: 0; 13 | } 14 | 15 | .tag-box:after { 16 | clear: both; 17 | } 18 | 19 | .tag-box.inline li { 20 | float: left; 21 | font-size: 14px; 22 | font-size: 1em; 23 | line-height: 2.1; 24 | } 25 | 26 | .tag-box a { 27 | padding: 4px 6px; 28 | margin: 2px; 29 | background-color: #e6e6e6; 30 | -webkit-border-radius: 4px; 31 | -moz-border-radius: 4px; 32 | border-radius: 2px; 33 | text-decoration: none; 34 | } 35 | 36 | .tag-box a span { 37 | vertical-align: super; 38 | font-size: 10px; 39 | font-size: 0.875rem; 40 | } -------------------------------------------------------------------------------- /static/img/JavaScriptMindMap.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bit-ranger/blog/04d656f78632289b7e09da4d4b9ec8d1b349fab3/static/img/JavaScriptMindMap.jpg -------------------------------------------------------------------------------- /static/img/bg-canvas_bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bit-ranger/blog/04d656f78632289b7e09da4d4b9ec8d1b349fab3/static/img/bg-canvas_bg.jpg -------------------------------------------------------------------------------- /static/img/bitmap.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bit-ranger/blog/04d656f78632289b7e09da4d4b9ec8d1b349fab3/static/img/bitmap.jpg -------------------------------------------------------------------------------- /static/img/boundary-A.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bit-ranger/blog/04d656f78632289b7e09da4d4b9ec8d1b349fab3/static/img/boundary-A.png -------------------------------------------------------------------------------- /static/img/boundary-B.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bit-ranger/blog/04d656f78632289b7e09da4d4b9ec8d1b349fab3/static/img/boundary-B.png -------------------------------------------------------------------------------- /static/img/boundary-C.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bit-ranger/blog/04d656f78632289b7e09da4d4b9ec8d1b349fab3/static/img/boundary-C.png -------------------------------------------------------------------------------- /static/img/http-get.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bit-ranger/blog/04d656f78632289b7e09da4d4b9ec8d1b349fab3/static/img/http-get.png -------------------------------------------------------------------------------- /static/img/http-post.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bit-ranger/blog/04d656f78632289b7e09da4d4b9ec8d1b349fab3/static/img/http-post.png -------------------------------------------------------------------------------- /static/img/i0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bit-ranger/blog/04d656f78632289b7e09da4d4b9ec8d1b349fab3/static/img/i0.jpg -------------------------------------------------------------------------------- /static/img/icon-picture.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 9 | 11 | 12 | -------------------------------------------------------------------------------- /static/img/io-match-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bit-ranger/blog/04d656f78632289b7e09da4d4b9ec8d1b349fab3/static/img/io-match-2.jpg -------------------------------------------------------------------------------- /static/img/io-match.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bit-ranger/blog/04d656f78632289b7e09da4d4b9ec8d1b349fab3/static/img/io-match.jpg -------------------------------------------------------------------------------- /static/img/ip.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bit-ranger/blog/04d656f78632289b7e09da4d4b9ec8d1b349fab3/static/img/ip.jpg -------------------------------------------------------------------------------- /static/img/java-system.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bit-ranger/blog/04d656f78632289b7e09da4d4b9ec8d1b349fab3/static/img/java-system.png -------------------------------------------------------------------------------- /static/img/jvm-arg0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bit-ranger/blog/04d656f78632289b7e09da4d4b9ec8d1b349fab3/static/img/jvm-arg0.png -------------------------------------------------------------------------------- /static/img/jvm-arg1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bit-ranger/blog/04d656f78632289b7e09da4d4b9ec8d1b349fab3/static/img/jvm-arg1.png -------------------------------------------------------------------------------- /static/img/jvm-gc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bit-ranger/blog/04d656f78632289b7e09da4d4b9ec8d1b349fab3/static/img/jvm-gc.png -------------------------------------------------------------------------------- /static/img/jvm-handle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bit-ranger/blog/04d656f78632289b7e09da4d4b9ec8d1b349fab3/static/img/jvm-handle.png -------------------------------------------------------------------------------- /static/img/jvm-point.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bit-ranger/blog/04d656f78632289b7e09da4d4b9ec8d1b349fab3/static/img/jvm-point.png -------------------------------------------------------------------------------- /static/img/jvm-runtime.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bit-ranger/blog/04d656f78632289b7e09da4d4b9ec8d1b349fab3/static/img/jvm-runtime.png -------------------------------------------------------------------------------- /static/img/mvc-validator/adapter.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bit-ranger/blog/04d656f78632289b7e09da4d4b9ec8d1b349fab3/static/img/mvc-validator/adapter.jpg -------------------------------------------------------------------------------- /static/img/mvc-validator/driven.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bit-ranger/blog/04d656f78632289b7e09da4d4b9ec8d1b349fab3/static/img/mvc-validator/driven.jpg -------------------------------------------------------------------------------- /static/img/mvc-validator/google.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bit-ranger/blog/04d656f78632289b7e09da4d4b9ec8d1b349fab3/static/img/mvc-validator/google.jpg -------------------------------------------------------------------------------- /static/img/mvc-validator/hasErrors.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bit-ranger/blog/04d656f78632289b7e09da4d4b9ec8d1b349fab3/static/img/mvc-validator/hasErrors.jpg -------------------------------------------------------------------------------- /static/img/mvc-validator/initBinder.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bit-ranger/blog/04d656f78632289b7e09da4d4b9ec8d1b349fab3/static/img/mvc-validator/initBinder.jpg -------------------------------------------------------------------------------- /static/img/mvc-validator/notNull.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bit-ranger/blog/04d656f78632289b7e09da4d4b9ec8d1b349fab3/static/img/mvc-validator/notNull.jpg -------------------------------------------------------------------------------- /static/img/mvc-validator/validator-config.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bit-ranger/blog/04d656f78632289b7e09da4d4b9ec8d1b349fab3/static/img/mvc-validator/validator-config.jpg -------------------------------------------------------------------------------- /static/img/namespace.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bit-ranger/blog/04d656f78632289b7e09da4d4b9ec8d1b349fab3/static/img/namespace.png -------------------------------------------------------------------------------- /static/img/rmi.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bit-ranger/blog/04d656f78632289b7e09da4d4b9ec8d1b349fab3/static/img/rmi.jpg -------------------------------------------------------------------------------- /static/img/servlet-encode/body-iso.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bit-ranger/blog/04d656f78632289b7e09da4d4b9ec8d1b349fab3/static/img/servlet-encode/body-iso.png -------------------------------------------------------------------------------- /static/img/servlet-encode/body-utf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bit-ranger/blog/04d656f78632289b7e09da4d4b9ec8d1b349fab3/static/img/servlet-encode/body-utf.png -------------------------------------------------------------------------------- /static/img/servlet-encode/encodeURI.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bit-ranger/blog/04d656f78632289b7e09da4d4b9ec8d1b349fab3/static/img/servlet-encode/encodeURI.png -------------------------------------------------------------------------------- /static/img/servlet-encode/get.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bit-ranger/blog/04d656f78632289b7e09da4d4b9ec8d1b349fab3/static/img/servlet-encode/get.png -------------------------------------------------------------------------------- /static/img/servlet-encode/param.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bit-ranger/blog/04d656f78632289b7e09da4d4b9ec8d1b349fab3/static/img/servlet-encode/param.png -------------------------------------------------------------------------------- /static/img/servlet-encode/post.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bit-ranger/blog/04d656f78632289b7e09da4d4b9ec8d1b349fab3/static/img/servlet-encode/post.png -------------------------------------------------------------------------------- /static/img/servlet-encode/world.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bit-ranger/blog/04d656f78632289b7e09da4d4b9ec8d1b349fab3/static/img/servlet-encode/world.png -------------------------------------------------------------------------------- /static/img/shell-trash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bit-ranger/blog/04d656f78632289b7e09da4d4b9ec8d1b349fab3/static/img/shell-trash.png -------------------------------------------------------------------------------- /static/img/spring-security-filter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bit-ranger/blog/04d656f78632289b7e09da4d4b9ec8d1b349fab3/static/img/spring-security-filter.png -------------------------------------------------------------------------------- /static/img/spring-security-filterChain.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bit-ranger/blog/04d656f78632289b7e09da4d4b9ec8d1b349fab3/static/img/spring-security-filterChain.jpg -------------------------------------------------------------------------------- /static/img/xml-data-type.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bit-ranger/blog/04d656f78632289b7e09da4d4b9ec8d1b349fab3/static/img/xml-data-type.jpg -------------------------------------------------------------------------------- /static/img/xml-field-type.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bit-ranger/blog/04d656f78632289b7e09da4d4b9ec8d1b349fab3/static/img/xml-field-type.jpg -------------------------------------------------------------------------------- /static/js/category.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 页面ready方法 3 | */ 4 | $(document).ready(function() { 5 | categoryDisplay(); 6 | }); 7 | 8 | /** 9 | * 分类展示 10 | * 点击右侧的分类展示时 11 | * 左侧的相关裂变展开或者收起 12 | * @return {[type]} [description] 13 | */ 14 | function categoryDisplay() { 15 | selectCategory(); 16 | $('.categories-item').click(function() { 17 | window.location.hash = "#" + $(this).attr("cate"); 18 | selectCategory(); 19 | }); 20 | } 21 | 22 | function selectCategory(){ 23 | var exclude = ["",undefined]; 24 | var thisId = window.location.hash.substring(1); 25 | var allow = true; 26 | for(var i in exclude){ 27 | if(thisId == exclude[i]){ 28 | allow = false; 29 | break; 30 | } 31 | } 32 | if(allow){ 33 | var cate = thisId; 34 | $("section[post-cate!='" + cate + "']").hide(200); 35 | $("section[post-cate='" + cate + "']").show(200); 36 | } else { 37 | $("section[post-cate='All']").show(); 38 | } 39 | } -------------------------------------------------------------------------------- /static/js/post.js: -------------------------------------------------------------------------------- 1 | --- 2 | layout: null 3 | --- 4 | 5 | /** 6 | * 页面ready方法 7 | */ 8 | $(document).ready(function() { 9 | generateContent(); 10 | // share(); 11 | renderComment(); 12 | }); 13 | 14 | /** 15 | * 侧边目录 16 | */ 17 | function generateContent() { 18 | var $mt = $('.toc'); 19 | var toc = $(".post ul#markdown-toc").clone().get(0); 20 | $mt.each(function(i,o){ 21 | $(o).html(toc); 22 | }); 23 | } 24 | 25 | function share(){ 26 | window._bd_share_config={"common":{"bdSnsKey":{},"bdText":"","bdMini":"2","bdMiniList":false,"bdPic":"","bdStyle":"1","bdSize":"24"},"share":{}}; 27 | with(document)0[getElementsByTagName("script")[0].parentNode.appendChild(createElement('script')).src='//bdimg.share.baidu.com/static/api/js/share.js?v=89860593.js?cdnversion='+~(-new Date()/36e5)]; 28 | } 29 | 30 | 31 | function renderComment() { 32 | var gittalk = new Gitalk({ 33 | id: window.location.pathname, 34 | clientID: '{{site.comment.client_id}}', 35 | clientSecret: '{{site.comment.client_secret}}', 36 | owner: '{{site.github.username}}', 37 | repo: '{{site.comment.repo}}', 38 | admin: ['{{site.github.username}}'], 39 | perPage: 20, 40 | distractionFreeMode: false 41 | }); 42 | gittalk.render('post-comment') 43 | $("#post-comment").removeClass('hidden'); 44 | } 45 | 46 | 47 | -------------------------------------------------------------------------------- /static/js/script.js: -------------------------------------------------------------------------------- 1 | --- 2 | layout: null 3 | --- 4 | 5 | /** 6 | * 页面ready方法 7 | */ 8 | $(document).ready(function() { 9 | 10 | console.log("你不乖哦,彼此之间留点神秘感不好吗?"); 11 | 12 | backToTop(); 13 | search(); 14 | }); 15 | 16 | /** 17 | * 回到顶部 18 | */ 19 | function backToTop() { 20 | $("[data-toggle='tooltip']").tooltip(); 21 | var st = $(".page-scrollTop"); 22 | var $window = $(window); 23 | var topOffset; 24 | //滚页面才显示返回顶部 25 | $window.scroll(function() { 26 | var currnetTopOffset = $window.scrollTop(); 27 | if (currnetTopOffset > 0 && topOffset > currnetTopOffset) { 28 | st.fadeIn(500); 29 | } else { 30 | st.fadeOut(500); 31 | } 32 | topOffset = currnetTopOffset; 33 | }); 34 | 35 | //点击回到顶部 36 | st.click(function() { 37 | $window.scrollTop(0) 38 | }); 39 | 40 | 41 | } 42 | 43 | function search(){ 44 | (function(w,d,t,u,n,s,e){w['SwiftypeObject']=n;w[n]=w[n]||function(){ 45 | (w[n].q=w[n].q||[]).push(arguments);};s=d.createElement(t); 46 | e=d.getElementsByTagName(t)[0];s.async=1;s.src=u;e.parentNode.appendChild(s); 47 | })(window,document,'script','//s.swiftypecdn.com/install/v2/st.js','_st'); 48 | 49 | _st('install','{{site.swiftype.searchId}}','2.0.0'); 50 | } 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /static/js/tagCloud.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @copyright http://blog.rainynight.top/ 3 | * Licensed under MIT 4 | */ 5 | (function($){ 6 | $.fn.tagCloud = function(){ 7 | 8 | var $this = $(this); 9 | 10 | for(var i=0; i<$this.length;i++){ 11 | execute($this.get(i)); 12 | } 13 | 14 | return $this; 15 | 16 | function execute(shell){ 17 | //半径 18 | var radius = 120; 19 | //是否活动 20 | var active = true; 21 | //是否分散 22 | var scatter = true; 23 | //旋转速度 24 | var speed = 2; 25 | //右旋偏移 26 | var rightOffset = 50; 27 | //下旋偏移 28 | var downOffset = 0; 29 | //与眼睛的距离 30 | var distance=300; 31 | 32 | var items = shell.getElementsByTagName('a'); 33 | var itemWraps = []; 34 | for(var i=0; ivItem2.z) 133 | { 134 | return -1; 135 | } 136 | else if(vItem1.zJump to...', 7 | minimumHeaders: 3, 8 | headers: 'h1, h2, h3, h4, h5, h6', 9 | listType: 'ol', // values: [ol|ul] 10 | showEffect: 'show', // values: [show|slideDown|fadeIn|none] 11 | showSpeed: 'slow' // set to 0 to deactivate effect 12 | }, 13 | settings = $.extend(defaults, options); 14 | 15 | function fixedEncodeURIComponent (str) { 16 | return encodeURIComponent(str).replace(/[!'()*]/g, function(c) { 17 | return '%' + c.charCodeAt(0).toString(16); 18 | }); 19 | } 20 | 21 | var headers = $(settings.headers).filter(function() { 22 | // get all headers with an ID 23 | var previousSiblingName = $(this).prev().attr( "name" ); 24 | if (!this.id && previousSiblingName) { 25 | this.id = $(this).attr( "id", previousSiblingName.replace(/\./g, "-") ); 26 | } 27 | return this.id; 28 | }), output = $(this); 29 | if (!headers.length || headers.length < settings.minimumHeaders || !output.length) { 30 | return; 31 | } 32 | 33 | if (0 === settings.showSpeed) { 34 | settings.showEffect = 'none'; 35 | } 36 | 37 | var render = { 38 | show: function() { output.hide().html(html).show(settings.showSpeed); }, 39 | slideDown: function() { output.hide().html(html).slideDown(settings.showSpeed); }, 40 | fadeIn: function() { output.hide().html(html).fadeIn(settings.showSpeed); }, 41 | none: function() { output.html(html); } 42 | }; 43 | 44 | var get_level = function(ele) { return parseInt(ele.nodeName.replace("H", ""), 10); } 45 | var highest_level = headers.map(function(_, ele) { return get_level(ele); }).get().sort()[0]; 46 | var return_to_top = ' '; 47 | 48 | var level = get_level(headers[0]), 49 | this_level, 50 | html = settings.title + " <"+settings.listType+">"; 51 | headers.on('click', function() { 52 | if (!settings.noBackToTopLinks) { 53 | window.location.hash = this.id; 54 | } 55 | }) 56 | .addClass('clickable-header') 57 | .each(function(_, header) { 58 | this_level = get_level(header); 59 | if (!settings.noBackToTopLinks && this_level === highest_level) { 60 | $(header).addClass('top-level-header').after(return_to_top); 61 | } 62 | if (this_level === level) // same level as before; same indenting 63 | html += "
  • " + header.innerHTML + ""; 64 | else if (this_level <= level){ // higher level than before; end parent ol 65 | for(i = this_level; i < level; i++) { 66 | html += "
  • " 67 | } 68 | html += "
  • " + header.innerHTML + ""; 69 | } 70 | else if (this_level > level) { // lower level than before; expand the previous to contain a ol 71 | for(i = this_level; i > level; i--) { 72 | html += "<"+settings.listType+">
  • " 73 | } 74 | html += "" + header.innerHTML + ""; 75 | } 76 | level = this_level; // update for the next one 77 | }); 78 | html += ""; 79 | if (!settings.noBackToTopLinks) { 80 | $(document).on('click', '.back-to-top', function() { 81 | $(window).scrollTop(0); 82 | window.location.hash = ''; 83 | }); 84 | } 85 | 86 | render[settings.showEffect](); 87 | }; 88 | })(jQuery); -------------------------------------------------------------------------------- /static/octicons/LICENSE.txt: -------------------------------------------------------------------------------- 1 | (c) 2012-2015 GitHub 2 | 3 | When using the GitHub logos, be sure to follow the GitHub logo guidelines (https://github.com/logos) 4 | 5 | Font License: SIL OFL 1.1 (http://scripts.sil.org/OFL) 6 | Applies to all font files 7 | 8 | Code License: MIT (http://choosealicense.com/licenses/mit/) 9 | Applies to all other files 10 | -------------------------------------------------------------------------------- /static/octicons/README.md: -------------------------------------------------------------------------------- 1 | If you intend to install Octicons locally, install `octicons-local.ttf`. It should appear as “github-octicons” in your font list. It is specially designed not to conflict with GitHub's web fonts. 2 | -------------------------------------------------------------------------------- /static/octicons/octicons-local.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bit-ranger/blog/04d656f78632289b7e09da4d4b9ec8d1b349fab3/static/octicons/octicons-local.ttf -------------------------------------------------------------------------------- /static/octicons/octicons.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bit-ranger/blog/04d656f78632289b7e09da4d4b9ec8d1b349fab3/static/octicons/octicons.eot -------------------------------------------------------------------------------- /static/octicons/octicons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bit-ranger/blog/04d656f78632289b7e09da4d4b9ec8d1b349fab3/static/octicons/octicons.ttf -------------------------------------------------------------------------------- /static/octicons/octicons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bit-ranger/blog/04d656f78632289b7e09da4d4b9ec8d1b349fab3/static/octicons/octicons.woff --------------------------------------------------------------------------------