UBUNTU14.04 卸载输入法后循环登录一直跳到登录界面

转自http://ijuer.cn/blog/313.html

今天看到 Ubuntu 下期待已久的搜狗拼音输入法更新到了 1.2 版本,就激动的下载安装了,谁知安装后还是各种的不爽,接着就卸载了,一并卸载了 fcitx ,重启完机器后。。。。。。。。问题来了。

在登录界面输入正确的用户名和密码,显示器黑屏一下,然后重新跳转回登录界面。重启无效。一直循环在登录界面。

通过在网上查找。发现这个问题有不少人遇到,但是都没有真正的解决问题,大家说的最多的造成该情况的原因是 主目录下的.Xauthority文件拥有者变成了root,从而以用户登陆的时候无法都取.Xauthority文件。

解决方法是将.Xauthority的拥有者改为登陆用户(或者干脆将.Xauthority删除)

步骤:

开机后在登陆界面按下shift + ctrl + F1进入tty命令行终端登陆后输入:

$ cd ~
$ sudo chown user:user .Xauthority (注:user为要登录的用户名)
ls .Xauthority -l
-rw------- 1 user user 80  1月 27 10:41 .Xauthority
此时拥有者已经变为用户。按下shift + ctrl + F7切换回图形登陆界面登陆即可。

在本人满怀欣喜的按照步骤操作之后,发现,问题果然没有解决。看来不是 Xauthority 的原因!

由于是卸载完搜狗拼音和 fcitx 后出现的问题,感觉一定和 fcitx 有一定的关系,应该是没卸载干净,或者在卸载 fcitx 的时候误删了 ibus 的配置,然后决定按照推测试一下,执行命令:
dpkg -l |grep ^rc|awk '{print $2}' |sudo xargs dpkg -P
该命令为清除所有已删除包的残馀配置文件,发现果然有 fcitx 的残馀配置文件被清除,然后重启系统,输入密码,问题果然解决!!!

最后提醒大家:输入法有风险,安装需谨慎!!!

http协议

Quick reminder about HTTP


When haproxy is running in HTTP mode, both the request and the response are
fully analyzed and indexed, thus it becomes possible to build matching criteria
on almost anything found in the contents.

当haproxy运行在HTTP mode的时候,它会把所有的响应以及请求都会分析和建索引,这样才能够让匹配内容中的关键信息成为可能

However, it is important to understand how HTTP requests and responses are
formed, and how HAProxy decomposes them. It will then become easier to write
correct rules and to debug existing configurations.

然后,我们需要要理解http的请求和响应的格式记忆haproxy是如何解析他们的,这样我们才能更加正确和简便的写出正确的规则以及调试现有的配置文件




## 1.1. The HTTP transaction model


The HTTP protocol is transaction-driven. This means that each request will lead
to one and only one response. Traditionally, a TCP connection is established
from the client to the server, a request is sent by the client on the
connection, the server responds and the connection is closed. A new request
will involve a new connection :

[CON1] [REQ1] … [RESP1] [CLO1] [CON2] [REQ2] … [RESP2] [CLO2] …

In this mode, called the “HTTP close” mode, there are as many connection
establishments as there are HTTP transactions. Since the connection is closed
by the server after the response, the client does not need to know the content
length.

http协议时事物驱动的,这意味这每一个请求都会导致有且仅有一个响应。一般来说,一个TCP链接是客户端和服务器之间建立的双向链接,一个客户端的请求在这个链路上被发送,服务器会返回一个响应,然后关闭这个链接,一个请求就会有一个新的链接,就如下一样

  [CON1] [REQ1] … [RESP1] [CLO1] [CON2] [REQ2] … [RESP2] [CLO2] …

这种模式叫做“HTTP close”模式,当HTTP事物开始的时候会建立很多次的链接。当服务器返回请求后链接被其关闭客户端并不需要知道内容的长度。

Due to the transactional nature of the protocol, it was possible to improve it
to avoid closing a connection between two subsequent transactions. In this mode
however, it is mandatory that the server indicates the content length for each
response so that the client does not wait indefinitely. For this, a special
header is used: “Content-length”. This mode is called the “keep-alive” mode :

[CON] [REQ1] … [RESP1] [REQ2] … [RESP2] [CLO] …

根据这种协议的特性,我们可以在两次连续的事物之间避免关闭tcp链接来提升性能。在这种模式下,必须让服务器强制性的设定每个响应的内容长度,这样才能让客户端无限制的等待。为了达到这个目标,一个特殊的头将会被用到“Content-length”。这个模式叫做“keep-alive”模式

  [CON] [REQ1] … [RESP1] [REQ2] … [RESP2] [CLO] …

Its advantages are a reduced latency between transactions, and less processing
power required on the server side. It is generally better than the close mode,
but not always because the clients often limit their concurrent connections to
a smaller value.

keep-alive模式效的减少了两个事物之间的延迟,且可以让服务器提升性能,一般情况下这种模式比close模式好很多,但也不是总是,因为客户端经常会限制并发连接数在一个很小的值上。

A last improvement in the communications is the pipelining mode. It still uses
keep-alive, but the client does not wait for the first response to send the
second request. This is useful for fetching large number of images composing a
page :

[CON] [REQ1] [REQ2] … [RESP1] [RESP2] [CLO] …

为了解决这个问题,我们可以使用一种叫做pipelining的模式,它使用了keep-alive,客户端在发送第二个请求的时候就不需要等待第一个请求的响应了。这个在其请求页面上有大量的图片的时候很有用。

This can obviously have a tremendous benefit on performance because the network
latency is eliminated between subsequent requests. Many HTTP agents do not
correctly support pipelining since there is no way to associate a response with
the corresponding request in HTTP. For this reason, it is mandatory for the
server to reply in the exact same order as the requests were received.
这明显在性能上有明显的提升,因为连个请求之间的网络延迟被消灭了。许多http代理不能正确的支持pipelining,因为他们没有办法把响应和正确的请求联系起来。因为这个原因,服务器必须按照请求的顺序进行响应。

By default HAProxy operates in keep-alive mode with regards to persistent
connections: for each connection it processes each request and response, and
leaves the connection idle on both sides between the end of a response and the
start of a new request.

haproxy默认使用的时keep-alive的模式:对于每一个链接,它接受一个请求就处理一个请求,链接会在结束响应之后和新开始一个请求之间的时间内处于空闲。

HAProxy supports 5 connection modes :
- keep alive : all requests and responses are processed (default)
- tunnel : only the first request and response are processed,
everything else is forwarded with no analysis.
- passive close : tunnel with “Connection: close” added in both directions.
- server close : the server-facing connection is closed after the response.
- forced close : the connection is actively closed after end of response.

haproxy支持5种链接模式

-keep alive:所已有的请求和响应都会被处理

-tunnel     :只有第一个请求和响应会被处理,其余的直接转发,不会被分析

-passive close:tunnel with “connection:close” added in both directions

-server close:the server-facing connection is closed after the response.

-forced close:在响应结束后链接会被关闭




## 1.2. HTTP request


First, let’s consider this HTTP request :

Line Contents
number
1 GET /serv/login.php?lang=en&profile=2 HTTP/1.1
2 Host: www.mydomain.com
3 User-agent: my small browser
4 Accept: image/jpeg, image/gif
5 Accept: image/png

首先,我们来考虑下HTTP请求:

  Line     Contents
number
1 GET /serv/login.php?lang=en&profile=2 HTTP/1.1
2 Host: www.mydomain.com
3 User-agent: my small browser
4 Accept: image/jpeg, image/gif
5 Accept: image/png




### 1.2.1. The Request line


Line 1 is the “request line”. It is always composed of 3 fields :
第一行时请求行,一般由三个部分组成
- a METHOD : GET
- a URI : /serv/login.php?lang=en&profile=2
- a version tag : HTTP/1.1

All of them are delimited by what the standard calls LWS (linear white spaces),
which are commonly spaces, but can also be tabs or line feeds/carriage returns
followed by spaces/tabs. The method itself cannot contain any colon (‘:’) and
is limited to alphabetic letters. All those various combinations make it
desirable that HAProxy performs the splitting itself rather than leaving it to
the user to write a complex or inaccurate regular expression.
这些都被叫做LWS(Linear white spaces,其实一般就是空格)的标准分割开。

方法:不能有任何的冒号,它被限制在字母表中的字符。

所有的这些不同组合在一起,可以让haproxy分析它,而不是让用户写复杂的正则表达式。

The URI itself can have several forms :

- A “relative URI” :

/serv/login.php?lang=en&profile=2

It is a complete URL without the host part. This is generally what is
received by servers, reverse proxies and transparent proxies.

- An “absolute URI”, also called a “URL” :

http://192.168.0.12:8080/serv/login.php?lang=en&profile=2

It is composed of a “scheme” (the protocol name followed by ‘://‘), a host
name or address, optionally a colon (‘:’) followed by a port number, then
a relative URI beginning at the first slash (‘/‘) after the address part.
This is generally what proxies receive, but a server supporting HTTP/1.1
must accept this form too.
URI:有两种格式

第一种时相对路径的URI,如下

/serv/login.php?lang=en&profile=2

这种是不需要域名部分的,这一般都是服务器/反向代理/透明代理接受到的样子

还有一种时绝对路径叫做URL,如下

http://xxx.com/serv/login.php?lang=en&profile=2

这被一个scheme压缩(协议名字在://前面)

  - a star (‘*’) : this form is only accepted in association with the OPTIONS
method and is not relayable. It is used to inquiry a next hop’s
capabilities.

- an address:port combination : 192.168.0.12:80
This is used with the CONNECT method, which is used to establish TCP
tunnels through HTTP proxies, generally for HTTPS, but sometimes for
other protocols too.

In a relative URI, two sub-parts are identified. The part before the question
mark is called the “path“. It is typically the relative path to static objects
on the server. The part after the question mark is called the “query string”.
It is mostly used with GET requests sent to dynamic scripts and is very
specific to the language, framework or application in use.




### 1.2.2. The request headers


The headers start at the second line. They are composed of a name at the
beginning of the line, immediately followed by a colon (‘:’). Traditionally,
an LWS is added after the colon but that’s not required. Then come the values.
Multiple identical headers may be folded into one single line, delimiting the
values with commas, provided that their order is respected. This is commonly
encountered in the “Cookie:” field. A header may span over multiple lines if
the subsequent lines begin with an LWS. In the example in 1.2, lines 4 and 5
define a total of 3 values for the “Accept:” header.

请求头从第二行开始,都是由一个语法时name: value1,value2

Contrary to a common mis-conception, header names are not case-sensitive, and
their values are not either if they refer to other header names (such as the
“Connection:” header).

http头的名字都不是大小写敏感的,当它的值引用其他的头的名字时,也不大小写敏感

The end of the headers is indicated by the first empty line. People often say
that it’s a double line feed, which is not exact, even if a double line feed
is one valid form of empty line.
头的结束时有一个空的行来表示

Fortunately, HAProxy takes care of all these complex combinations when indexing
headers, checking values and counting them, so there is no reason to worry
about the way they could be written, but it is important not to accuse an
application of being buggy if it does unusual, valid things.
幸运的时,haprxy对这些复杂的组合都会认证的建索引,检查值和对他们进行计数。
Important note:
As suggested by RFC2616, HAProxy normalizes headers by replacing line breaks
in the middle of headers by LWS in order to join multi-line headers. This
is necessary for proper analysis and helps less capable HTTP parsers to work
correctly and not to be fooled by such complex constructs.




## 1.3. HTTP response


An HTTP response looks very much like an HTTP request. Both are called HTTP
messages. Let’s consider this HTTP response :
一个http响应比较像http的请求,都叫做http消息
Line Contents
number
1 HTTP/1.1 200 OK
2 Content-length: 350
3 Content-Type: text/html

As a special case, HTTP supports so called “Informational responses” as status
codes 1xx. These messages are special in that they don’t convey any part of the
response, they’re just used as sort of a signaling message to ask a client to
continue to post its request for instance. In the case of a status 100 response
the requested information will be carried by the next non-100 response message
following the informational one. This implies that multiple responses may be
sent to a single request, and that this only works when keep-alive is enabled
(1xx messages are HTTP/1.1 only). HAProxy handles these messages and is able to
correctly forward and skip them, and only process the next non-100 response. As
such, these messages are neither logged nor transformed, unless explicitly
state otherwise. Status 101 messages indicate that the protocol is changing
over the same connection and that haproxy must switch to tunnel mode, just as
if a CONNECT had occurred. Then the Upgrade header would contain additional
information about the type of protocol the connection is switching to.




### 1.3.1. The Response line


Line 1 is the “response line”. It is always composed of 3 fields :

- a version tag : HTTP/1.1
- a status code : 200
- a reason : OK

The status code is always 3-digit. The first digit indicates a general status :
- 1xx = informational message to be skipped (eg: 100, 101)
- 2xx = OK, content is following (eg: 200, 206)
- 3xx = OK, no content following (eg: 302, 304)
- 4xx = error caused by the client (eg: 401, 403, 404)
- 5xx = error caused by the server (eg: 500, 502, 503)

Please refer to RFC2616 for the detailed meaning of all such codes. The
“reason” field is just a hint, but is not parsed by clients. Anything can be
found there, but it’s a common practice to respect the well-established
messages. It can be composed of one or multiple words, such as “OK”, “Found”,
or “Authentication Required”.

Haproxy may emit the following status codes by itself :

Code When / reason
200 access to stats page, and when replying to monitoring requests
301 when performing a redirection, depending on the configured code
302 when performing a redirection, depending on the configured code
303 when performing a redirection, depending on the configured code
307 when performing a redirection, depending on the configured code
308 when performing a redirection, depending on the configured code
400 for an invalid or too large request
401 when an authentication is required to perform the action (when
accessing the stats page)
403 when a request is forbidden by a “block“ ACL or “reqdeny“ filter
408 when the request timeout strikes before the request is complete
500 when haproxy encounters an unrecoverable internal error, such as a
memory allocation failure, which should never happen
502 when the server returns an empty, invalid or incomplete response, or
when an “rspdeny“ filter blocks the response.
503 when no server was available to handle the request, or in response to
monitoring requests which match the “monitor fail“ condition
504 when the response timeout strikes before the server responds

The error 4xx and 5xx codes above may be customized (see “errorloc“ in section
4.2).




### 1.3.2. The response headers


Response headers work exactly like request headers, and as such, HAProxy uses
the same parsing function for both. Please refer to paragraph 1.2.2 for more
details.

haproxy简单使用http-翻译自digitalocean

原文链接:https://www.digitalocean.com/community/tutorials/how-to-use-haproxy-to-set-up-http-load-balancing-on-an-ubuntu-vps

准备工作

3个节点

1. node1

hostname:haproxy

os:ubuntu

ip:10.0.0.100

2.node2

hostname lamp1

ip:10.0.0.1

3.node3

hostname lamp2

ip:10.0.0.2

安装HAProxy

1.直接使用apt-get安装

apt-get install haproxy
2.安装完成之后,我们打开/etc/default/haproxy把ENABLED设置为1

配置HAProxy

1.进入到/etc/haproxy里新建haproxy.cfg

2.打开haproxy.cfg,我们一部分一部分的向里面加入

2.1 首先加入如下配置

global

log 127.0.0.1 local0 notice

maxconn 2000

user haproxy

group haproxy

log指令的意思是告诉haproxy把系统日志发送到哪一台的syslog服务器,ubuntu里面的sysslog当然是运行着的,但是它没有绑定任何ip地址,我们将会在后面修改rsyslog的配置文件

maxconn的意思当然时说明了同一时间最大的连接数了,这个数字可以随便改

user和group是给正在运行haproxy进程的制定用户和用户组,这个就不要改变他了

2.2 然后加入defaluts块

defaluts

log global

mode http

option httplog

option dontlognull

retries 3

option redispath

timeout connect 5000

timeout client 10000

timeout server 10000

在这个块里,我们指定了默认的值。

timeout connect选项指定了最大的连接时间

timeout client和server设置的值表示如果服务器或者客户端在这段时间没有进行任何tcp处理,那么将会被断开,HAProxy推荐两个使用相同的值

retries指令用来设置重连的次数

option redispatch会开启当链接断掉的时候重分发session,所以如果后面的服务挂掉了,session粘滞不会存在。

2.3 下面配置listen

listen appname 0.0.0.0:80

mode http

stats enable

stats uri /haproxy?stats

stats realm Strictly\ Private

stats auth A_Username:YourPassword

stats auth Another_User:passwd

balance roundrobin

option httpclose

option forwardfor

server lamp1 10.0.0.1:80 check

server lamp2 10.0.0.2:80 check

这里包含了前端和后端的配置,我们配置haproxy为appname监听80端口,appname仅仅是个用来区分引用的名字。

stats指令用来开启连接状态统计页面,这个通过HTTP Basic authentication保证安全,它时stats auth指定的

stats uri就是这个统计页面的url地址

balance指令指定了一个负载均衡算法,可以有的选项有roundrobin/static-rr/leastconn/source/uri/url_param

server指令时来申明一个后端服务器,语法如下

server <name> <address>[:port] [param*]

name主要是写入日志用,方便我们跟踪日志,这里有更多的参数,在这篇文章中我们只使用check和cookie这两个参数

check参数时开启健康检查,检查服务器是否正常

2.4 好了,我们现在配置好了可以使用下面的命令开启它

service haproxy start

测试负载均衡

1.创建php文件打印处服务器ip和客户端ip

 

<?php header(‘Content-Type: text/plain’); `

<pre>`echo "Server IP: ".$_SERVER['SERVER_ADDR']; `</pre>

<pre>`echo "\nClient IP: ".$_SERVER['REMOTE_ADDR'];`</pre>

<pre>` echo "\nX-Forwarded-for: ".$_SERVER['HTTP_X_FORWARDED_FOR']; `</pre>

<pre>`?&gt;`</pre>
<pre>`我们访问haproxy所在的ip地址`</pre>
<pre>`第一次访问`</pre>

Server IP: 172.17.0.5

Client IP: 172.17.0.3

X-Forwarded-for: 172.17.42.1
第二次访问

Server IP: 172.17.0.4

Client IP: 172.17.0.3

X-Forwarded-for: 172.17.42.1
<span style="color: #ff0000;">注:我没有多个机器,所以我使用的docker容器来做实验,这个是我自己跑出来的结果</span>
从上面我们看到haproxy轮番的把我们的请求放到两个后端服务器上,X-Forwarded-for是你自己客户端

2.模拟服务器挂掉的情况

我们现在模拟服务器挂掉,首先把一个服务关掉,我关掉172.17.0.4的服务
> service apache2 stop
继续访问

连续访问两次都是如下结果
> Server IP: 172.17.0.5
Client IP: 172.17.0.3

X-Forwarded-for: 172.17.42.1
我们达到了服务负载和容错的功能
## Session Stickness

如果拟的web应用需要用户登陆(就是你的服务使用到了session),这种负载均衡可能就会遇到问题,因为session是和具体的服务器相关的,haproxy会把用户随机搞到不同的服务器上,服务器就带状态了,就不好搞成分布式了,如果是这种需求,那么我们必须保证用户会被分配到他第一次访问的服务器上去,不然就会乱套,haproxy可以通过设置cookie来标记所使用的服务器来达到这种效果。

下面我们增加一个session.php文件来阐述下这个是如何工作的
> <pre>`&lt;?php header('Content-Type: text/plain'); `</pre>
<pre>`session_start(); `</pre>

<pre>`if(!isset($_SESSION['visit'])) { `</pre>

<pre>`echo "This is the first time you're visiting this server"; `</pre>

<pre>`$_SESSION['visit'] = 0; `</pre>

<pre>`} `</pre>

<pre>`else `</pre>

<pre>`echo "Your number of visits: ".$_SESSION['visit']; `</pre>

<pre>`$_SESSION['visit']++; echo "\nServer IP: ".$_SERVER['SERVER_ADDR']; `</pre>

<pre>`echo "\nClient IP: ".$_SERVER['REMOTE_ADDR']; echo "\nX-Forwarded-for: ".$_SERVER['HTTP_X_FORWARDED_FOR']."\n"; print_r($_COOKIE);`</pre>

<pre>` ?&gt;`</pre>
<pre>`上面的代码就在apache的服务器上对用户增加了session,会记录你使用这个session多少次`</pre>
<pre>`下面,我们就要使用haproxy在cookie中增加我们的东西了`</pre>
<pre>`我们默认所有到haproxy的请求在http头里都包含set-cookie的字段,我们在这里面存服务器信息`</pre>
<pre>第一步就是在配置文件中,在listen块的下面增加cookie指令</pre>
`cookie SRVNAME insert `
<pre>`server lamp1 10.0.0.1:80 cookie S1 check `</pre>

<pre>`server lamp2 10.0.0.2:80 cookie S2 check`</pre>
<pre>`我们重启haproxy`</pre>
<pre>`下面使用curl访问`</pre>
`curl -i http://172.17.0.3/session.php
结果如下

HTTP/1.1 200 OK

Date: Fri, 16 Jan 2015 14:12:00 GMT

Server: Apache/2.4.7 (Ubuntu)

X-Powered-By: PHP/5.5.9-1ubuntu4.5

Set-Cookie: PHPSESSID=q29dmd1eusk0gs1ja9qcq0paj5; path=/

Expires: Thu, 19 Nov 1981 08:52:00 GMT

Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0

Pragma: no-cache

Vary: Accept-Encoding

Content-Length: 134

Connection: close

Content-Type: text/plain

Set-Cookie: SRVNAME=S1; path=/


This is the first time you're visiting this server

Server IP: 172.17.0.4

Client IP: 172.17.0.3

X-Forwarded-for: 172.17.42.1

Array

(

)
这个时第一次访问,为了看到我们刚刚的SRVNAME=S1,我们把上面的Set-cookies的头作为参数访问加入SRVNAME=S1(当然你要是浏览器,可以F12召唤神兽直接看)
curl -i http://172.17.0.3/session.php --cookie "PHPSESSID=0juvu9ir7bvm4agiivfc5pvg11; SRVNAME=S1"
结果如下

HTTP/1.1 200 OK

Date: Fri, 16 Jan 2015 14:12:15 GMT

Server: Apache/2.4.7 (Ubuntu)

X-Powered-By: PHP/5.5.9-1ubuntu4.5

Expires: Thu, 19 Nov 1981 08:52:00 GMT

Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0

Pragma: no-cache

Vary: Accept-Encoding

Content-Length: 174

Connection: close

Content-Type: text/plain


Your number of visits: 1

Server IP: 172.17.0.4

Client IP: 172.17.0.3

X-Forwarded-for: 172.17.42.1

Array

(

 [PHPSESSID] => q29dmd1eusk0gs1ja9qcq0paj5

 [SRVNAME] => S1

)


 
在执行一下

HTTP/1.1 200 OK

Date: Fri, 16 Jan 2015 14:12:18 GMT

Server: Apache/2.4.7 (Ubuntu)

X-Powered-By: PHP/5.5.9-1ubuntu4.5

Expires: Thu, 19 Nov 1981 08:52:00 GMT

Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0

Pragma: no-cache

Vary: Accept-Encoding

Content-Length: 174

Connection: close

Content-Type: text/plain


Your number of visits: 2

Server IP: 172.17.0.4

Client IP: 172.17.0.3

X-Forwarded-for: 172.17.42.1

Array

(

 [PHPSESSID] => q29dmd1eusk0gs1ja9qcq0paj5

 [SRVNAME] => S1

)


 
可以看到我们解决了这个Session strickness

ld链接脚本(转)

转自:http://www.cnblogs.com/Neddy/archive/2011/12/18/2291576.html
================================== 我 们对每个c或者汇编文件进行单独编译,但是不去连接,生成很多.o 的文件,这些.o文件首先是分散的,我们首先要考虑的如何组合起来;其次,这些.o文件存在相互调用的关系;再者,我们最后生成的bin文件是要在硬件中 运行的,每一部分放在什么地址都要有仔细的说明。我觉得在写makefile的时候,最为重要的就是ld的理解,下面说说我的经验: 首先,要确定我们的程序用没有用到标准的c库,或者一些系统的库文件,这些一般是在操作系统之上开发要注意的问题,这里并不多说,熟悉在Linux编程的人,基本上都会用ld命令;这里,我们从头开始,直接进行汇编语言的连接。 我们写一个汇编程序,控制GPIO,从而控制外接的LED,代码如下; .text .global _start _start: LDR R0,=0x56000010 @GPBCON寄存器
MOV R1,# 0x00000400
STR R1,[R0]
LDR R0,=0x56000014
MOV R1,#0x00000000
STR R1,[R0]
MAIN_LOOP:
B MAIN_LOOP
代码很简单,就是一个对io口 进行设置然后写数据。我们看它是如何编译的,注意我们这里使用的不是arm-linux-gcc而是arm-elf-gcc,二者之间没有什么比较大的区 别,arm-linux-gcc可能包含更多的库文件,在命令行的编译上面是没有区别。我们来看是如何编译的: arm-elf-gcc -g -c -o led_On.o led_On.s 首先纯编译不连接 arm-elf-ld -Ttext 0x00000000 -g led_On.o -o led_on_elf 用Ttext指明我们程序存储的地方,这里生成的是elf文件,还不是我们真正的bin,但是可以借助一些工具可以进行调试。然后: arm-elf-objcopy -O binary -S led_on_elf led_on.bin 生成bin文件。 -T选项是ld命令中比较重要的一个选项,可以用它直接指明代码的代码段、数据段、bss段,对于复杂的连接,可以专门写一个脚本来告诉编译器如何连接。 -Ttext   addr -Tdata addr -Tbss     addr arm-elf-ld -Ttext 0x00000000 -g led_On.o -o led_on_elf ,运行地址为0x00000000,由于没有指明数据段和bss,他们会默认的依次放在后面。相同的代码 不同的Ttext,你可以对比一下他们之间会变的差异,ld会自动调整跳转的地址。 第二个概 念:section,section可以理解成一块,例如像c里面的一个子函数,就是一个section,链接器ld把object文件中的每个 section都作为一个整体,为其分配运行的地址(memory layout),这个过程就是重定位(relocation);最后把所有目标文件合并为一个目标文件。 链接通过一个linker script来控制,这个脚本描述了输入文件的sections到输出文件的映射,以及输出文件的memory layout。 因此,linker总会使用一个linker script,如果不特别指定,则使用默认的script;可以使用‘-T’命令行选项来指定一个linker script。 *映像文件的输入段与输出段 linker把多个输入文件合并为一个输出文件。输出文件和输入文件都是目标文件(object file),输出文件通常被称为可执行文件(executable)。 每个目标文件都有一系列section,输入文件的section称为input section,输出文件的section则称为output section。 一个section可以是 loadable的,即输出文件运行时需要将这样的section加载到memory(类似于RO&RW段);也可以是 allocatable的,这样的section没有任何内容,某些时候用0对相应的memory区域进行初始化(类似于ZI段);如果一个 section既非loadable也非allocatable,则它通常包含的是调试信息。 每个loadable或 allocatable的output section都有两个地址,一是VMA(virtual memory address),是该section的运行时域地址;二是LMA(load memory address),是该section的加载时域地址。 可以通过objdump工具附加’-h’选项来查看目标文件中的sections。 *简单的Linker script (1) SECTIONS命令: The SECTIONS command tells the linker how to map input sections into output sections, and how to place the output sections in memory. 命令格式如下: SECTIONS { sections-command sections-command …… } 其中sections-command可以是ENTRY命令,符号赋值,输出段描述,也可以是overlay描述。 (2) 地址计数器‘.’(location counter): 该符号只能用于SECTIONS命令内部,初始值为‘0’,可以对该符号进行赋值,也可以使用该符号进行计算或赋值给其他符号。它会自动根据SECTIONS命令内部所描述的输出段的大小来计算当前的地址。 (3) 输出段描述(output section description): 前面提到在SECTIONS命令中可以作输出段描述,描述的格式如下: section [address] [(type)] : [AT(lma)] { output-section-command output-section-command } [>region] [AT>lma_region] [:phdr :phdr …] [=fillexp] 很多附加选项是用不到的。其中的output-section-command又可以是符号赋值,输入段描述,要直接包含的数据值,或者某一特定的输出段关键字。 *linker script 实例 ============================== OUTPUT_ARCH(arm) ENTRY(_start) SECTIONS { . = 0xa3f00000; __boot_start = .; .start ALIGN(4) : { *(.text.start) } .setup ALIGN(4) : { setup_block = .; *(.setup) setup_block_end = .; } .text ALIGN(4) : { *(.text) } .rodata ALIGN(4) : { *(.rodata) } .data ALIGN(4) : { *(.data) } .got ALIGN(4) : { *(.got) } __boot_end = .; .bss ALIGN(16) : { bss_start = .; *(.bss) *(COMMON) bss_end = .; } .comment ALIGN(16) : { *(.comment) } stack_point = __boot_start + 0x00100000; loader_size = boot_end - boot_start; setup_size = setup_block_end - setup_block; } ============================= 在SECTIONS命令中的类似于下面的描述结构就是输出段描述: .start ALIGN(4) : { *(.text.start) } .start 为output section name,ALIGN(4)返回一个基于location counter(.)的4字节对齐的地址值。(.text.start)是输入段描述,为通配符,意思是把所有被链接的object文件中 的.text.start段都链接进这个名为.start的输出段。 源文件中所标识的section及其属性实际上就是对输入段的描述,例如.text.start输入段在源文件start.S中的代码如下: .section .text.start .global _start _start : b start arm-elf-ld -Ttimer.lds -o timer_elf header .o 这里就必须存在一个timer.lds的文件。 对于.lds文件,它定义了整个程序编译之后的连接过程,决定了一个可执行程序的各个段的存储位置。虽然现在我还没怎么用它,但感觉还是挺重要的,有必要了解一下。 先看一下GNU官方网站上对.lds文件形式的完整描述: SECTIONS {

secname start BLOCK(align) (NOLOAD) : AT ( ldadr )
{ contents } >region :phdr =fill

}
secname和contents是必须的,其他的都是可选的。下面挑几个常用的看看: 1、secname:段名 2、contents:决定哪些内容放在本段,可以是整个目标文件,也可以是目标文件中的某段(代码段、数据段等) 3、start:本段连接(运行)的地址,如果没有使用AT(ldadr),本段存储的地址也是start。GNU网站上说start可以用任意一种描述地址的符号来描述。 4、AT(ldadr):定义本段存储(加载)的地址。 / nand.lds /
SECTIONS {
firtst 0x00000000 : { head.o init.o }
second 0x30000000 : AT(4096) { main.o }
}
以上,head.o放在0x00000000地址开始处,init.o放在head.o后面,他们的运行地址也是0x00000000,即连接和存储地址 相同(没有AT指定);main.o放在4096(0x1000,是AT指定的,存储地址)开始处,但是它的运行地址在0x30000000,运行之前需 要从0x1000(加载处)复制到0x30000000(运行处),此过程也就用到了读取Nand flash。 这就是存储地址和连接(运行)地址的不同,称为加载时域和运行时域,可以在.lds连接脚本文件中分别指定。 编写好的.lds文件,在用arm-linux-ld连接命令时带-Tfilename来调用执行,如
arm-linux-ld –Tnand.lds x.o y.o –o xy.o。也用-Ttext参数直接指定连接地址,如
arm-linux-ld –Ttext 0x30000000 x.o y.o –o xy.o。
既然程序有了两种地址,就涉及到一些跳转指令的区别,这里正好写下来,以后万一忘记了也可查看,以前不少东西没记下来现在忘得差不多了。 ARM汇编中,常有两种跳转方法:b跳转指令、ldr指令向PC赋值。 我自己经过归纳如下: b step1 :b跳转指令是相对跳转,依赖当前PC的值,偏移量是通过该指令本身的bit[23:0]算出来的,这使得使用b指令的程序不依赖于要跳到的代码的位置,只看指令本身。 ldr pc, =step1 :该指令是从内存中的某个位置(step1)读出数据并赋给PC,同样依赖当前PC的值,但是偏移量是那个位置(step1)的连接地址(运行时的地址),所以可以用它实现从Flash到RAM的程序跳转。 此外,有必要回味一下adr伪指令,U-boot中那段relocate代码就是通过adr实现当前程序是在RAM中还是flash中。仍然用我当时的注释 adr r0, _start / r0是代码的当前位置 /
/ adr伪指令,汇编器自动通过当前PC的值算出 如果执行到_start时PC的值,放到r0中:
当 此段在flash中执行时r0 = _start = 0;当此段在RAM中执行时_start = _TEXT_BASE(在board/smdk2410/config.mk中指定的值为0x33F80000,即u-boot在把代码拷贝到RAM中去 执行的代码段的开始)
/
ldr r1, _TEXT_BASE / 测试判断是从Flash启动,还是RAM /
/ 此句执行的结果r1始终是0x33FF80000,因为此值是又编译器指定的(ads中设置,或-D设置编译器参数) /
cmp r0, r1 / 比较r0和r1,调试的时候不要执行重定位 /
下面,结合u-boot.lds看看一个正式的连接脚本文件。这个文件的基本功能还能看明白,虽然上面分析了好多,但其中那些GNU风格的符号还是着实让我感到迷惑。 OUTPUT_FORMAT(“elf32&shy;littlearm”, “elf32&shy;littlearm”, “elf32&shy;littlearm”)
;指定输出可执行文件是elf格式,32位ARM指令,小端
OUTPUT_ARCH(arm)
;指定输出可执行文件的平台为ARM
ENTRY(_start)
;指定输出可执行文件的起始代码段为_start.
SECTIONS
{
. = 0x00000000 ; 从0x0位置开始
. = ALIGN(4) ; 代码以4字节对齐
.text : ;指定代码段
{
cpu/arm920t/start.o (.text) ; 代码的第一个代码部分
(.text) ;其它代码部分
}
. = ALIGN(4)
.rodata : {
(.rodata) } ;指定只读数据段
. = ALIGN(4);
.data : { (.data) } ;指定读/写数据段
. = ALIGN(4);
.got : {
(.got) } ;指定got段, got段式是uboot自定义的一个段, 非标准段
u_boot_cmd_start = . ;把u_boot_cmd_start赋值为当前位置, 即起始位置
.u_boot_cmd : { (.u_boot_cmd) } ;指定u_boot_cmd段, uboot把所有的uboot命令放在该段.
u_boot_cmd_end = .;把u_boot_cmd_end赋值为当前位置,即结束位置
. = ALIGN(4);
bss_start = .; 把bss_start赋值为当前位置,即bss段的开始位置
.bss : {
(.bss) }; 指定bss段
_end = .; 把_end赋值为当前位置,即bss段的结束位置
}

理解 Android Build 系统(转)




Android Build 系统是用来编译 Android 系统,Android SDK 以及相关文档的一套框架。众所周知,Android 是一个开源的操作系统。Android 的源码中包含了许许多多的模块。 不同产商的不同设备对于 Android 系统的定制都是不一样的。如何将这些模块统一管理起来,如何能够在不同的操作系统上进行编译,如何在编译时能够支持面向不同的硬件设备,不同的编译类型, 且还要提供面向各个产商的定制扩展,是非常有难度的。 但 Android Build 系统很好的解决了这些问题,这里面有很多值得我们开发人员学习的地方。对于 Android 平台开发人员来说,本文可以帮助你熟悉你每天接触到的构建环境。对于其他开发人员来说,本文可以作为一个 GNU Make 的使用案例,学习这些成功案例,可以提升我们的开发经验。

作者:




强 波, Java 软件工程师, 富士通南大软件技术有限公司

2013 年 3 月 28 日

原链接:http://www.ibm.com/developerworks/cn/opensource/os-cn-android-build/






## 前言

Android Build 系统是 Android 源码的一部分。关于如何获取 Android 源码,请参照 Android Source 官方网站:

http://source.android.com/source/downloading.html

Android Build 系统用来编译 Android 系统,Android SDK 以及相关文档。该系统主要由 Make 文件,Shell 脚本以及 Python 脚本组成,其中最主要的是 Make 文件。

众所周知,Android 是一个开源的操作系统。Android 的源码中包含了大量的开源项目以及许多的模块。不同产商的不同设备对于 Android 系统的定制都是不一样的。

如何将这些项目和模块的编译统一管理起来,如何能够在不同的操作系统上进行编译,如何在编译时能够支持面向不同的硬件设备,不同的编译类型,且还要提供面向各个产商的定制扩展,是非常有难度的。

但 Android Build 系统很好的解决了这些问题,这里面有很多值得我们开发人员学习的地方。

对于 Android 平台开发人员来说,本文可以帮助你熟悉你每天接触到的构建环境。

对于其他开发人员来说,本文可以作为一个 GNU Make 的使用案例,学习这些成功案例,可以提升我们的开发经验。

回页首

## 概述

Build 系统中最主要的处理逻辑都在 Make 文件中,而其他的脚本文件只是起到一些辅助作用,由于篇幅所限,本文只探讨 Make 文件中的内容。

整个 Build 系统中的 Make 文件可以分为三类:

第一类是 Build 系统核心文件,此类文件定义了整个 Build 系统的框架,而其他所有 Make 文件都是在这个框架的基础上编写出来的。

图 1 是 Android 源码树的目录结构,Build 系统核心文件全部位于 /build/core(本文所提到的所有路径都是以 Android 源码树作为背景的,“/”指的是源码树的根目录,与文件系统无关)目录下。

##### 图 1. Android 源码树的目录结构

图 1\. Android 源码树的目录结构第 二类是针对某个产品(一个产品可能是某个型号的手机或者平板电脑)的 Make 文件,这些文件通常位于 device 目录下,该目录下又以公司名以及产品名分为两级目录,图 2 是 device 目录下子目录的结构。对于一个产品的定义通常需要一组文件,这些文件共同构成了对于这个产品的定义。例如,/device/sony/it26 目录下的文件共同构成了对于 Sony LT26 型号手机的定义。

##### 图 2. device 目录下子目录的结构

图 2\. device 目录下子目录的结构第 三类是针对某个模块(关于模块后文会详细讨论)的 Make 文件。整个系统中,包含了大量的模块,每个模块都有一个专门的 Make 文件,这类文件的名称统一为“Android.mk”,该文件中定义了如何编译当前模块。Build 系统会在整个源码树中扫描名称为“Android.mk”的文件并根据其中的内容执行模块的编译。

回页首

## 编译 Android 系统

### 执行编译

Android 系统的编译环境目前只支持 Ubuntu 以及 Mac OS 两种操作系统。关于编译环境的构建方法请参见以下路径:http://source.android.com/source/initializing.html

在完成编译环境的准备工作以及获取到完整的 Android 源码之后,想要编译出整个 Android 系统非常的容易:

打开控制台之后转到 Android 源码的根目录,然后执行如清单 1 所示的三条命令即可("$"是命令提示符,不是命令的一部分。):

完整的编译时间依赖于编译主机的配置,在笔者的 Macbook Pro(OS X 10.8.2, i7 2G CPU,8G RAM, 120G SSD)上使用 8 个 Job 同时编译共需要一个半小时左右的时间。

##### 清单 1. 编译 Android 系统


 $ source build/envsetup.sh
$ lunch full-eng
$ make -j8


这三行命令的说明如下:

第一行命令“source build/envsetup.sh”引入了 build/envsetup.sh脚本。该脚本的作用是初始化编译环境,并引入一些辅助的 Shell 函数,这其中就包括第二步使用 lunch 函数。

除此之外,该文件中还定义了其他一些常用的函数,它们如表 1 所示:

##### 表 1. build/envsetup.sh 中定义的常用函数
















































名称说明
croot切换到源码树的根目录
m在源码树的根目录执行 make
mmBuild 当前目录下的模块
mmmBuild 指定目录下的模块
cgrep在所有 C/C++ 文件上执行 grep
jgrep在所有 Java 文件上执行 grep
resgrep在所有 res/.xml 文件上执行 grep
godir转到包含某个文件的目录路径
printconfig显示当前 Build 的配置信息
add_lunch_combo在 lunch 函数的菜单中添加一个条目

第二行命令“lunch full-eng”是调用 lunch 函数,并指定参数为“full-eng”。lunch 函数的参数用来指定此次编译的目标设备以及编译类型。在这里,这两个值分别是“full”和“eng”。“full”是 Android 源码中已经定义好的一种产品,是为模拟器而设置的。而编译类型会影响最终系统中包含的模块,关于编译类型将在表 7 中详细讲解。

如果调用 lunch 函数的时候没有指定参数,那么该函数将输出列表以供选择,该列表类似图 3 中的内容(列表的内容会根据当前 Build 系统中包含的产品配置而不同,具体参见后文“添加新的产品”),此时可以通过输入编号或者名称进行选择。

##### 图 3. lunch 函数的输出

图 3\. lunch 函数的输出第三行命令“make -j8”才真正开始执行编译。make 的参数“-j”指定了同时编译的 Job 数量,这是个整数,该值通常是编译主机 CPU 支持的并发线程总数的 1 倍或 2 倍(例如:在一个 4 核,每个核支持两个线程的 CPU 上,可以使用 make -j8 或 make -j16)。在调用 make 命令时,如果没有指定任何目标,则将使用默认的名称为“droid”目标,该目标会编译出完整的 Android 系统镜像。

### Build 结果的目录结构

所有的编译产物都将位于 /out 目录下,该目录下主要有以下几个子目录: /out/host/:该目录下包含了针对主机的 Android 开发工具的产物。即 SDK 中的各种工具,例如:emulator,adb,aapt 等。
/out/target/common/:该目录下包含了针对设备的共通的编译产物,主要是 Java 应用代码和 Java 库。 /out/target/product/<product_name>/:包含了针对特定设备的编译结果以及平台相关的 C/C++ 库和二进制文件。其中,<product_name>是具体目标设备的名称。
/out/dist/:包含了为多种分发而准备的包,通过“make disttarget”将文件拷贝到该目录,默认的编译目标不会产生该目录。

### Build 生成的镜像文件

Build 的产物中最重要的是三个镜像文件,它们都位于 /out/target/product/<product_name>/ 目录下。

这三个文件是:
system.img:包含了 Android OS 的系统文件,库,可执行文件以及预置的应用程序,将被挂载为根分区。
ramdisk.img:在启动时将被 Linux 内核挂载为只读分区,它包含了 /init 文件和一些配置文件。它用来挂载其他系统镜像并启动 init 进程。 userdata.img:将被挂载为 /data,包含了应用程序相关的数据以及和用户相关的数据。
回页首

## Make 文件说明

整个 Build 系统的入口文件是源码树根目录下名称为“Makefile”的文件,当在源代码根目录上调用 make 命令时,make 命令首先将读取该文件。

Makefile 文件的内容只有一行:“include build/core/main.mk”。该行代码的作用很明显:包含 build/core/main.mk 文件。在 main.mk 文件中又会包含其他的文件,其他文件中又会包含更多的文件,这样就引入了整个 Build 系统。

这些 Make 文件间的包含关系是相当复杂的,图 3 描述了这种关系,该图中黄色标记的文件(且除了 $开头的文件)都位于 build/core/ 目录下。

##### 图 4. 主要的 Make 文件及其包含关系

图 4\. 主要的 Make 文件及其包含关系表 2 总结了图 4 中提到的这些文件的作用:

##### 表 2. 主要的 Make 文件的说明








































































文件名说明
main.mk最主要的 Make 文件,该文件中首先将对编译环境进行检查,同时引入其他的 Make 文件。另外,该文件中还定义了几个最主要的 Make 目标,例如 droid,sdk,等(参见后文“Make 目标说明”)。
help.mk包含了名称为 help 的 Make 目标的定义,该目标将列出主要的 Make 目标及其说明。
pathmap.mk将许多头文件的路径通过名值对的方式定义为映射表,并提供 include-path-for 函数来获取。例如,通过 $(call include-path-for, frameworks-native)便可以获取到 framework 本地代码需要的头文件路径。
envsetup.mk配置 Build 系统需要的环境变量,例如:TARGETPRODUCT,TARGET_BUILD_VARIANT,HOST_OS,HOST_ARCH 等。
当前编译的主机平台信息(例如操作系统,CPU 类型等信息)就是在这个文件中确定的。
另外,该文件中还指定了各种编译结果的输出路径。
combo/select.mk根据当前编译器的平台选择平台相关的 Make 文件。
dumpvar.mk在 Build 开始之前,显示此次 Build 的配置信息。
config.mk整个 Build 系统的配置文件,最重要的 Make 文件之一。该文件中主要包含以下内容:

定义了许多的常量来负责不同类型模块的编译。 定义编译器参数以及常见文件后缀,例如 .zip,.jar.apk。
根据 BoardConfig.mk 文件,配置产品相关的参数。 设置一些常用工具的路径,例如 flex,e2fsck,dx。
definitions.mk最重要的 Make 文件之一,在其中定义了大量的函数。这些函数都是 Build 系统的其他文件将用到的。例如:my-dir,all-subdir-makefiles,find-subdir-files,sign- package 等,关于这些函数的说明请参见每个函数的代码注释。
distdir.mk针对 dist 目标的定义。dist 目标用来拷贝文件到指定路径。
dex_preopt.mk针对启动 jar 包的预先优化。
pdk_config.mk顾名思义,针对 pdk(Platform Developement Kit)的配置文件。
${ONE_SHOT_MAKEFILE}ONE_SHOT_MAKEFILE 是一个变量,当使用“mm”编译某个目录下的模块时,此变量的值即为当前指定路径下的 Make 文件的路径。
${subdir_makefiles}各个模块的 Android.mk 文件的集合,这个集合是通过 Python 脚本扫描得到的。
post_clean.mk在前一次 Build 的基础上检查当前 Build 的配置,并执行必要清理工作。
legacy_prebuilts.mk该文件中只定义了 GRANDFATHERED_ALL_PREBUILT 变量。
Makefile被 main.mk 包含,该文件中的内容是辅助 main.mk 的一些额外内容。

Android 源码中包含了许多的模块,模块的类型有很多种,例如:Java 库,C/C++ 库,APK 应用,以及可执行文件等 。并且,Java 或者 C/C++ 库还可以分为静态的或者动态的,库或可执行文件既可能是针对设备(本文的“设备”指的是 Android 系统将被安装的设备,例如某个型号的手机或平板)的也可能是针对主机(本文的“主机”指的是开发 Android 系统的机器,例如装有 Ubuntu 操作系统的 PC 机或装有 MacOS 的 iMac 或 Macbook)的。不同类型的模块的编译步骤和方法是不一样,为了能够一致且方便的执行各种类型模块的编译,在 config.mk 中定义了许多的常量,这其中的每个常量描述了一种类型模块的编译方式,这些常量有:

BUILD_HOST_STATIC_LIBRARY BUILD_HOST_SHARED_LIBRARY
BUILD_STATIC_LIBRARY BUILD_SHARED_LIBRARY
BUILD_EXECUTABLE BUILD_HOST_EXECUTABLE
BUILD_PACKAGE BUILD_PREBUILT
BUILD_MULTI_PREBUILT BUILD_HOST_PREBUILT
BUILD_JAVA_LIBRARY BUILD_STATIC_JAVA_LIBRARY
* BUILD_HOST_JAVA_LIBRARY
通过名称大概就可以猜出每个变量所对应的模块类型。(在模块的 Android.mk 文件中,只要包含进这里对应的常量便可以执行相应类型模块的编译。对于 Android.mk 文件的编写请参见后文:“添加新的模块”。)

这 些常量的值都是另外一个 Make 文件的路径,详细的编译方式都是在对应的 Make 文件中定义的。这些常量和 Make 文件的是一一对应的,对应规则也很简单:常量的名称是 Make 文件的文件名除去后缀全部改为大写然后加上“BUILD”作为前缀。例如常量 BUILDHOST_PREBUILT 的值对应的文件就是 host_prebuilt.mk。

这些 Make 文件的说明如表 3 所示:

##### 表 3. 各种模块的编译方式的定义文件




























































文件名说明
host_static_library.mk定义了如何编译主机上的静态库。
host_shared_library.mk定义了如何编译主机上的共享库。
static_library.mk定义了如何编译设备上的静态库。
shared_library.mk定义了如何编译设备上的共享库。
executable.mk定义了如何编译设备上的可执行文件。
host_executable.mk定义了如何编译主机上的可执行文件。
package.mk定义了如何编译 APK 文件。
prebuilt.mk定义了如何处理一个已经编译好的文件 ( 例如 Jar 包 )。
multi_prebuilt.mk定义了如何处理一个或多个已编译文件,该文件的实现依赖 prebuilt.mk。
host_prebuilt.mk处理一个或多个主机上使用的已编译文件,该文件的实现依赖 multi_prebuilt.mk。
java_library.mk定义了如何编译设备上的共享 Java 库。
static_java_library.mk定义了如何编译设备上的静态 Java 库。
host_java_library.mk定义了如何编译主机上的共享 Java 库。

不同类型的模块的编译过程会有一些相同的步骤,例如:编译一个 Java 库和编译一个 APK 文件都需要定义如何编译 Java 文件。因此,表 3 中的这些 Make 文件的定义中会包含一些共同的代码逻辑。为了减少代码冗余,需要将共同的代码复用起来,复用的方式是将共同代码放到专门的文件中,然后在其他文件中包含这 些文件的方式来实现的。这些包含关系如图 5 所示。由于篇幅关系,这里就不再对其他文件做详细描述(其实这些文件从文件名称中就可以大致猜出其作用)。

##### 图 5. 模块的编译方式定义文件的包含关系

图 5\. 模块的编译方式定义文件的包含关系回页首

## Make 目标说明

### make /make droid

如果在源码树的根目录直接调用“make”命令而不指定任何目标,则会选择默认目标:“droid”(在 main.mk 中定义)。因此,这和执行“make droid”效果是一样的。

droid 目标将编译出整个系统的镜像。从源代码到编译出系统镜像,整个编译过程非常复杂。这个过程并不是在 droid 一个目标中定义的,而是 droid 目标会依赖许多其他的目标,这些目标的互相配合导致了整个系统的编译。

图 6 描述了 droid 目标所依赖的其他目标:

##### 图 6. droid 目标所依赖的其他 Make 目标

图 6\. droid 目标所依赖的其他 Make 目标图 6 中这些目标的说明如表 4 所示:

##### 表 4. droid 所依赖的其他 Make 目标的说明





































名称说明
apps_only该目标将编译出当前配置下不包含 user,userdebug,eng 标签(关于标签,请参见后文“添加新的模块”)的应用程序。
droidcore该目标仅仅是所依赖的几个目标的组合,其本身不做更多的处理。
dist_files该目标用来拷贝文件到 /out/dist 目录。
files该目标仅仅是所依赖的几个目标的组合,其本身不做更多的处理。
prebuilt该目标依赖于 $(ALL_PREBUILT)$(ALL_PREBUILT)的作用就是处理所有已编译好的文件。
$(modules_to_install)modules_to_install 变量包含了当前配置下所有会被安装的模块(一个模块是否会被安装依赖于该产品的配置文件,模块的标签等信息),因此该目标将导致所有会被安装的模块的编译。
$(modules_to_check)该目标用来确保我们定义的构建模块是没有冗余的。
$(INSTALLED_ANDROID_INFO_TXT_TARGET)该目标会生成一个关于当前 Build 配置的设备信息的文件,该文件的生成路径是:out/target/product/<_product_name
>/android-info.txt


systemimage
生成 system.img。


$(INSTALLED_BOOTIMAGE_TARGET)
生成 boot.img。


$(INSTALLED_RECOVERYIMAGE_TARGET)
生成 recovery.img。


$(INSTALLED_USERDATAIMAGE_TARGET)
生成 userdata.img。


$(INSTALLED_CACHEIMAGE_TARGET)
生成 cache.img。


$(INSTALLED_FILES_FILE)
该目标会生成 out/target/product/<product_name>/ installed-files.txt 文件,该文件中内容是当前系统镜像中已经安装的文件列表。




### 其他目标

Build 系统中包含的其他一些 Make 目标说明如表 5 所示:

##### 表 5. 其他主要 Make 目标




















































































Make 目标说明
make clean执行清理,等同于:rm -rf out/。
make sdk编译出 Android 的 SDK。
make clean-sdk清理 SDK 的编译产物。
make update-api更新 API。在 framework API 改动之后,需要首先执行该命令来更新 API,公开的 API 记录在 frameworks/base/api 目录下。
make dist执行 Build,并将 MAKECMDGOALS 变量定义的输出文件拷贝到 /out/dist 目录。
make all编译所有内容,不管当前产品的定义中是否会包含。
make help帮助信息,显示主要的 make 目标。
make snod从已经编译出的包快速重建系统镜像。
make libandroid_runtime编译所有 JNI framework 内容。
make**framework编译所有 Java framework 内容。
make**services编译系统服务和相关内容。
make <local_target>编译一个指定的模块,local_target 为模块的名称。
make clean-<local_target>清理一个指定模块的编译结果。
make**dump-products显示所有产品的编译配置信息,例如:产品名,产品支持的地区语言,产品中会包含的模块等信息。
make**PRODUCT-xxx-yyy编译某个指定的产品。
make**bootimage生成 boot.img
make**recoveryimage生成 recovery.img
make**userdataimage生成 userdata.img
make**cacheimage生成 cache.img

回页首

## 在 Build 系统中添加新的内容

### 添加新的产品

当我们要开发一款新的 Android 产品的时候,我们首先就需要在 Build 系统中添加对于该产品的定义。

在 Android Build 系统中对产品定义的文件通常位于 device 目录下(另外还有一个可以定义产品的目录是 vender 目录,这是个历史遗留目录,Google 已经建议不要在该目录中进行定义,而应当选择 device 目录)。device 目录下根据公司名以及产品名分为二级目录,这一点我们在概述中已经提到过。

通常,对于一个产品的定义通常至少会包括四个文件:AndroidProducts.mk,产品版本定义文件,BoardConfig.mk 以及 verndorsetup.sh。下面我们来详细说明这几个文件。

* AndroidProducts.mk:该文文件中的内容很简单,其中只需要定义一个变量,名称为“PRODUCT_MAKEFILES”,该变量的值为产品版本定义文件名的列表,例如:

 PRODUCT_MAKEFILES := \
$(LOCAL_DIR)/full_stingray.mk \
$(LOCAL_DIR)/stingray_emu.mk \
$(LOCAL_DIR)/generic_stingray.mk

  • 产品版本定义文件:顾名思义,该文件中包含了对于特定产品版本的定义。该文件可能不只一个,因为同一个产品可能会有多种版本(例如,面向中国地区一个版本,面向美国地区一个版本)。该文件中可以定义的变量以及含义说明如表 6 所示:
表 6. 产品版本定义文件中的变量及其说明

常量

说明

PRODUCT_NAME

最终用户将看到的完整产品名,会出现在“关于手机”信息中。

PRODUCT_MODEL

产品的型号,这也是最终用户将看到的。

PRODUCT_LOCALES

该产品支持的地区,以空格分格,例如:en_GB de_DE es_ES fr_CA。

PRODUCT_PACKAGES

该产品版本中包含的 APK 应用程序,以空格分格,例如:Calendar Contacts。

PRODUCT_DEVICE

该产品的工业设计的名称。

PRODUCT_MANUFACTURER

制造商的名称。

PRODUCT_BRAND

该产品专门定义的商标(如果有的话)。

PRODUCT_PROPERTY_OVERRIDES

对于商品属性的定义。

PRODUCT_COPY_FILES

编译该产品时需要拷贝的文件,以“源路径 : 目标路径”的形式。

PRODUCT_OTA_PUBLIC_KEYS

对于该产品的 OTA 公开 key 的列表。

PRODUCT_POLICY

产品使用的策略。

PRODUCT_PACKAGE_OVERLAYS

指出是否要使用默认的资源或添加产品特定定义来覆盖。

PRODUCT_CONTRIBUTORS_FILE

HTML 文件,其中包含项目的贡献者。

PRODUCT_TAGS

该产品的标签,以空格分格。



通常情况下,我们并不需要定义所有这些变量。Build 系统的已经预先定义好了一些组合,它们都位于 /build/target/product 下,每个文件定义了一个组合,我们只要继承这些预置的定义,然后再覆盖自己想要的变量定义即可。例如:


 # 继承 full_base.mk 文件中的定义
$(call inherit-product, $(SRC_TARGET_DIR)/product/full_base.mk)
# 覆盖其中已经定义的一些变量
PRODUCT_NAME := full_lt26
PRODUCT_DEVICE := lt26
PRODUCT_BRAND := Android
PRODUCT_MODEL := Full Android on LT26

  • BoardConfig.mk:该文件用来配置硬件主板,它其中定义的都是设备底层的硬件特性。例如:该设备的主板相关信息,Wifi 相关信息,还有 bootloader,内核,radioimage 等信息。对于该文件的示例,请参看 Android 源码树已经有的文件。
  • vendorsetup.sh:该文件中作用是通过 add_lunch_combo 函数在 lunch 函数中添加一个菜单选项。该函数的参数是产品名称加上编译类型,中间以“-”连接,例如:add_lunch_combo full_lt26-userdebug。/build/envsetup.sh 会扫描所有 device 和 vender 二 级目 录下的名称 为”vendorsetup.sh”文件,并根据其中的内容来确定 lunch 函数的 菜单选项。
    在配置了以上的文件之后,便可以编译出我们新添加的设备的系统镜像了。

首先,调用“source build/envsetup.sh”该命令的输出中会看到 Build 系统已经引入了刚刚添加的 vendorsetup.sh 文件。

然后再调用“lunch”函数,该函数输出的列表中将包含新添加的 vendorsetup.sh 中添加的条目。然后通过编号或名称选择即可。

最后,调用“make -j8”来执行编译即可。

添加新的模块

关于“模块”的说明在上文中已经提到过,这里不再赘述。

在 源码树中,一个模块的所有文件通常都位于同一个文件夹中。为了将当前模块添加到整个 Build 系统中,每个模块都需要一个专门的 Make 文件,该文件的名称为“Android.mk”。Build 系统会扫描名称为“Android.mk”的文件,并根据该文件中内容编译出相应的产物。

需 要注意的是:在 Android Build 系统中,编译是以模块(而不是文件)作为单位的,每个模块都有一个唯一的名称,一个模块的依赖对象只能是另外一个模块,而不能是其他类型的对象。对于已经 编译好的二进制库,如果要用来被当作是依赖对象,那么应当将这些已经编译好的库作为单独的模块。对于这些已经编译好的库使用 BUILD_PREBUILT 或 BUILD_MULTI_PREBUILT。例如:当编译某个 Java 库需要依赖一些 Jar 包时,并不能直接指定 Jar 包的路径作为依赖,而必须首先将这些 Jar 包定义为一个模块,然后在编译 Java 库的时候通过模块的名称来依赖这些 Jar 包。

下面,我们就来讲解 Android.mk 文件的编写:

Android.mk 文件通常以以下两行代码作为开头:


 LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)


这两行代码的作用是:

1. 设置当前模块的编译路径为当前文件夹路径。
2. 清理(可能由其他模块设置过的)编译环境中用到的变量。
为了方便模块的编译,Build 系统设置了很多的编译环境变量。要编译一个模块,只要在编译之前根据需要设置这些变量然后执行编译即可。它们包括:

LOCAL_SRC_FILES:当前模块包含的所有源代码文件。 LOCAL_MODULE:当前模块的名称,这个名称应当是唯一的,模块间的依赖关系就是通过这个名称来引用的。
LOCAL_C_INCLUDES:C 或 C++ 语言需要的头文件的路径。 LOCAL_STATIC_LIBRARIES:当前模块在静态链接时需要的库的名称。
LOCAL_SHARED_LIBRARIES:当前模块在运行时依赖的动态库的名称。 LOCAL_CFLAGS:提供给 C/C++ 编译器的额外编译参数。
LOCAL_JAVA_LIBRARIES:当前模块依赖的 Java 共享库。 LOCAL_STATIC_JAVA_LIBRARIES:当前模块依赖的 Java 静态库。
LOCAL_PACKAGE_NAME:当前 APK 应用的名称。 LOCAL_CERTIFICATE:签署当前应用的证书名称。
LOCAL_MODULE_TAGS:当前模块所包含的标签,一个模块可以包含多个标签。标签的值可能是 debug, eng, user,development 或者 optional。其中,optional 是默认标签。标签是提供给编译类型使用的。不同的编译类型会安装包含不同标签的模块,关于编译类型的说明如表 7 所示:

##### 表 7. 编译类型的说明









名称说明
eng默认类型,该编译类型适用于开发阶段。
当选择这种类型时,编译结果将:
安装包含 eng, debug, user,development 标签的模块
安装所有没有标签的非 APK 模块 安装所有产品定义文件中指定的 APK 模块



user
该编译类型适合用于最终发布阶段。
当选择这种类型时,编译结果将:

安装所有带有 user 标签的模块 安装所有没有标签的非 APK 模块
安装所有产品定义文件中指定的 APK 模块,APK 模块的标签将被忽略



userdebug
该编译类型适合用于 debug 阶段。
该类型和 user 一样,除了: 会安装包含 debug 标签的模块
编译出的系统具有 root 访问权限




表 3 中的文件已经定义好了各种类型模块的编译方式。所以要执行编译,只需要引入表 3 中对应的 Make 文件即可(通过常量的方式)。例如,要编译一个 APK 文件,只需要在 Android.mk 文件中,加入“include $(BUILD_PACKAGE)

除此以外,Build 系统中还定义了一些便捷的函数以便在 Android.mk 中使用,包括: $(call my-dir):获取当前文件夹路径。
$(call all-java-files-under, &lt;src&gt;):获取指定目录下的所有 Java 文件。 $(call all-c-files-under, &lt;src&gt;):获取指定目录下的所有 C 语言文件。
$(call all-Iaidl-files-under, &lt;src&gt;) :获取指定目录下的所有 AIDL 文件。 $(call all-makefiles-under, &lt;folder&gt;):获取指定目录下的所有 Make 文件。
* $(call intermediates-dir-for, &lt;class&gt;, &lt;app_name&gt;, &lt;host or target&gt;, &lt;common?&gt; ):获取 Build 输出的目标文件夹路径。
清单 2 和清单 3 分别是编译 APK 文件和编译 Java 静态库的 Make 文件示例:

##### 清单 2. 编译一个 APK 文件


  LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
# 获取所有子目录中的 Java 文件
LOCAL_SRC_FILES := $(call all-subdir-java-files)
# 当前模块依赖的静态 Java 库,如果有多个以空格分隔
LOCAL_STATIC_JAVA_LIBRARIES := static-library
# 当前模块的名称
LOCAL_PACKAGE_NAME := LocalPackage
# 编译 APK 文件
include $(BUILD_PACKAGE)

清单 3. 编译一个 Java 的静态库

  LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)

# 获取所有子目录中的 Java 文件
LOCAL_SRC_FILES := $(call all-subdir-java-files)

# 当前模块依赖的动态 Java 库名称
LOCAL_JAVA_LIBRARIES := android.test.runner

# 当前模块的名称
LOCAL_MODULE := sample

# 将当前模块编译成一个静态的 Java 库
include $(BUILD_STATIC_JAVA_LIBRARY)


回页首

## 结束语

整个 Build 系统包含了非常多的内容,由于篇幅所限,本文只能介绍其中最主要内容。

由于 Build 系统本身也是在随着 Android 平台不断的开发过程中,所以不同的版本其中的内容和定义可能会发生变化。网络上关于该部分的资料很零碎,并且很多资料中的一些内容已经过时不再适用,再加上缺少官方文档,所以该部分的学习存在一定的难度。

这就要求我们要有很强的代码阅读能力,毕竟代码是不会说谎的。 要知道,对于我们这些开发人员来说,源代码就是我们最忠实的朋友。 Use the Source,Luke!


参考资料

学习

linux 如何杀死僵尸进程

转自blog.51osos.com/linux/linux-how-to-kill-zombie-process/
In UNIX System terminology, a process that has terminated,but whose parent has not yet waited for it, is called a zombie.
在UNIX 系统中,一个进程结束了,但是他的父进程没有等待(调用wait / waitpid)他, 那么他将变成一个僵尸进程. 在fork()/execve()过程中,假设子进程结束时父进程仍存在,而父进程fork()之前既没安装SIGCHLD信号处理函数调用 waitpid()等待子进程结束,又没有显式忽略该信号,则子进程成为僵尸进程。
如何查看linux系统上的僵尸进程,如何统计有多少僵尸进程?

ps -ef | grep defunct

或者查找状态为Z的进程,Z就是代表zombie process,僵尸进程的意思。
另外使用top命令查看时有一栏为S,如果状态为Z说明它就是僵尸进程。
Tasks: 95 total, 1 running, 94 sleeping, 0 stopped, 0 zombie
top命令中也统计了僵尸进程。或者使用下面的命令:
ps -ef | grep defunct | grep -v grep | wc -l
如何杀死僵尸进程呢?
一般僵尸进程很难直接kill掉,不过您可以kill僵尸爸爸。父进程死后,僵尸进程成为”孤儿进程”,过继给1号进程init,init始终会负责清理僵尸进程.它产生的所有僵尸进程也跟着消失。
ps -e -o ppid,stat | grep Z | cut -d” ” -f2 | xargs kill -9

kill -HUP ps -A -ostat,ppid | grep -e ’^[Zz]‘ | awk ’{print $2}’
当然您可以自己编写更好的shell脚本,欢迎与大家分享。
另外子进程死后,会发送SIGCHLD信号给父进程,父进程收到此信号后,执行waitpid()函数为子进程收尸。就是基于这样的原理:就算父进程没有调用wait,内核也会向它发送SIGCHLD消息,而此时,尽管对它的默认处理是忽略,如果想响应这个消息,可以设置一个处理函数。
如何避免僵尸进程呢?
处理SIGCHLD信号并不是必须的。但对于某些进程,特别是服务器进程往往在请求到来时生成子进程处理请求。如果父进程不等待子进程结 束,子进程将成为僵尸进程(zombie)从而占用系统资源。如果父进程等待子进程结束,将增加父进程的负担,影响服务器进程的并发性能。在Linux下 可以简单地将 SIGCHLD信号的操作设为SIG_IGN。
signal(SIGCHLD,SIG_IGN);
这样,内核在子进程结束时不会产生僵尸进程。这一点与BSD4不同,BSD4下必须显式等待子进程结束才能释放僵尸进程
或者
用两次fork(),而且使紧跟的子进程直接退出,是的孙子进程成为孤儿进程,从而init进程将负责清除这个孤儿进程。
本文水平有限,关于zombie更深层次的理解欢迎在评论中与大家分享。
Tags: zombie
wmflw on 2013 年 9 月 25 日 at 下午 4:21 said:
ps -ef | grep defunct | grep -v grep | awk ‘{print “kill -9 “ $2,$3}’
一条自动命令,试了下还不错的

一起来玩hadoop

1 使用百度开放研究社区






百度提供了开放研究社区,这里我们可以用一个百度的hadoop资源,就不需要你自己搭建hadoop了,这多爽啊!下面我们来使用使用这个东西。

1.2 打开百度开放研究社区的web Shell






百度提供了一种类似shell的方式来提供交互。


我们在申请账号之后,进入到这个网页去看说明 http://openresearch.baidu.com/activity/platform.jspx 

2 使用百度云平台






百度的这个云平台网址是http://cc-ws.baidu.com,进入web shell之后如下图





图1 web shell


这个可以认为是一个类unix的操作界面吧,不过这里只支持这些命令,下面主要讲讲用这个来进行一些hadoop的经典入门——wordCount

<span style="font-family:times; orphans:0; text-indent:32px; widows:0"><span style="font-size:14px">

</span></span>

<span style="font-family:times; orphans:0; text-indent:32px; widows:0"><span style="font-size:14px">2.1 生成count所需的文件并且上传到百度的hadoop环境</span></span>






由于这个百度自带的upload和download命令有点不好用,所以我用从自己的百度网盘里面搞数据过来,我在我的网盘里面上传了两个文件f1.txt,f2.txt



hello world    #f1.txt






hello programmer    #f2.txt


然后通过命令把文件同步到hdfs上



<pre lang=”shell””>pan -put ./f*.txt ./input
把网盘的两个文件都同步到hdfs目录下的input目录里面


然后使用



hadoop fs -ls ./input

查看,结果如下




图2 结果已同步到hdfs上(涂掉的部分是我的用户名)



##




##
2.2 使用自带的wordcount例子来运行wordcount







百度的云平台有三个全局变量,hadoop命令如下介绍



Hadoop命令
■ hadoop: 用于访问Hadoop的功能,其使用方式与命令行使用hadoop的方式一致。对于普通用户权限,其能够使用的hadoop命令包括hadoop fs,hadoop jar,hadoop job等。相对于hadoop命令的本地文件系统的当前路径即为用户的工作目录。为了访问hadoop系统的某些模块,平台也提供了三个变量可以直接使用,包括:$hadoop_home(hadoop系统的根目录);$hadoop_examples(hadoop-examples-1.0.0.jar的路径),$hadoop_streaming(hadoop-streaming-1.0.0.jar的路径)


我们只需要执行下面的命令就可以统计了



hadoop jar $hadoop_examples wordcount input output

运行结果如下




图三 这个是wordcount运行结果



我们进入output文件夹查看结果







图四 这个是计算出来的结果,结果放在part-r-00000






这下把这些单词统计过来了。


下次准备在本地写一个本地版本的wordcount,然后上传上来玩玩

高可用MYSQL学习之路(一)——环境搭建&mysql复制

1.资源概述

由于实验室机器太少,手上只有一个服务器和自己的小笔记本,一直想玩玩这种感觉高大上的mysql集群,今天终于下定决心进行。


在这里,我遇到了两个问题,先给大家阐述一下,希望能帮助大家



一台机器里面如何装多个mysql 为复制而创建的用户不能本地登录


下面围绕这两个问题进行解决。

2.如何在一台机器里面安装多个mysql

显然这个是开多个mysql的实例,只是对应着不同的端口就可以轻松达成。


下面讲讲我的做法。


1. 下载mysql源码,我这里下载的是mysql5.5.32的


2. 使用牛叉的cmake、make、make install进行安装


安装第一个实例mysql1


首先cmake



cmake \
-DCMAKE_INSTALL_PREFIX=/data/mysql1 \ #安装路径
-DMYSQL_DATADIR=/data/mysql1/data \ #数据文件存放位置
-DSYSCONFDIR=/etc \ #my.cnf路径
-DWITH_MYISAM_STORAGE_ENGINE=1 \ #支持MyIASM引擎
-DWITH_INNOBASE_STORAGE_ENGINE=1 \ #支持InnoDB引擎
-DWITH_MEMORY_STORAGE_ENGINE=1 \ #支持Memory引擎
-DWITH_READLINE=1 \ #快捷键功能(我没用过)
-DMYSQL_UNIX_ADDR=/data/mysql1/mysql1.sock \ #连接数据库socket路径
-DMYSQL_TCP_PORT=3306 \ #端口
-DENABLED_LOCAL_INFILE=1 \ #允许从本地导入数据
-DWITH_PARTITION_STORAGE_ENGINE=1 \ #安装支持数据库分区
-DEXTRA_CHARSETS=all \ #安装所有的字符集
-DDEFAULT_CHARSET=utf8 \ #默认字符
-DDEFAULT_COLLATION=utf8_general_ci



执行完毕之后我们然后make & make install


这样我们的mysql就安装好了。


然后我们需要做的事情就是为这个实例写一个配置文件


3. 为我们的这个实例搞一个配置文件


配置文件这个东西其实mysql都准备好了的,我们只需要拿出来改一改,进入到我们的安装目录/data/mysql1,将support-file文件夹下的my-medium.cnf作为我们的配置文件吧,


我将my-medium.cnf拷贝到了/data/mysql1目录下,并重命名为my.cnf,命令如下



cp my-medium.cnf ../my.cnf

打开我们的my.cnf,我们主要是需要改如下几个地方



[client]
#password = your_password
port = 3308
socket = /data/mysql1/mysql1.sock


# Here follows entries for some specific programs

# The MySQL server
[mysqld]
port = 3308
socket = /data/mysql1/mysql1.sock

skip-external-locking
key_buffer_size = 16M
max_allowed_packet = 1M
table_open_cache = 64
sort_buffer_size = 512K
net_buffer_length = 8K
read_buffer_size = 256K
read_rnd_buffer_size = 512K
myisam_sort_buffer_size = 8M

# Don’t listen on a TCP/IP port at all. This can be a security enhancement,
# if all processes that need to connect to mysqld run on the same host.
# All interaction with mysqld must be made via Unix sockets or named pipes.
# Note that using this option without enabling named pipes on Windows
# (via the "enable-named-pipe" option) will render mysqld useless!
#
#skip-networking

# Replication Master Server (default)
# binary logging is required for replication
log-bin=mysql1-bin

# binary logging format - mixed recommended
binlog_format=mixed

# required unique id between 1 and 2^32 - 1
# defaults to 1 if master-host is not set
# but will not function as a master if omitted
server-id = 2


port不用说了,这个肯定是为每一个mysql确定一个端口,socket这个是指每个mysql实例需要用的sock文件,server-id这个是全局统一的id号,不可以出现重复的,这三个地方是需要注意的。





4. 配置文件更改完毕之后,我们对mysql这个实例进行初始化


进入到scripts文件夹下,运行下面的命令



./mysql_install_db —defaults-file=/data/mysql1/my.cnf —basedir=/data/mysql1 —datadir=/data/mysql1/data —user=mysql



—defaults-file是指我们刚刚修改的配置文件,注意这里必须要用绝对路径,用相对路径就是个坑,坑爆了。


—basedir是指我们的这个实例所在的安装目录。


—datadir是指我们这个实例以后放数据的文件夹


5.这个脚本执行成功之后,我们进入到其bin的文件夹下准备启动它


执行以下命令



./mysqld_safe —defaults-file=/data/mysql1/my.cnf

一般的情况下,我们不出意外就可以启动它了


6.设置root的密码和远程访问的权限



./mysqladmin -P 3308 -S /data/mysql1/mysql1.sock -u root password

由于我们是多个实例,所以我们需要随时指定-P端口 -S sock文件


为了方便,我自己写了两个小脚本进行处理,入参如下


第一个脚本runSQL.sh,主要是进行关闭和启动mysql的


第一个参数是实例编号,像上面我们在/data/mysql1所以编号是1;


第二个参数是命令,shutdown和start,分别控制启动和关闭


第三个参数是端口号


以上三个参数都需要加上



dir=’/data/mysql’;
para=$1;
port=$3;
#echo $para;
sqlBasedir=$dir$para;
sqlDatadir=$sqlBasedir’/data’;
sqlRundir=$sqlBasedir’/bin’;
sqlConfigdir=$sqlBasedir’/my.cnf’;
sqlRunSock=$sqlBasedir’/mysql’$para’.sock’;
echo $2;
echo $sqlRundir;
cmd="start";
if [ "$cmd"x = "$2"x ];then
echo ‘start’;
$sqlRundir/mysqld_safe —defaults-file=$sqlConfigdir —user=mysql&
echo ‘ok’;
#echo $sqlBasedir;
#echo $sqlDatadir;
else
echo ‘shutdown’;
$sqlRundir/mysqladmin -P $port -S $sqlRunSock -uroot -p shutdown;
fi




第二个脚本是enterSQL.sh,主要是通过root账号进入到mysql控制台


第一个参数是实例编号,意义同上一个脚本


第二个参数是端口号



dir=’/data/mysql’;
para=$1;
port=$2;
#echo $para;
sqlBasedir=$dir$para;
sqlDatadir=$sqlBasedir’/data’;
sqlRundir=$sqlBasedir’/bin’;
sqlConfigdir=$sqlBasedir’/my.cnf’;
sqlRunSock=$sqlBasedir’/mysql’$para’.sock’;
$sqlRundir/mysql -P $port -S $sqlRunSock -uroot -p ;


这下我们安装好了一个区别于一般情况的mysql实例了。


下面只需要重复上面的步骤,分别把上面my.cnf中的红色的地方换掉就可以了。

3.新建的用户不能本地登录

一开始我为主从复制创建了一个新的用户,结果我发现,新创建的用户是不能本地登录的!以至于我的slave总是连接不上master而不能进行复制操作。


参考下文

4.来做我们的主从复制吧~

· 在master上创建一个复制账号并且授权



create user repl_user;
grant replication slave on . to repl_user@’%’ identified by ‘123456’;




然后在slave上进行如下配置



change master to master_host=’localhost’ master_port=3309 master_user=’repl_user’ master_password=’123456’

因为是在一台机器上所以host就是localhost,我选取的是一个端口为3309的mysql实例。


执行完这句之后,然后执行


start slave



这样大功告成。



有问题欢迎交流~



尾递归

1.什么是递归?

递归就是自己调用自己

2.编译器是如何实现递归的?

编译器是通过栈来实现递归,其实编译器也是通过栈来实现函数调用的,为了明白递归,我们先来看看我们的程序是如何实现函数调用的吧。

下面我们看一个函数调用的栗子

int adder(int x,int y)
{
return x+y;
}

void call()
{
int x=2;
int y=3;
adder(x,y);
}
 

我们使用gcc编译成汇编(PS:要想明白一个程序是怎么运行的最好的方式还是看汇编吧)

gcc -S test.c

 

下面我们看看汇编片段


.file “recu.c”
.text
.globl adder
.type adder, @function
adder:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl %edi, -4(%rbp)
movl %esi, -8(%rbp)
movl -8(%rbp), %eax
movl -4(%rbp), %edx
addl %edx, %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size adder, .-adder
.globl call
.type call, @function
call:
.LFB1:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $16, %rsp
movl $2, -4(%rbp)
movl $3, -8(%rbp)
movl -8(%rbp), %edx
movl -4(%rbp), %eax
movl %edx, %esi
movl %eax, %edi
call adder
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE1:
.size call, .-call
.ident “GCC: (GNU) 4.8.2”
.section .note.GNU-stack,””,@progbits

 

擦!汇编怎么那么多看不懂的符号啊?!!真拙计!

**我们这里是r开头的,原因是我们使用的是64位的操作系统**

为了读懂汇编,我还是先去复习了一下,首先先明白几个寄存器吧

在X86上,用户寄存器为eax, ebx, ecx, edx, esi, edi, ebp, esp 以及eip

eax、ebx、ecx以及edx寄存器作为通用寄存器,可以用来进行临时存储

esi和edi可以用来存储,但对操作字符串类的函数有其他意义,在很多字符串操作指令中,&nbsp;DS:ESI指向源串,而ES:EDI指向目标串

ebp通常用来容纳当前栈帧(stack frame)的内存地址,esp保存栈顶地址
eip保存当前执行指令的内存地址。机器代码不能直接修改该寄存器,只能通过jmp和call指令族进行间接修改,实现循环,调用等

 

然后我们在明白一下重要术语:

栈帧:值得是ebpesp的这一段内存区间,每一个函数的调用都会生成一个栈帧,这里面保存着函数里的变量和指令。

我们主要关心call函数以及adder函数,所以单独拿出来

我们先来看call函数:

**这里的栈类别是指的向下满栈
call:
.LFB1:
.cfi_startproc                 ##这个表示函数的入口参见.cfi_startproc pushq %rbp                 ##把rbp寄存器入栈
.cfi_def_cfa_offset 16         ##定义CFI(call frame information)的cfa(Canonical Frame Address)的偏移量,主要是由于push了%rbp造成的偏移量
.cfi_offset 6, -16             ##.cfi我也不明
movq %rsp, %rbp ##把rsp的值搞到rbp中去,表示现在进入了新栈
.cfi_def_cfa_register 6 ##说明现在的cfa register是rbp
subq $16, %rsp              ##空出两个单位,根据程序,我们应该猜得到是为x、y变量提供空间,为毛是压16个字节?难道是内存对其?
movl $2, -4(%rbp)           ##把rbp下面的一个4字节定义为x的空间
movl $3, -8(%rbp)           ##把rbp下面的第8个字节处定义成y的空间
movl -8(%rbp), %edx         ##把x、y的地址赋给寄存器edx和eax,这两个是通用寄存器
movl -4(%rbp), %eax
movl %edx, %esi             ##又把他们送个esi、edi这里是为传值做准备
movl %eax, %edi
call adder                 ##这里调用adder函数,同时这个指令暗含一个把当前栈顶压入栈
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc

下面用图来说明
   
 
好了,下面我们来谈谈尾递归。
主要围绕下面一个方面
1.什么是尾递归?与递归有什么区别?
我们先来一段代码来看看这两者之间的区别
 

int fact(int n){
if(n <0) return 0;
if(n ==0) return 1;
if(n ==1) return 1;
return n*fact(n-1);
}

int facttail(int n,int a){
if(n <0) return 0;
if(n ==0) return 1;
if(n ==1) return a;

return facttail(n-1,n*a);

}
 
fact函数是递归版本,facttail函数是尾递归版本。
 
我们在文章最初知道了函数调用是通过栈帧来实现的,因为栈帧中需要保存运算的变量,在使用递归的时候,比如函数fact,如果n非常大的话,很显然栈帧会越来越多,直到撑爆内存,这显然是我们不乐意见到的。
但是我们来看factail这个函数,它也是使用递归来实现求n的阶乘,但是注意到,这个函数中,其栈帧是没有变量需要保存的,这就是说我们的栈帧是不需要的,我们完全可以就在原来的栈帧上进行运算!!
于是乎C编译器抖了个机灵,它如果发现了你使用了尾递归,他就会自动帮你优化,现在我们来看着两个函数的汇编。
为了看着方便,我们把判断全部删除,如下
 

int fact(int n){
return n*fact(n-1);
}

int facttail(int n,int a){
return facttail(n-1,n*a);
}
汇编代码如下:
 
 

fact:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $16, %rsp
movl %edi, -4(%rbp)
movl -4(%rbp), %eax
subl $1, %eax
movl %eax, %edi
call fact
imull -4(%rbp), %eax
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size fact, .-fact
.globl facttail
.type facttail, @function
facttail:
.LFB1:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $16, %rsp
movl %edi, -4(%rbp)
movl %esi, -8(%rbp)
movl -4(%rbp), %eax
imull -8(%rbp), %eax
movl -4(%rbp), %edx
subl $1, %edx
movl %eax, %esi
movl %edx, %edi
call facttail
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc

卧槽!怎么都是递归呢?和传说中主动优化的不一样呢?
 
原来是我搞忘记加入优化参数了,现在把优化参数加上,汇编代码如下
 

.file   “fact.c”
.text
.p2align 4,,15
.globl fact
.type fact, @function
fact:
.LFB7:
.cfi_startproc
.p2align 4,,10
.p2align 3
.L2:
jmp .L2
.cfi_endproc
.LFE7:
.size fact, .-fact
.p2align 4,,15
.globl facttail
.type facttail, @function
facttail:
.LFB8:
.cfi_startproc
.p2align 4,,10
.p2align 3
.L4:
jmp .L4
.cfi_endproc
.LFE8:
.size facttail, .-facttail
.ident “GCC: (GNU) 4.8.2”
.section .note.GNU-stack,””,@progbits

卧槽,我完全看不懂了,不过估计这东西优化成为了一个循环,看.L4,不过这个怎么把递归也搞成循环了?!这个真心不懂,求大家指教

1966年图灵奖——艾伦·佩利(Alan J. Perlis)

艾伦·杰·佩利Alan Jay Perlis,1922年4月1日-1990年2月7日),生于美国宾夕法尼亚州匹兹堡,是美国计算机程序设计领域的科学,首届图灵奖的获得者。

<span style="font-family:sans-serif;font-size:15px;line-height:22.066667556762695px;background-color:#FFFFFF;">![](http://zt2peilong-wordpress.stor.sinaapp.com/uploads/2014/02/Alan-Perlis.jpg) </span> 

Alan Perlis获得图灵奖的颁奖词如下:

由于其在[高级程序设计](http://zh.wikipedia.org/w/index.php?title=%E9%AB%98%E7%BA%A7%E7%A8%8B%E5%BA%8F%E8%AE%BE%E8%AE%A1&amp;action=edit&amp;redlink=1 "%E9%AB%98%E7%BA%A7%E7%A8%8B%E5%BA%8F%E8%AE%BE%E8%AE%A1&amp;action=edit&amp;redlink=1")技术和[编译器](http://zh.wikipedia.org/wiki/%E7%BC%96%E8%AF%91%E5%99%A8)构造方面的影响。

(_for his influence in the area of advanced programming techniques and compiler construction_)

从获奖词我们看出Alan Perlis最大的贡献就是他的ALGOL语言了。

_下面内容转载自《[程序员](http://www.programmer.com.cn/4752/#more-4752)》。_ 

当我们提起高级语言的先驱,首先想到的总是Dennis Ritchie、Bjarne Stroustrup这些主流语言的设计者,似乎很少会提到Alan Perlis这个名字。但Alan Perlis主持设计的ALGOL,趋势C/C++等语言的鼻祖。

Alan Perlis 1922年出生于美国匹兹堡犹太家庭。21岁时,他获得了卡内基理工学院的化学学士学位,这个学院后来发展为现在美国计算机专业排名第一的卡内基梅隆大学。时逢二战,他弃笔从戎。服役期间,他突然对数学产生了强烈兴趣,并在1950年, 从麻省理工学院获得了数学博士学位。两年后他来到普渡大学,出任普渡大学计算中心首任主任,并将IBM CPC计算机引入了普渡,还为其设计了一个叫作IT(Internal Translator)的编程语言。四年之后,他又一跃当上了卡内基理工学院第一任计算机科学系主任,在卡耐基引入了IBM 650,并将IT语言移植到650上。这些使他在程序语言的设计方面,有了许多经验和体会。当ACM成立程序设计语言委员会时,Perlis坐上了主席的位置。

1958年5月27日,瑞士苏黎世召开了一场8个人的讨论会。这个会议并不隆重。ACM的4名代表和德国应用数学和力学学会(GAMM)的4名代表,索性把地点和双方名称连起来,叫苏黎世ACM-GAMM会议。这场会议的组织者,就是Alan Perlis。他们将要在此讨论并规划一种新型的编程语言,叫作国际代数语言(IAL)。

世界上第一个高级语言Fortran存在一些严重的缺陷。比如说它专门为IBM 704设计的,要依赖特定的机器型号工作,很难向其他机器移植等等。IAL的专家们力求设计一种更好的高级语言。在讨论过程中,Perlis认为 “IAL”这个词很绕口,于是将它改名叫作ALGOL。会议结束后,他们成立了一个工作组,根据讨论的结果,开发ALGOL的编译器。1958年年底,第一套编译器诞生了,按照年份命名为ALGOL 58。1960年,Alan Perlis总结了一些经验之后,再次召集参与ALGOL工作的计算机科学家们在巴黎进行了另一场研讨。这场会议的结果,就是后来的ALGOL 60。

1960年夏天,Dijkstra开发了第一个ALGOL 60的编译器,于是ALGOL系列语言正式登上了计算机科学的舞台。随后,Alan Perlis将它引入了大学的课堂。这是Alan Perlis的另一个重要贡献,因为在那个时代,计算机科学混沌初开,程序设计都只是数值分析的一部分。经过Alan Perlis的努力,人们终于看到计算机科学应该是什么样子。Dijkstra后来说,这是一个伟大的标志,可以说直到这个时候,计算机科学才真正地诞生了。而在接下来的30年里,ALGOL一直是教学和学术界用来描述算法的不二之选。它体现出的许多概念,都被后来的编程语言沿用。包括C、C++和 Pascal在内许多主流语言,都因为继承了ALGOL的许多概念,而被称为“类ALGOL语言”。

Alan Perlis因领导了ALGOL的设计工作,并在早期计算机教育中做出了重要贡献,在1962年当选为美国计算机学会的主席,并在四年后,因为他对高级编程技术及其编译器构造的影响成为历史上首位图灵奖得主。五年后,他跳槽到耶鲁大学,连任多年计算机科学系主任,1977年当选美国工程院院士。1982 年,退休后的Alan Perlis返老还童,写下了名文“Epigrams on Programming”,发表在ACM的SIGPLAN期刊上。其中包含130条编程箴言,可能是被引用最广泛的计算机文章之一。在发表在ACM的 SIGPLAN期刊上,他写下了一系列关于编程的幽默:“如果你给别人讲解程序时,看到对方点头了,那你就拍他一下,他肯定睡觉了。”

Alan Perlis为名著《[计算机程序的构造和解释](http://www.amazon.cn/mn/detailApp/ref=as_li_qf_sp_asin_tl?_encoding=UTF8&amp;tag=vastwork-23&amp;linkCode=as2&amp;asin=B0011AP7RY&amp;camp=536&amp;creative=3200&amp;creativeASIN=B0011AP7RY)》所写的序中写道,**如果说艺术解释了我们的梦想,那么计算机就是以程序的名义执行着它们。**计算机科学中特别重要的一点是保持趣味性,不要局限于眼前,应该不断寻找新方向,扩展计算机的能力。这些话今天听来,依然发人深省。

1990年2月7日,Alan Perlis因心脏病在纽哈芬去世,享年68岁。

_下面内容是Alan Perlis的《Epigrams On Programming》_ 

<!--more-->

Epigrams on Programming

Alan J. Perlis

Yale University

This text has been published in SIGPLAN Notices Vol. 17, No. 9, September 1982, pages 7 - 13. I’m offering it here online until ACM stops me.

The phenomena surrounding computers are diverse and yield a surprisingly rich base for launching metaphors at individual and group activities. Conversely, classical human endeavors provide an inexhaustible source of metaphor for those of us who are in labor within computation. Such relationships between society and device are not new, but the incredible growth of the computer's influence (both real and implied) lends this symbiotic dependency a vitality like a gangly youth growing out of his clothes within an endless puberty.

The epigrams that follow attempt to capture some of the dimensions of this traffic in imagery that sharpens, focuses, clarifies, enlarges and beclouds our view of this most remarkable of all mans' artifacts, the computer.
  1. One man’s constant is another man’s variable.(一个人的常量是别人的变量)
  2. Functions delay binding: data structures induce binding. Moral: Structure data late in the programming process.(这个翻译不来==)
  3. Syntactic sugar causes cancer of the semicolons.(糖衣语法容易引起分号的问题?)
  4. Every program is a part of some other program and rarely fits.(每一段程序都是另外一段程序的一部分,但总有差别?)
  5. If a program manipulates a large amount of data, it does so in a small number of ways.(一段程序只有使用很少的方法操纵了一大堆的数据?)
  6. Symmetry is a complexity reducing concept (co-routines include sub-routines); seek it everywhere.(追求对称性是一种降低复杂度的方法(和子函数合作),我们要不断的去追求)
  7. It is easier to write an incorrect program than understand a correct one.(写一段错误的程序比理解一段正确的程序容易)
  8. A programming language is low level when its programs require attention to the irrelevant.(如果一种程序语言关注一些不相关的东西,那么它就是低级的)
  9. It is better to have 100 functions operate on one data structure than 10 functions on 10 data structures.(使用100个函数操作一个数据结构比使用是个函数操作10个数据结构好)
  10. Get into a rut early: Do the same processes the same way. Accumulate idioms. Standardize. The only difference (!) between Shakespeare and you was the size of his idiom list - not the size of his vocabulary.(开始积累:使用同样的方法做同样的事,积累常用方法和标准。你和莎士比亚的区别不是在于单词量上的差距,而是在于习语上)
  11. If you have a procedure with 10 parameters, you probably missed some.(如果你需要处理十个参数,你最好忽略掉其中一些)
  12. Recursion is the root of computation since it trades description for time.(递归是计算的根本,应为它更加容易表示清楚,虽然花了更多的时间)
  13. If two people write exactly the same program, each should be put in micro-code and then they certainly won’t be the same.(如果两个人写了完全一样的程序,那么把程序放缩到微代码级别,他们的程序一定是不一样的)
  14. In the long run every program becomes rococo - then rubble.(每个程序长时间运行都会变的很渣)
  15. Everything should be built top-down, except the first time.(每次应该从顶至下的构造,除了第一次)
  16. Every program has (at least) two purposes: the one for which it was written and another for which it wasn’t.(每次写程序都有2个目标:一个是写你想要的功能,另一个是写你不想要的功能)
  17. If a listener nods his head when you’re explaining your program, wake him up.(你向别人讲你的程序,如果他点头了,那么他一定是睡着了)
  18. A program without a loop and a structured variable isn’t worth writing.(一个没有循环和结构化变量的程序是不值得写的)
  19. A language that doesn’t affect the way you think about programming, is not worth knowing.(一个不能影响你思考程序的编程语言是不值得去学习的)
  20. Wherever there is modularity there is the potential for misunderstanding: Hiding information implies a need to check communication.(只有有模块化,那就有误解的可能,隐藏信息基本上都意味着需要交流)
  21. Optimization hinders evolution.(优化阻碍发展)
  22. A good system can’t have a weak command language.(好的系统一定有个强大的控制语言)
  23. To understand a program you must become both the machine and the program.(理解一个程序的最佳方式就是把你自己变成机器和程序)
  24. Perhaps if we wrote programs from childhood on, as adults we’d be able to read them.(如果我们从小就开始写程序,那么当我们成年的时候我们就有能力去读他们了)
  25. One can only display complex information in the mind. Like seeing, movement or flow or alteration of view is more important than the static picture, no matter how lovely.(人只能在脑中展现复杂的信息。就像是远闻不如一见)
  26. There will always be things we wish to say in our programs that in all known languages can only be said poorly.(总是会出现这样的事儿:每个人都认为自己的程序是最好)
  27. Once you understand how to write a program get someone else to write it.(一旦你某一天理解了如何去写程序,就叫另外一个人去写)
  28. Around computers it is difficult to find the correct unit of time to measure progress. Some cathedrals took a century to complete. Can you imagine the grandeur and scope of a program that would take as long?(计算机里很难去找到正确的单位时间去丈量进步)
  29. For systems, the analogue of a face-lift is to add to the control graph an edge that creates a cycle, not just an additional node.(对于一个系统,模拟一个侧面就是在控制图中增加一个边创造一个循环,而不是仅仅增加一个节点)
  30. In programming, everything we do is a special case of something more general - and often we know it too quickly.(在写程序里,我们做的大多数都是一般情况中的个例,我们经常知道的太快了)
  31. Simplicity does not precede complexity, but follows it.(简单的方法不意味着领先复杂的方法,但是还是要做用简单的方法)
  32. Programmers are not to be measured by their ingenuity and their logic but by the completeness of their case analysis.(程序猿的评估方法不是他们的天赋和逻辑,是他们分析问题的完整程度)
  33. The 11th commandment was “Thou Shalt Compute” or “Thou Shalt Not Compute” - I forget which.(我不记得第十一诫里应该是“你应该计算”还是“你不该计算”)
  34. The string is a stark data structure and everywhere it is passed there is much duplication of process. It is a perfect vehicle for hiding information.(字符串是一种很爽的数据结构,无论什么时候都是通过复制操作进行传送的,这简直是隐藏信息的良器)
  35. Everyone can be taught to sculpt: Michelangelo would have had to be taught how not to. So it is with the great programmers.(你可以教任何人雕刻:但是对米开朗基罗来说,他应该被教那些事儿不能去做,这对于好的程序员也是一样的)
  36. The use of a program to prove the 4-color theorem will not change mathematics - it merely demonstrates that the theorem, a challenge for a century, is probably not important to mathematics.
  37. The most important computer is the one that rages in our skulls and ever seeks that satisfactory external emulator. The standardization of real computers would be a disaster - and so it probably won’t happen.
  38. Structured Programming supports the law of the excluded muddle.
  39. Re graphics: A picture is worth 10K words - but only those to describe the picture. Hardly any sets of 10K words can be adequately described with pictures.
  40. There are two ways to write error-free programs; only the third one works.
  41. Some programming languages manage to absorb change, but withstand progress.
  42. You can measure a programmer’s perspective by noting his attitude on the continuing vitality of FORTRAN.
  43. In software systems it is often the early bird that makes the worm.
  44. Sometimes I think the only universal in the computing field is the fetch-execute-cycle.
  45. The goal of computation is the emulation of our synthetic abilities, not the understanding of our analytic ones.
  46. Like punning, programming is a play on words.
  47. As Will Rogers would have said, “There is no such thing as a free variable.”
  48. The best book on programming for the layman is “Alice in Wonderland”; but that’s because it’s the best book on anything for the layman.
  49. Giving up on assembly language was the apple in our Garden of Eden: Languages whose use squanders machine cycles are sinful. The LISP machine now permits LISP programmers to abandon bra and fig-leaf.
  50. When we understand knowledge-based systems, it will be as before - except our finger-tips will have been singed.
  51. Bringing computers into the home won’t change either one, but may revitalize the corner saloon.
  52. Systems have sub-systems and sub-systems have sub-systems and so on ad infinitum - which is why we’re always starting over.
  53. So many good ideas are never heard from again once they embark in a voyage on the semantic gulf.
  54. Beware of the Turing tar-pit in which everything is possible but nothing of interest is easy.
  55. A LISP programmer knows the value of everything, but the cost of nothing.
  56. Software is under a constant tension. Being symbolic it is arbitrarily perfectible; but also it is arbitrarily changeable.
  57. It is easier to change the specification to fit the program than vice versa.
  58. Fools ignore complexity. Pragmatists suffer it. Some can avoid it. Geniuses remove it.
  59. In English every word can be verbed. Would that it were so in our programming languages.
  60. Dana Scott is the Church of the Lattice-Way Saints.
  61. In programming, as in everything else, to be in error is to be reborn.
  62. In computing, invariants are ephemeral.
  63. When we write programs that “learn”, it turns out we do and they don’t.
  64. Often it is means that justify ends: Goals advance technique and technique survives even when goal structures crumble.
  65. Make no mistake about it: Computers process numbers - not symbols. We measure our understanding (and control) by the extent to which we can arithmetize an activity.
  66. Making something variable is easy. Controlling duration of constancy is the trick.
  67. Think of all the psychic energy expended in seeking a fundamental distinction between “algorithm” and “program”.
  68. If we believe in data structures, we must believe in independent (hence simultaneous) processing. For why else would we collect items within a structure? Why do we tolerate languages that give us the one without the other?
  69. In a 5 year period we get one superb programming language. Only we can’t control when the 5 year period will begin.
  70. Over the centuries the Indians developed sign language for communicating phenomena of interest. Programmers from different tribes (FORTRAN, LISP, ALGOL, SNOBOL, etc.) could use one that doesn’t require them to carry a blackboard on their ponies.
  71. Documentation is like term insurance: It satisfies because almost no one who subscribes to it depends on its benefits.
  72. An adequate bootstrap is a contradiction in terms.
  73. It is not a language’s weaknesses but its strengths that control the gradient of its change: Alas, a language never escapes its embryonic sac.
  74. It is possible that software is not like anything else, that it is meant to be discarded: that the whole point is to always see it as soap bubble?
  75. Because of its vitality, the computing field is always in desperate need of new cliches: Banality soothes our nerves.
  76. It is the user who should parameterize procedures, not their creators.
  77. The cybernetic exchange between man, computer and algorithm is like a game of musical chairs: The frantic search for balance always leaves one of the three standing ill at ease.
  78. If your computer speaks English it was probably made in Japan.
  79. A year spent in artificial intelligence is enough to make one believe in God.
  80. Prolonged contact with the computer turns mathematicians into clerks and vice versa.
  81. In computing, turning the obvious into the useful is a living definition of the word “frustration”.
  82. We are on the verge: Today our program proved Fermat’s next-to-last theorem!
  83. What is the difference between a Turing machine and the modern computer? It’s the same as that between Hillary’s ascent of Everest and the establishment of a Hilton hotel on its peak.
  84. Motto for a research laboratory: What we work on today, others will first think of tomorrow.
  85. Though the Chinese should adore APL, it’s FORTRAN they put their money on.
  86. We kid ourselves if we think that the ratio of procedure to data in an active data-base system can be made arbitrarily small or even kept small.
  87. We have the mini and the micro computer. In what semantic niche would the pico computer fall?
  88. It is not the computer’s fault that Maxwell’s equations are not adequate to design the electric motor.
  89. One does not learn computing by using a hand calculator, but one can forget arithmetic.
  90. Computation has made the tree flower.
  91. The computer reminds one of Lon Chaney - it is the machine of a thousand faces.
  92. The computer is the ultimate polluter. Its feces are indistinguishable from the food it produces.
  93. When someone says “I want a programming language in which I need only say what I wish done,” give him a lollipop.
  94. Interfaces keep things tidy, but don’t accelerate growth: Functions do.
  95. Don’t have good ideas if you aren’t willing to be responsible for them.
  96. Computers don’t introduce order anywhere as much as they expose opportunities.
  97. When a professor insists computer science is X but not Y, have compassion for his graduate students.
  98. In computing, the mean time to failure keeps getting shorter.
  99. In man-machine symbiosis, it is man who must adjust: The machines can’t.
  100. We will never run out of things to program as long as there is a single program around.
  101. Dealing with failure is easy: Work hard to improve. Success is also easy to handle: You’ve solved the wrong problem. Work hard to improve.
  102. One can’t proceed from the informal to the formal by formal means.
  103. Purely applicative languages are poorly applicable.
  104. The proof of a system’s value is its existence.
  105. You can’t communicate complexity, only an awareness of it.
  106. It’s difficult to extract sense from strings, but they’re the only communication coin we can count on.
  107. The debate rages on: Is PL/I Bactrian or Dromedary?
  108. Whenever two programmers meet to criticize their programs, both are silent.
  109. Think of it! With VLSI we can pack 100 ENIACs in 1 sq.cm.
  110. Editing is a rewording activity.
  111. Why did the Roman Empire collapse? What is the Latin for office automation?
  112. Computer Science is embarrassed by the computer.
  113. The only constructive theory connecting neuroscience and psychology will arise from the study of software.
  114. Within a computer natural language is unnatural.
  115. Most people find the concept of programming obvious, but the doing impossible.
  116. You think you know when you learn, are more sure when you can write, even more when you can teach, but certain when you can program.
  117. It goes against the grain of modern education to teach children to program. What fun is there in making plans, acquiring discipline in organizing thoughts, devoting attention to detail and learning to be self-critical?
  118. If you can imagine a society in which the computer-robot is the only menial, you can imagine anything.
  119. Programming is an unnatural act.
  120. Adapting old programs to fit new machines usually means adapting new machines to behave like old ones.
  121. In seeking the unattainable, simplicity only gets in the way.

    If there are epigrams, there must be meta-epigrams.
    
  122. Epigrams are interfaces across which appreciation and insight flow.

  123. Epigrams parameterize auras.
  124. Epigrams are macros, since they are executed at read time.
  125. Epigrams crystallize incongruities.
  126. Epigrams retrieve deep semantics from a data base that is all procedure.
  127. Epigrams scorn detail and make a point: They are a superb high-level documentation.
  128. Epigrams are more like vitamins than protein.
  129. Epigrams have extremely low entropy.
  130. The last epigram? Neither eat nor drink them, snuff epigrams.