[转载]Android应用安全之数据传输安全 - bamb00 - 博客园

mikel阅读(1060)

[转载]Android应用安全之数据传输安全 – bamb00 – 博客园.

Android软件通常使用WIFI网络与服务器进行通信。WiFi并非总是可靠的,例如,开放式网络或弱加密网络中,接入者可以监听网络流量;攻击者可能 自己设置WIFI网络钓鱼。此外,在获得root权限后,还可以在Android系统中监听网络数据。

不加密地明文传输敏感数据

最危险的是直接使用HTTP协议登录账户或交换数据。例如,攻击者在自己设置的钓鱼网络中配置DNS服务器,将软件要连接的服务器域名解析至攻击者的另一台服务器在,这台服务器就可以获得用户登录信息,或者充当客户端与原服务器的中间人,转发双方数据。

早期,国外一些著名社交网站的Android客户端的登录会话没有加密,后来出现了黑客工具FaceNiff,专门嗅探这些会话并进行劫持(它甚至支持在WEP、WPA、WPA2加密的WIFI网络上展开攻击),这是目前我所知的唯一一个公开攻击移动软件漏洞的案例。

这类问题的解决方法很显然—–对敏感数据采用基于SSL/TLS的HTTPS进行传输。

SSL通信不检查证书有效性

在SSL/TLS通信中,客户端通过数字证书判断服务器是否可信,并采用证书的公钥与服务器进行加密通信。

然而,开发人员在开发过程中为了解决ssl证书报错的问题(使用了自己生成了证书后,客户端发现证书无法与系统可信根CA形成信任链,出现了CertificateException等异常),会在客户端代码中采用信任客户端中所有证书的方式:

public static HttpClient getWapHttpClient() {

try {

KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());

trustStore.load(null, null);

SSLSocketFactory sf = new MySSLSocketFactory(trustStore);

sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);

//此处信任手机中的所有证书,包括用户安装的第三方证书

HttpParams params = new BasicHttpParams();

HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);

HttpProtocolParams.setContentCharset(params, HTTP.UTF_8);

SchemeRegistry registry = new SchemeRegistry();

registry.register(new Scheme(“http”, PlainSocketFactory.getSocketFactory(), 80));

registry.register(new Scheme(“https”, sf, 443));

ClientConnectionManager ccm = new ThreadSafeClientConnManager(params, registry);

return new DefaultHttpClient(ccm, params);

} catch (Exception e) {

return new DefaultHttpClient();

}

}

而在客户端中覆盖google默认的证书检查机制(X509TrustManager),并且在代码中无任何校验SSL证书有效性相关代码:

public class MySSLSocketFactory extends SSLSocketFactory {

SSLContext sslContext = SSLContext.getInstance(“TLS”);

public MySSLSocketFactory(KeyStore truststore) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {

super(truststore);

TrustManager tm = new X509TrustManager() {

public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {

}

//客户端并未对SSL证书的有效性进行校验,并且使用了自定义方法的方式覆盖android自带的校验方法

public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {

}

public X509Certificate[] getAcceptedIssuers() {

return null;

}

};

sslContext.init(null, new TrustManager[] { tm }, null);

}
}

如果用户手机中安装了一个恶意证书,那么就可以通过中间人攻击的方式进行窃听用户通信以及修改request或者response中的数据。

在钓鱼Wifi网络中,同样地,攻击者可以通过设置DNS服务器使客户端与指定的服务器进行通信。攻击者在服务器上部署另一个证书,在会话建立 阶段,客户端会收到这张证书,如果客户端忽略这个证书上的异常,或者接受这个证书,就会成功建立会话、开始加密通信。但攻击者拥有私钥,因此可以解密得到 客户端发来数据的明文。攻击者还可以模拟客户端,与真正的服务器联系,充当中间人做监听。

手机应用中间人攻击过程:

1 客户端在启动时,传输数据之前需要客户端与服务端之间进行一次握手,在握手过程中将确立双方加密传输数据的密码信息。

2 中间人在此过程中将客户端请求服务器的握手信息拦截后,模拟客户端请求给服务器(将自己支持的一套加密规则发送给服务器),服务器会从中选出一组加密算法 与HASH算法,并将自己的身份信息以证书的形式发回给客户端。证书里面包含了网站地址,加密公钥,以及证书的颁发机构等信息。

3 而此时中间人会拦截下服务端返回给客户端的证书信息,并替换成自己的证书信息。

4 客户端得到中间人的response后,会选择以中间人的证书进行加密数据传输。

5 中间人在得到客户端的请求数据后,以自己的证书进行解密。

6 在经过窃听或者是修改请求数据后,再模拟客户端加密请求数据传给服务端。就此完成整个中间人攻击的过程。

防护办法:

使用CA机构颁发证书的方式可行,但是如果与实际情况相结合来看的话,时间和成本太高,所以目前很少有用此办法来做。由于手机应用服务器其实是固定的,所以证书也是固定的,可以使用“证书或公钥锁定”的办法来防护证书有效性未作验证的问题。

具体实现:

1 公钥锁定

将证书公钥写入客户端apk中,https通信时检查服务端传输时证书公钥与apk中是否一致(实现X509TrustManager接口)

public final class PubKeyManager implements X509TrustManager{
private static String PUB_KEY = "30820122300d06092a864886f70d0101" + "0105000382010f003082010a0282010100b35ea8adaf4cb6db86068a836f3c85" +"5a545b1f0cc8afb19e38213bac4d55c3f2f19df6dee82ead67f70a990131b6bc" + "ac1a9116acc883862f00593199df19ce027c8eaaae8e3121f7f329219464e657" +"2cbf66e8e229eac2992dd795c4f23df0fe72b6ceef457eba0b9029619e0395b8" + "609851849dd6214589a2ceba4f7a7dcceb7ab2a6b60c27c69317bd7ab2135f50" +"c6317e5dbfb9d1e55936e4109b7b911450c746fe0d5d07165b6b23ada7700b00" + "33238c858ad179a82459c4718019c111b4ef7be53e5972e06ca68a112406da38" + "cf60d2f4fda4d1cd52f1da9fd6104d91a34455cd7b328b02525320a35253147b" + "e0b7a5bc860966dc84f10d723ce7eed5430203010001";

//锁定证书公钥在apk中

public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException

{

if (chain == null) {

throw new IllegalArgumentException("checkServerTrusted: X509Certificate array is null");

}

if (!(chain.length > 0)) {

throw new IllegalArgumentException("checkServerTrusted: X509Certificate is empty");

}

if (!(null != authType && authType.equalsIgnoreCase("RSA"))) {

throw new CertificateException("checkServerTrusted: AuthType is not RSA");

}

// Perform customary SSL/TLS checks

try {

TrustManagerFactory tmf = TrustManagerFactory.getInstance("X509");

tmf.init((KeyStore) null);

for (TrustManager trustManager : tmf.getTrustManagers()) {

((X509TrustManager) trustManager).checkServerTrusted(chain, authType);

}

} catch (Exception e) {

throw new CertificateException(e);

}

// Hack ahead: BigInteger and toString(). We know a DER encoded Public Key begins

// with 0×30 (ASN.1 SEQUENCE and CONSTRUCTED), so there is no leading 0×00 to drop.

RSAPublicKey pubkey = (RSAPublicKey) chain[0].getPublicKey();

String encoded = new BigInteger(1 /* positive */, pubkey.getEncoded()).toString(16);

// Pin it!

final boolean expected = PUB_KEY.equalsIgnoreCase(encoded);

if (!expected) {

throw new CertificateException("checkServerTrusted: Expected public key: " + PUB_KEY + ", got public key:" + encoded);

}

}

}

2 证书锁定:

为客户端颁发公钥证书存放在手机客户端中(使用keystore),在https通信时,在客户端代码中固定去取证书信息,不是从服务端中获取

关于证书或公钥锁定技术可参考下面链接:

https://www.owasp.org/index.php/Certificate_and_Public_Key_Pinning

靠文章堆起来的媒体,就是没有作品的媒体

mikel阅读(1118)

原文《自媒体人,也不过是网红》原文摘要:

“对于一个网红来说,最痛苦的事情就是:ta必须和前赴后继的网红争夺注意力。这是和一个兵员源源不断的军团作战,必败无疑。没有人可以一直撑下去。唯一的 方法是:有作品,厚重的作品。以文字内容纯媒体,要么你能拿出来足以颠覆一个领域的文章,要么,乖乖地承认,这玩意儿,不值钱。

 

正如我相当讨厌别人称我为“知名自媒体人”一样。

在我的语境里,知名博客都比知名自媒体人好。

由于要参加各种会议,需要写一个自我介绍,我从来不会写:魏武挥,知名自媒体人。

这六个字的真实意思就是:网红。

 

我混互联网十余年了,在我的视野里,网红红到发紫的,颇有一些。但红到发紫而后死的,同样不少。

网红是一个只有几年的职业——甚至几年都没有。

网红唯一不死的可能是:拿得出足够有分量的作品。

作品,这两个字很重要。对于网红来说,电影、电视剧,可以算。但得够分量。而出席什么商品发布会,唱首别人唱过的歌,跳一段自己创作的舞蹈,真心不算。

对于一个自媒体人来说,文章,算不算作品?

 

互联网的快速和即时,已经很难允许媒体做这样的文章。

还是当年那帮人,近年来,很少再能推出他们当年那种颠覆行业的文章了。

几乎可以这么说,靠文章堆起来的媒体,就是没有作品的媒体。

就是“机构化的网红”。

 

有两个蛮有名的所谓自媒体,一个我称之为W,一个我称之为L。

L最近经常有人控诉它盗版,在我的视野里,已经发生三起了。

控诉成立与否,不是我想讨论的。

我琢磨的事情是,为什么会发生这样的事呢?

是不是被掏干了以至于会用到别人的内容还有些吃相难看了?

 

芙蓉姐姐想成为…唔,姑且用个赵薇吧,难。

赵薇想做芙蓉姐姐,那是….高维打低维,简直手到擒来。

其实,赵薇就是有作品的红人。

单纯的网红,是没有积累的,没有沉淀的。

靠不长不短的文章,靠耸人听闻的标题,这样的纯内容媒体,和网红没啥差别。

能做到的,无非就是一时里朋友圈刷刷人屏。

然后,

然后,就没有了。

 

一篇文章?

呵呵,在互联网上的寿命,夸张点说,只有几分钟而已。

反正不会超过三天。

在这个市场上,有太多的机构媒体,已露“网红”之像。

自媒体,就越发了。

别人我不方便说,说自己总可以:比如我。

不要做网红。

在得意洋洋于十万加的时候,其实,网红的命运,是那么凄然。

无论是个人,还是机构。

观点:

作者讨厌网红,可又不得不迎合读者的口味,这就是互联网媒体的尴尬,一篇文章几分钟之内就过期了,可见又有多少做媒体的人的心酸在里面,的确没有自己鲜明内容和价值的自媒体只能沦为一时的眼球效应,过后就尘归尘土归土了,再也难以为继,其实就看每个人怎么看自媒体了,我觉得是不是自媒体都说不上就那么稀里糊涂随大流儿写起文章做上自媒体的大有人在,这不能说是赶潮流,应该说是人的本身需要,也就是找存在感而已,上升成自媒体的高度未免有点儿过了。

对于那些真正媒体圈的人来说,我这种根本就不能算,就是自己在那写日记而已,所以说别把写日记的上升到自媒体高度,那太抬举我们这种刷存在感的人了,什么网红不网红的跟我们这种没关系,就是写着玩儿图个乐呵,至于作者说的作品,那就更谈不上了,你做个网站也是作品,拍段视频也是作品,要是能称之为媒体作品的,还真不多,何必纠结这些,自己喜欢就写,不喜欢就干点儿别的,自媒体本来是自由的东西,非要给上纲上线,总觉得别扭。

[转载]SWFUpload插件+FTP文件上传,我这么玩 - Ar.Issac - 博客园

mikel阅读(965)

[转载]SWFUpload插件+FTP文件上传,我这么玩 – Ar.Issac – 博客园.

效果图:

虽然之前接触过swfupload这个上传插件,但是之前做的样子是这样的
实战项目做的这么丑爆了我估计老大的内心是会崩溃的,所以特地在网上找了美观一点的样式,原帖地址:http://www.xiariboke.com/article/200.html
原帖后台是基于php写的插件,虽然各位看官也许没学过php但是也应该见过php跑,后台改成C#代码就可以了。
前台页面是一样的,在引入一堆js文件之后,改动一下对js文件的引用路径即可,比如这样:
由于原版直接使用页面会有乱码
我也懒得测试是哪个js文件的编码不支持中文所以对js文件一律加上了 chartset=”gb2312″的属性,显示问题解决。
来看看js中的配置,改动如下:
upload_url:请求上传的地址。请求自己写的aspx或者ashx或者MVC的controller
file_size_limit:上传文件限制大小。当然这只是插件初步对文件大小的检测,为防止大请求报文攻击,iis已经默认对文件上传做了限制,具体的数据我忘记了,测试是在5M以内,因此并不是在这里写多少就能上传多少,具体实现修改文件上传大小见下文
file_types:上传文件类型的检查。
下面的file_upload_limit也可以自行配置,用于配置上传文件个数
private FtpStatusCode FtpUpload(string sFileDstPath, string FolderName, string ftpServerIP, string ftpUserName, string ftpPwd)
{
try
{
FileInfo fileInf = new FileInfo(sFileDstPath);
FtpWebRequest reqFTP;
FtpWebResponse uploadResponse = null;
reqFTP = (FtpWebRequest)FtpWebRequest.Create(new Uri(ftpServerIP + "/" + FolderName + "/" + fileInf.Name));
reqFTP.Credentials = new NetworkCredential(ftpUserName, ftpPwd);
reqFTP.KeepAlive = false;
reqFTP.Method = WebRequestMethods.Ftp.UploadFile;
reqFTP.UseBinary = true;
reqFTP.ContentLength = fileInf.Length;
int buffLength = 2048;
byte[] buff = new byte[buffLength];
int contentLen;
using (FileStream fs = fileInf.OpenRead())
{
using (Stream strm = reqFTP.GetRequestStream())
{
contentLen = fs.Read(buff, 0, buffLength);
while (contentLen != 0)
{
strm.Write(buff, 0, contentLen);
contentLen = fs.Read(buff, 0, buffLength);
}
}
}
uploadResponse = (FtpWebResponse)reqFTP.GetResponse();
return uploadResponse.StatusCode;
}
catch (Exception e)
{
return FtpStatusCode.Undefined;
throw new Exception("Ftp错误", e);
}
}

后台代码,根据项目加入了上传FTP及插入数据库:

ok,基本的配置就已经完成,但是由于前文说的iis对上传做了限制,所以我们还需要对此进行解除操作。
1.在Web.config文件中添加httpRuntime节点
<httpRuntime targetFramework=”4.5″ maxRequestLength=”1073741824″ executionTimeout=”3600″ appRequestQueueLimit=”100″ />
maxRequestLength决定了你要上传文件的最大长度,单位是kb,我随便写了个很大的数。后面两个一个是超时时间和最大请求队列,根据情况自定义设置。
2.部署项目后,在iis管理器里
请求筛选-编辑功能设置
最大内容长度里面填写在Web.config里面设置的最大长度
以上,所谓的大文件上传功能就基本实现了,你以为完事了?最好玩的才开始。
举个例子,实际项目在上传我们可能要显示上传失败的原因,比如上面的数据库错误,FTP错误等其它问题,因此要修改SWFupload的handler中的js事件,js配置部分:
我修改了file_dialog_complete_handler事件,改成了自己写的fileDialogComplete1,比如要在上传前添加些必填信息才可以执行上传,在【选择上传文件弹窗关闭事件】之后确定验证信息是否填写完毕再执行上传。
function fileDialogComplete1(numFilesSelected, numFilesQueued) {
try {
var info = $('#info').val();
if (numFilesQueued &gt; 0) {
if (info == "") {
alert("信息不能为空");
swfu.cancelUpload();
return;
}
//在上传文件中加入自定义参数,用于后台接收处理
var postParams = {
'info': info
};
swfu.setPostParams(postParams);
if (numFilesSelected &gt; 0) {
document.getElementById(this.customSettings.cancelButtonId).disabled = false;
}
swfu.startUpload();
}
} catch (ex) {
this.debug(ex);
}
}

实际上SWFupload可以玩的事件有很多,在handler.js文件中可以看到,如果需要控制某个事件可以直接修改,具体可见这篇文章:

SWFUpload 2.5.0版 官方说明文档 http://www.cnblogs.com/youring2/archive/2012/07/13/2590010.html

[转载]百度分享如何自定义分享url和内容? - kwishly - 博客园

mikel阅读(806)

[转载]百度分享如何自定义分享url和内容? – kwishly – 博客园.

百度分享默认分享的是当前页的url,但也可以在同一个页面中分享多个不同的url,仅需进行如下简单的配置。

默认的代码如下:

<div id=”bdshare” class=”bdshare_t bds_tools get-codes-bdshare”>

其实是有很多隐藏的参数的:

<div id=”bdshare” class=”bdshare_t bds_tools_32 get-codes-bdshare” data=”{
‘bdDes’:’您的自定义分享摘要’,  //’请参考自定义分享摘要’
‘text’:’您的自定义分享内容’,  //’请参考自定义分享内容’
‘title’:’您的自定义pop窗口标题’, //’请参考自定义pop窗口标题’
‘pic’:’您的自定义分享出去的图片’, //’请参考自定义分享出去的图片’
‘bdComment’:’您的自定义分享评论’, //’请参考自定义分享评论’
‘url’:’您的自定义分享url’,  //’请参考自定义分享url’,
‘wbuid’:’您的自定义微博 ID’
}”>

如果想自定义url,只需要写如下代码即可,红色文字部分就是增加的url:

<!– Baidu Button BEGIN –>
<div id=”bdshare” class=”bdshare_t bds_tools_32 get-codes-bdshare” data=”{‘url’:’http://www.weste.net/index.html’}“>
<span class=”bds_more”>更多</span>
<a class=”bds_tsina”>新浪</a>
<a class=”bds_tqq”>腾讯</a>
<a class=”bds_renren”>人人</a>
<a class=”bds_qzone”>QQ </a>
</div>
<script type=”text/JavaScript” id=”bdshare_js” data=”type=tools” ></script>
<script type=”text/JavaScript” id=”bdshell_js”></script>
<script type=”text/javascript”>
document.getElementById(“bdshell_js”).src = “http://bdimg.share.baidu.com/static/js/shell_v2.js?cdnversion=” + Math.ceil(new Date()/3600000);
</script>
<!– Baidu Button END –>

参考资料:http://share.baidu.com/help/customization/config

互联网赚钱不需要咋呼

mikel阅读(1074)

互联网上真正能赚钱的都在闷头做事,很少在那咋呼,因为都知道咋呼半天只不过是在浪费赚钱的时间,为什么这么说,因为只有互联网上不赚钱的人才有时间在那瞎逼逼,真正赚到钱的或者操作项目的都在忙着操作项目,千万别信那些所谓的自动赚钱系统,那都是扯淡的,互联网和传统行业一样一份付出一份收获。

很多人都在问怎么快速赚钱,其实完全没有快速赚钱的方法,一天赚个几块算快速吗?可是需要付出的辛苦的劳动,比如打码、游戏之类的体验任务都是这种方式,如果说能够赚更多的最快也就是最直接的就是推广,比如淘宝客、陆金所这种拿推广佣金的,不过同样是要付出辛苦努力推广的,发帖或者QQ群发之类的推广方式需要的不是什么技术而是持之以恒的毅力。

如果说能够有一夜暴富的项目,那都是些见不得光的,赚钱应该有底线,就像做人要有原则一样,很多人问我为什么不做某某项目,其实就是原则问题,可能很多人说我矫情,有钱还不赚。我只想说不以恶少而为之,出来混迟早要还的,且行且珍惜。

互联网上可以操作的赚钱项目很多,同样都有个项目的生命周期,从开始得发展期到中期的暴利期,然后到最后的衰退期和消亡期,都有个高潮低谷到消失,外在和人为的因素很多,很多人唯利是图明明很好的项目最后被生生作死的比比皆是,人心不古蛇吞象,有人的地方就有不安定的因素,因为人性是最难参透的东西,所以说想要在互联网上赚钱首先要有适应这种变化的心态,没有永远不消失的项目,也没有不赚钱的项目,其实就是这么矛盾的存在才让人欲罢不能的迷恋操作项目的过程,就像冲浪一样总是有那么多的变化一波波来袭,不适应则会被淘汰,永远不要指望操作一个永远能赚钱的互联网项目到老,所以说生于忧患死于安乐,越是赚钱的时候就越要做好没钱赚的准备,那就是果断寻找新的项目,不要盯死在赚钱的项目上。

[转载]五分钟 知晓Eclipse不为人知的秘密(越熟悉 越陌生) - RunforLove - 博客园

mikel阅读(869)

[转载]五分钟 知晓Eclipse不为人知的秘密(越熟悉 越陌生) – RunforLove – 博客园.

  大家好,这篇博客的目的是总结一下Eclipse这个软件中一些不为常用的功能。与大家分享。谢谢~

1.利用one hour看了一下Eclipse的使用,用two hour写了这篇blog。

2.在现实项目中,活学活用,才会真正对你有利,否则你浪费时间看了本博客,对你毫无帮助。

本博客结构:目录 + 按目录分述

目录:

1.给Eclipse添加书签。

2.通过Attach File查看源码和系统函数。也可以通过open Type;Open Type Hierarchy;Open resources查所有类

3.Font size修改字体;Theme修改Eclipse IDE主题。

4.Content Assistant,添加熟悉的代码提示功能(与其它快捷键冲突);常用 “Alt+/”

5.Quick Fix功能+F2代码提示功能。

6.重启功能,就像microsoft onenote的自动保存功能。

7.设置web browser,通过Debug模式+Firebug模式,前后台调试。

8.Eclipse中常时用的快捷键。+win D + win E。

9.show line number。

10.market place,安装集成插件的功能。

11.new window;new console功能。管理工作区;切换工作区。

12.Eclipse管理任务;悬浮提示;关闭项目的重要性。

13.run 配置参数。写小程序要输入参数的。

14.open perspective的功能。

15.学会使用problems视图,

16.一个文件夹视图,与两个文件夹视图的切换。

17.管理workspace工作区。切换工作区。

18.Java Build Path设置Java构建路径。

 

正文:

1.使用书签作为提示信息,或者使用书签可以快速定位到文件中的指定的行。如果你想设置书签,你只需要在代码区右侧竖线上右击鼠标并选择能 “Add Bookmark” 即可。。

 

如上图所示,是操作方法及操作效果图。同时在show view视图中也有个Bookmarks视图。可以查看所有书签。删除时,还要定位到添加的那个界面,有一个选项是Remove Tasks,即删除了该书签。

2.Navigate, 在英文中含义是驾驶 航行的意思。可见其重要性。在菜单栏的这个菜单中,有几个很重要的选项。Last Edit Location;Back .java;此外,还有Open Type;浏览(Navigate)菜单提供了多个菜单可以让你快速定位到指定资源。Open Type, Open Type in Hierarchy 和 Open Resource 三个菜单项是非常有用的。

如上图所示,Open Type 菜单项可以打开一个对话框,对话框中可以查找 Java 类型文件。你可以在输入框中输入类名查找。 ‘*’ 号表示 0 个或多个字母,’?’ 号表示单个字母可用于指定模式。对话框中将显示所有匹配的模式。(包括JDK中的类)第二个图片中,你可以点击 Attach Source 按钮来查看类文件对应的源码。源代码位于 Java 主目录中的 src.zip 压缩文件中。

如上图所示,Open Type in Hierarchy 菜单允许用户在 Type Hierarchy 视图中查看类的继承层次。Type Hierarchy视图中选择指定的类就可以看到类的定义信息,包含对应的属性和方法,当然还有它的继承的层次结构。

3.

中国特色的万亿级社区O2O 身边的买卖最赚钱?!

mikel阅读(1230)

原文《中国特色的万亿级社区O2O,下一个“阿里巴巴”在哪里?》 ,摘要:

现在,中国许多城市的小区正上演着如下一幕幕场景:

一大早,“阿姨帮”上叫的家政服务人员已经来敲门,做早餐、搞清洁、然后送孩子上学。业主吃完早餐,用滴滴叫了一部专车去上班。

中午,留守的太太不想做饭,通过“百度外卖”叫了一份热气腾腾的快餐。衣服窗帘的洗涤由“e袋洗”上门来收,家具电器的维修保养通过彩生活的“易维修”上门服务。

黄昏,“链农”刚送到新鲜便宜的蔬菜水果,在“爱大厨”上叫的厨师就上门了,一顿晚餐让全家人都很满意。

晚上,孩子在“58到家”上叫的钢琴老师指导下练琴,享受“河狸家”上叫的美甲服务后,两口子在“美团猫眼”上预定了电影和座位,出门看电影去了。

这样的家庭生活场景,就是现在正在大爆发的“社区O2O”,未来可能超过淘宝的万亿级市场。

  1、社区O2O的商业模式本质是B2F(Business to Family)

这样的服务模式,在中国正红得发紫。既符合硅谷式称谓“O2O”特征,也符合李克强总理政府工作报告中的“互联网+”战略。

“O2O”是 “online to offline” 和 “offline to online” 的缩写,强调的是线上和线下的融合和联动;“互联网+”指移动互联网、云计算、大数据、物联网等与传统行业相结合,雷军认为代表着一种新的经济形态,于扬 (易观国际董事长)甚至认为中国的互联网+战略有点像美国信息高速公路战略的升级版。

社区O2O是通过移动互联网的革命性技术和新的商业模式,将家庭与商业服务之间做一个“连接”。

谁能成为这个“连接器”(正如马化腾对微信的定位),谁就将执万亿社区O2O市场的牛耳。现在看,也许“地派”即掌控物业的打法希望貌似比较大。

这个特殊性,就是物业在中国城市社区中的特殊定位和关键地位。正因为如此,这个连接,是足够重度的,也是黏性极高的。毕竟用户在家和小区的时间,加起来远远超过在外(公司、旅途)。

其次,与传统互联网企业的虚拟性迥异,基于线下的本地生活服务有许多的独特场景和远胜线上的真实交互、交易,对于严重依赖信任、真实的互联网金 融而言,线下可能是更重要的基础优势,未来是否在中国会出现富国银行、LendingClub 的社区金融?我认为,真实、快捷的线下服务入口是众多互联网金融企业未来争夺的新热点。

第三,线下服务平台的开放能力更胜于线上,因而商业生态体系可能更健康。例如,在彩生活服务的众多社区中,母公司花样年集团的物业只占3%不 到;一旦能形成更为开放的涵盖 Content、Community、Commerce的综合商业生态,可能会较基于支付宝的阿里生态系统对上下游企业更为友好,且面向真实家庭的大数据 数据积累的更为持续、优质和有价值。

觊觎社区O2O的巨头,其实有很多。譬如,中国房地产行业龙头万科在2014年首次正式提出“轻资产”运营模式,并放话要致力于掘金社区服务市场。

这个战争才刚刚开始。

反方的文章《万亿社区O2O? 别搞笑了 做了都不知道怎么死的》 摘要:

“这两天有企业背后鼓吹社区O2O是万亿市场,这纯是为吸引眼球忽悠不懂行的。这个万亿市场是这么算出来的?社区O2O包括哪些服务?怎么赚钱?谁能赚到?很多问题都没弄清楚,就鼓吹社区O2O是万亿市场,照这么下去这个概念迟早要被吹破的。

一群脑袋大的投资人一看社区O2O概念热就一股脑的投了进去,进去之后才发现现实没有想象中的那么顺利美好,各种问题开始爆发了,最具代表性的就是叮咚小区,现在还活不活着都不清楚了。类似的投资案例还有很多,细心的话你可能发现了,社区O2O的天使轮融资的消息比较多,但获得A轮B轮的却鲜有听闻,为何会这样?

更 有意思的是社区O2O的概念够热,吸引来一大群做生鲜电商的,开便利店的,送快递的,上门美甲、按摩、洗衣的等等也开始举起社区O2O的大旗“招摇撞 骗”。究竟什么是社区O2O,似乎越来越难搞清楚了,貌似与居民生活相关的都可以与社区O2O贴上边,要是这么说的话,社区O2O何止万亿,十万亿也不止 呀~

热吵社区O2O的主要分四派:其一,是具备互联网电商背景的公司,包括京东、顺丰、拉卡拉,以及房产业务的易居等;其二,是房产和物业公司,万科、龙湖,以及主打社区O2O的彩生活;其三,拿到融资的创业团队,包括叮咚小区、小区管家等;其四,就是蹭概念的上门服务类的互联网公司了。

如此丰富的参与者构成对社区O2O的发展本是好事,但现在却有些过犹不及了,参与者越多,社区O2O的概念就越模糊,可能多数人已经无法对社区O2O究竟是什么达成统一标准了。不同的参与者会根据自身的厉害关系给出有利自身的市场定义。

再 这么肆无忌惮的发展下去,用不了多久舆论对社区O2O的反思浪潮就会兴起,到时候这帮靠概念而生的社区O2O企业多数都会被淹死了。现在乐呵的热吵社区 O2O概念忽悠着投资,最后概念泡沫破灭创始人们拍拍屁股走人了,留下投资人在那傻眼。所以,奉劝做社区O2O投资的,眼光要放亮点,再遇个叮咚小区之类 的毁不毁啊?泡沫之下尽是炮灰。

知道目前社区O2O最大的问题是什么吗?答案:线上线下严重脱节。

以互联网形式做社区O2O的无法掌握线下大量的物业服务资源,因为 互联网公司不适合养那么多人,而且也没办法获得那么多物业资源。物业公司本来就比较零散,数量众多,而且每个楼盘的情况还不一样,首先整合物业资源就不太 现实。另外,很多楼盘与物业并不是“终生”制的,物业若服务不好,业委会有权利要求更换物业,所以这些物业哪有那个闲功夫去为互联网公司提供免费服务啊?

你跟物业谈未来如何美好,但物业跟你谈现在是否有钱赚,有钱赚还能合作,没钱赚的话多一事不如少一事,毕竟谁知道两三年后这家物业公司还能不能服务这个楼盘,他们更着眼于当下的利益。互联网公司想说服物业提供帮助的方法只有一个,要么直接给他们钱,要么间接帮他们获利。

观点:

人以群分,同样人多的地方就是社区,很多人的生活基本都在周围的一亩三分地儿,没有多少人上蹿下跳的四处奔波,中国又是一个比较喜欢聚堆儿的民情,所以说身边的买卖最好赚钱。

阿里巴巴将传统商业的信息差将到最低,倒逼传统企业压缩成本和各级代理商撕逼大战,结果淘宝成了最大的赢家啊,苦了卖家所以才有了淘宝刷单和各种所谓的“网货”的出现,最终也苦了买家,好了不说阿里的问题了,就说阿里的确是改变了人们的消费习惯,这年头没有支付宝就不好意思跟人打招呼,没网上买过东西那跟原始人差不多了,从支付习惯的养成到网上购物的生活习惯开始,天下就没有了难做的买卖了。

微信再次推波助澜得将移动消费的场景推到老百姓的手掌上,连小孩和老人基本上都有了微信号,并且通过发红包绑定了多少银行卡,让全民移动消费习惯养成了,社区O2O的天时地利基本上都被接受了,就看谁能够做起身边的买卖了,毕竟这个市场很大,不仅仅是交易这么简单,包括物业、本地服务、社交等等实在是很大的市场,没有哪个巨头能够保证自己能够一口吃下,所以才会给了大家一个讲故事的好机会,各种热钱会陆续投入进来,不过对于中国互联网这个“剩者为王”的竞争环境,那要多久能够“剩”出个王来,就看BAT等等巨头的动作了,目前最有希望的也最看好的还是微信,毕竟先入为主,人群覆盖率在那了,有了用户基础再有了好的社区O2O模式将是一个很有希望的未来。

反面的观点是大家都不知道社区O2O怎么做,就在那瞎逼逼只能是空中楼阁,别说万亿估计不对,连现在的舆论都是在瞎说,线上的不懂线下的规则运营,互联网不懂物业的想法,物业怕丢了饭碗眼前利益为大,接入互联网闲麻烦,然后就是各个社区的住户不买账,导致现在的社区O2O一直是个无主之地的大市场,就是没有几个能够存活得滋润的,其实没有问题的市场是没有未来可言的,我还是觉得大家都被局限在“社区”这两个代表地域的字眼儿上了,其实应该细化到“住户”的这个“社区”的重要分子上,毕竟没有人也就没有社区,至于物业不能不管,但是你只要取悦“住户”的需求,让他们满意的使用你的产品,就不用发愁物业不被 挟天子以令诸侯,让住户倒逼物业等线下社区相关服务商家变化,不痛则不通!想要真正革社区O2O的命,还是要从人下手!

[转载]Android锁定屏幕 - Wossoneri - 博客园

mikel阅读(1192)

[转载][Android]锁定屏幕 – Wossoneri – 博客园.

最近玩的比较欢脱,欠了好多东西没写。今天先简单地补一篇简单的内容。就是最近涉及到一个锁定Android设备屏幕的设计,大概就是通过一个 按钮或者服务消息,来控制设备界面完全锁定,不能点击任何东西。感觉上这个需求可能会涉及很多东西,然而做起来发现并没有。。。内容很简单,跟着代码快速 过一遍。

本文地址:http://www.cnblogs.com/rossoneri/p/4409691.html 

主界面就放一个按钮来触发事件好了:

 

按下按钮后显示另一个锁屏界面,同时加上一个解锁按钮(如果不加的话怎么退出程序啊- -!)

可以看到,启动锁屏后 顶部的status bar完全消失,下方的Navigation bar按钮淡化成小圆点。这时你点击屏幕任何地方程序都不会有任何反应。好了,下面上代码。

 

锁屏类 LockScreen.java:

package com.example.wow.demo_lockscreen;

import android.content.Context;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.Button;

/**
* Created by wow on 15-4-9.
*/
public class LockScreen {

Point mLpSize;
Button mBtnUnlock;
ViewGroup mView;
final WindowManager mWindowManager;
final WindowManager.LayoutParams mLp;

public LockScreen(Context mContext){

mView = (ViewGroup) LayoutInflater.from(mContext).inflate(R.layout.lock_screen_view, null);
mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
mLp = new WindowManager.LayoutParams();
mBtnUnlock = (Button)mView.findViewById(R.id.btn_unlock);

mBtnUnlock.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
show(false);
}
});

initLp();

}

private void initLp(){
mLp.type = WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;// TYPE_SYSTEM_ALERT;
mLp.format = PixelFormat.RGBA_8888;

mLp.flags |= WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
| WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
| WindowManager.LayoutParams.FLAG_FULLSCREEN
| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;

mLp.systemUiVisibility = View.STATUS_BAR_HIDDEN;

mLp.width = WindowManager.LayoutParams.MATCH_PARENT;
mLp.height = WindowManager.LayoutParams.MATCH_PARENT;

}

public void show(boolean flag){
if (flag){
mWindowManager.addView(mView, mLp);
} else {
mWindowManager.removeView(mView);
}
}

}

这里的重点在于

mLp.type = WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;

这一句,type一定要设置成这种类型,这个类型的功能就是让当前显示的view无理由地出现在最上端。 flags 就是根据需求来设定,可以再加其他内容,也可以删掉一些,不影响的。

这里还要在Manifest中添加权限才行

 

MainActivity:

package com.example.wow.demo_lockscreen;

import android.app.Activity;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;

public class MainActivity extends Activity {

Button mBtnLock;
LockScreen mLockScreen;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

mBtnLock = (Button)findViewById(R.id.btn_lock);
mLockScreen = new LockScreen(this);

mBtnLock.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mLockScreen.show(true);
}
});

}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();

//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}

return super.onOptionsItemSelected(item);
}
}

 界面文件就不贴了,太简单。。哦对,这个我在5.0上测的是完美运行,4.2有点问题,不太清楚为什么。。有知道的给我说下。

好了,锁屏其实就这么简单。后面我会再研究一些界面效果,做点实用的锁屏功能。

最近在研究 system bar,挺简单的,之前写了几篇相关的博客,发现之前理解还是有点偏差,打算过一段写个最终版的,把这块知识搞定。

本文版权归Wossoneri博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接,否则追究法律责任。

[转载]第三方支付集成 - 邹琼俊 - 博客园

mikel阅读(1794)

[转载]第三方支付集成 – 邹琼俊 – 博客园.

    看一个典型的在线支付流程。第三方支付平台就是提供网上支付的平台,由第三方支付平台来和各个银行进行对接,商户只要和第三方支付平台对接即可,降低了商户的技术难度和接入门槛。常见的第三方支付平台:支付宝、网银在线、快钱、财付通、易宝等。

一个支付流程的数据流动:客户在网上商店挑选商品、点击支付,网站将用户重定向到第三方支付平台的支付网关,并且将订单号、金额等信息通过 QueryString传递给支付网关,用户在第三方支付平台支付成功后,第三方支付平台会自动访问商户的确认页面,将支付成功的订单号等信息通过 QueryString传递给确认页面,这样商户网站就能得到支付成功的通知了。以服装卖场中的收银台流程类比(漏洞:自己偷偷盖假章,防范办法:收银台 和商户约定一个密钥“天灵灵”,然后收银台在小票上根据“小票编号”+“金额”+密钥计算出md5写到小票上)。

todo:模拟器增加1 3 5分钟重复通知商户支付成功。重复通知的话是支付宝向商户网站发请求,这时候支付宝服务器是客户端。商户号不存在的问题解决
Response.redirect过程商户网站、浏览器、支付宝的请求流程。注意Transfer和Redirect的区别。retun.aspx页面的参数是支付宝传递过来的。
虚拟商品(积分、充值卡)就直接增加积分、充值了。物理商品(鲜花、图书)就把订单标记为已支付,然后网站工作人员处理订单、发货,再把订单状态标记为“已发货”(业务员手动完成),最后用户确认收货。

第三方支付平台都提供了集成文档和示例代码,拿过来改改就行,当然如果你有兴趣的话,可以看下这些示例代码。至于具体的调用和实现,我就没必要讲了,因为支付宝有现成的Demo和灰常详细的api文档,我们要思考的是如何和订单系统结合。

网店不是直接向支付宝服务器发请求,而是让用户重定向到支付宝服务器去支付。就像服装店让客户自己拿着小票去交钱一样。

每个商家的密钥都和其他商家不一样,避免了“整个服装城用一个hahah”的漏洞。要把订单编号也加入md5的运算,防止用“001的返回小票领002号订单的商品的漏洞”。商户端的MD5算法要和支付宝一样,否则会报错,这个MD5算法是支付宝开发包中提供的。

开发包和密钥的获取,我们可以访问支付宝官网技术支持页面:https://b.alipay.com/order/techService.htm

下载相应的接口包,里面有各种语言的实例代码和详细的API文档。

注意事项:

1、  订单号是商家确定的。

2、  参数顺序无所谓。

3、  支付宝就是“收银台”,很形象,支付宝就是负责收银,商家引导客户去收银台。收费单据就是返回的值。

4、  不要直接访问模拟器主页,模拟器主页就是相当于支付宝的主页

模拟器整合方法

1、解压PaySiteSimulator.zip,如下:

运行Casini,把PaySiteSimulator当成网站运行,8080端口。这个相当于搭建支付宝模拟器网站,这个网站不需要咱们开发。

访问模拟器的首页:“支付宝网站后台(支付宝工作人员用)”是用来分配商户编号的,支付宝商户后台是供商户修改密钥的。

点击Start按钮,然后访问模拟器主页

2、修改C:\WINDOWS\system32\drivers\etc\hosts,把www.zhifubao.com映射到127.0.0.1

3、按照AliPay/支付宝接口集成文档.进行集成测试。注意:那个支付宝模拟器是模拟支付宝的网站,不是商户的程序。

博客地址: http://www.cnblogs.com/jiekzou/
博客版权: 本文以学习、研究和分享为主,欢迎转载,但必须在文章页面明显位置给出原文连接。
如果文中有不妥或者错误的地方还望高手的你指出,以免误人子弟。如果觉得本文对你有所帮助不如【推荐】一下!如果你有更好的建议,不如留言一起讨论,共同进步!
再次感谢您耐心的读完本篇文章。

[转载]Linux应用环境实战26:探索Linux系统的启动过程 - 京山游侠 - 博客园

mikel阅读(850)

[转载]Linux应用环境实战26:探索Linux系统的启动过程 – 京山游侠 – 博客园.

这篇文章有点长,我花了大约两个星期的时间才完成,虽然不是写得巨细无遗,但是我认为还是很有价值的,求推荐。不管你是不是 Linux 爱好者,也不管你是否能耐心读完,请果断 mark 之,你值得拥有。

引言

这一篇可能是我这系列随笔除总结之外的最后一篇,也可能不是。为什么这么说呢?因为我觉得在 Linux 应用环境方面再写下去也可能不会有什么新意了,至少以我的水平写不出什么新意了。我的这一个系列,基本上不涉及到编程,而是真真正正地将 Linux 桌面系统当成日常使用的工具,即使在我的这个系列中有探讨 C 语言或者 Bash 语言的部分,我也是展示的相关的开发工具,比如 binutils 工具包和 autotools 工具链。我曾经说过,折腾 Linux 就是折腾 Linux 系统下的软件,Linux 世界软件之多,可写的应该还有不少,但是我觉得已经没有办法写得很好了。比如有一些工作我还没有找到适当的工具,像 JavaScript 和 CSS 开发,对于博客园中的广大前端攻城狮来说,肯定是各有神通,但是我确实不知道用什么工具可以做到即顺手又高效率,我只会简单地用用 Firefox 提供的开发者工具。还有 3D 开发、游戏编程、移动开发等等,虽然可以写的领域比较广阔,但是毕竟我不是做这些的,没什么经验,不能说光写某某软件试用体验吧。再说,程序员终归还是程 序员,以后写博客应该还是介绍编程经验比较好。

之所以想到写这些东西,那是因为我确实想让大家也和我一样,把 Linux 桌面系统打造成真真正正日常使用的工具,而不是安装之后试用几把再删掉。我是真的在日常生活和工作中都使用 Linux,比如在 Linux 下编程、写博客、写论文和做幻灯。当然,对于 LibreOffice 这样的软件使用起来都不会有什么困难,所以在我的博客中就基本没有提到,就像 Windows 下的程序员不会去写 MS Office 的使用指南一样。如果有人不能坚持使用 Linux,那一定是 Linux 中的某些困难打败了他。刚使用 Linux 时确实会碰到很多困难,比如界面不够美观啊、字体不够顺眼啊、输入法太难用啊,还有就是想做某个工作找不到工具、想改一个属性找不到入口、系统崩溃了无法 恢复等等,甚至是刚安装一个软件包或者下载一个文件,却不知道放到文件系统的什么地方了。所以,我的这一个系列就是在向大家展示我的方法论,用什么软件不 重要,怎么用好也不重要,而是碰到问题了,用什么思路去找答案,到哪里去找答案。经过这二十几篇的展示,我的方法论也基本上讲完了。所以我决定,写完这一 篇探索 Linux 系统的启动过程之后,我将开始另外一个系列的工作。

关于 Linux 系统的启动过程也是几经变革。首先是操作系统从磁盘载入的过程发生了变化,很老旧的操作系统需要自己写启动扇区,现在的操作系统早就将这个过程交给了专门 的启动工具。最常用的操作系统加载程序是 Grub,这个名字大家肯定早就如雷灌耳了,而且 Grub 也已经从第1版发展到了第2版,我们称之为 Grub2。 Grub2 和 Grub1 有很大的差别,网络上有很多讲 Linux 的文章动不动就拿 Grub1 说事儿,比如让大家修改 menu.lst 文件这样的,都是很过时的内容了。关于 Grub2 的详细资料可以阅读文档info grub或者info grub2。Grub 的优点是支持多操作系统的启动,Windows、Linux、BSD都不在话下,但是它们的启动协议是不一样的,启动 Windows 靠的是 chainloader,启动 Linux 直接使用 linux 命令,而 BSD 系统好像是使用的 multiboot,特别是 Grub 的 multiboot 规范对那些很想自己写操作系统的程序员很有用,因为再也不用从启动扇区的代码开始写了,可以直接进入保护模式用 C 语言写操作系统。Linux 系统在启动的时候还有 initrd 或 initramfs 机制,这个也需要 Linux 用户特别注意。initrd 或 initramfs 也经历过一次技术革新,最开始采用的是 RamDisk 技术,目前采用的 tmpfs 技术。最后,内核加载入内存后,会将系统初始化的控制权交给 init 程序,而 init 程序也不是一成不变的,最开始使用的是 SysVinit,后来又改成 Upstart,现在最流行的又变成了 systemd。现在仍然有很多书和网文讲的还是 SysVinit 时代的做法,当然这些文章只能以 RedHat EL 5 以及之前的发行版作为示例,而对于最新的发行版,这些文章中讲的方法又会给新入门的用户造成困难。

探索 Linux 系统的启动过程是每一个 Linux 用户进步到一定程度后必然要去做的一件事。有的人可能只是好奇,也有的人可能确实需要从启动过程中去解决某一个特定的问题。这种探索我进行过多次,也早就 想写一写,但是总是写不好。这一次是借着 systemd 普及的东风,让这个探索过程成为我这一系列的压轴之作。为什么说是借着 systemd 普及的东风呢?因为听说本月即将发布的 Ubuntu 15.04 版本将会从 upstart 更换为 systemd,目前正在紧张地测试中。当然,一下子迁移过来对开发人员来讲还是有点难度的,但是只要确定目标,即使 Ubuntu 15.04 不使用 systemd,到 15.10 也肯定是它了。因此,对于我这个喜欢折腾多个 Linux 发行版的人来说,就不用多线作战了,只需要学好 systemd 即可。

Linux启动过程概述

这里先简单列一下 Linux 操作系统启动的全过程:

  1. 按下电脑的电源键后,电脑通电,BIOS启动;
  2. BIOS读取硬盘的MBR,运行启动扇区中的代码,旧系统往往需要自己写启动扇区,而新系统基本上由专用的启动软件接管了,在 Linux 世界中,目前都是用的 Grub2。由于启动扇区空间太小,放不下太复杂的代码逻辑,所以 Grub2 也使用了多阶段启动的策略;
  3. Grub2 负责将操作系统内核加载到内存,如果有必要,也会把 initramfs 文件加载到内存,然后将控制权交给内核;
  4. 内核进行初始化,内核的初始化过程结束后,就会把控制权交给/init程序,从此进入用户空间;
  5. 因为内核先是将 initramfs 文件挂在为根文件系统,所以刚开始运行的/init程序其实是 initramfs 文件中的,所以该文件需要的重要的初始化脚本、内核模块、配置文件等,都位于 initramfs 文件中,这也是为什么很多时候我们修改了某些配置文件后,需要先更新 initramfs 文件再重启操作系统才会生效;
  6. initramfs 文件中的/init程序负责挂载硬盘上的文件系统,然后再把根文件系统切换到硬盘上的根分区,再运行/sbin/init程序,这时所有程序、配置文件、脚本都是使用的硬盘上的了,当然,网络文件系统也是同理。可以看出 init 程序的运行也是一个分阶段的过程;
  7. /sbin/init程序负责系统的初始化、各种服务的运行、用户的登陆等等;
  8. 如果需要运行图形界面,则/sbin/init程序会运行 Display Manager,在 Fedora 中是 gdm,在 Ubuntu 中是 lightdm。然后 Display Manager 负责启动整个图形界面。

Grub加载Linux内核和initramfs

研究 Linux 启动过程的第一站就是要研究 Grub。大部分时候我们不用考虑 Grub 的安装,因为安装 Linux 桌面系统的时候已经帮我们自动安装好了,我们要做的,最多就是改改 Grub 的配置文件。只有在一种情况下需要手动安装 Grub,那就是在系统崩溃后急救的时候,当然,需要先运行一个光盘版或 U 盘版的 Linux 系统,然后运行grub-install命令,这个命令需要指定将 Grub 安装到那个硬盘的 MBR,具体操作看文档即可。

Grub2 和 Grub 相比,有了很多改进,改进到 Grub2 都相当于一个小型的操作系统了,比如 Grub2 能识别各种格式的硬盘分区及文件系统,能识别各种格式的图像,甚至还能动态加载模块。其中最重要的改变是配置文件由 Grub1 时代的/boot/menu.lst文件变成 Grub2 时代的/boot/grub/grub.cfg文件了(注意,有的系统中是/boot/grub2/grub.cfg,比如 Fedora),配置文件的语法也变复杂了很多。虽然 Grub2 配置文件的语法比较复杂,但是我们一般不需要直接修改该文件,而是修改/etc/default/grub文件后,再使用update-grub命令自动生成配置文件。当然,要完成一些简单任务时对/boot/grub/grub.cfg文件进行直接修改也是可以的,只是一旦系统更新,则对该文件的修改有可能被覆盖。

Grub 最重要的任务就是加载 Linux 内核以及 initramfs 文件了,这几个命令是非常简单易懂的,在/boot/grub/grub.cfg文件中也非常容易找到,如下图:

上图主要是展示 Grub 载入 Linux 内核并且给 Linux 内核传递参数的过程,同时展示载入 initramfs 文件的过程。

探索initramfs文件的方法

Grub 将 Linux 内核和 initramfs 文件载入内存后,就把控制权交给内核,内核会将内存中的 initramfs 文件挂载为根文件系统,当内核初始化完成后需要进入用户空间时,会运行/init,其实这个/init是在 initramfs 文件中的,所以如果想知道系统启动后都干了些什么,研究一下 initramfs 文件中的内容是有必要的。

/boot目录下,一般都有一个/boot/initrd.img文件或一个/boot/initramfs.img文 件,但是要看里面的内容还得有点技巧。先得学习一下历史。第一个问题就是为什么要有 initrd 或者 initramfs,答案是这样的,为了减小 Linux 内核的大小,有一些驱动并没有编译进内核,而是以模块的形式存在于文件系统中,但是有些文件系统呢又需要内核加载了相应的驱动模块才能读取,这样就形成了 一个是先有鸡还是先有蛋这样的一个矛盾的问题,为了解决这个问题,就是把这些驱动模块放到 initrd 或 initramfs 文件中,由启动器将其载入内存,然后内核加载内存中的驱动模块,再驱动其它的文件系统,然后进行更多的工作。同时,对于很多嵌入式操作系统而言,可能载入 内存的 initrd 或 initramfs 就是最终的文件系统。第二个问题就是 initrd 文件和 initramfs 文件是什么格式的,怎么创建和打开。这个问题有点复杂,且看我下面细细道来。

其实在 Linux 的历史中,内存中的文件系统使用的技术还不一样。在很老的系统中,是将某一块内存模拟成磁盘,称之为 RamDisk,这个技术效率不高,为什么不高呢,那是因为将内存模拟成磁盘后,依然需要像对待磁盘一样对待它,需要给它创建特定格式的文件系统,读取或 写入文件的时候还是要用到内核的缓存系统,这样同一份数据就在内存中存在了两份,所以很浪费。老系统中使用的 initrd 文件其实就相当于是一个磁盘的镜像,所以要读取或写入它的内容,就需要将它挂在到系统中,然后进行读写。

现在我们用的都是新系统,initrd 已经被 initramfs 取代了,虽然很多系统中仍然用initrd.img作 为文件名,其实使用的是 initramfs 技术。initramfs 技术不使用 RamDisk,而是使用 tempfs,也就是复用了内核中的缓存系统,所以 tempfs 中的内容在内存中只存在一份,那就是在内核的缓存中,不仅节约了内存开销,也提高了效率。那么 initramfs 文件是什么格式呢?它就是经过压缩的 CPIO 格式,只要会cpio,打开它就不是问题。

其实也不是完全没有问题,我在研究 Fedora 21 的 initramfs.img 文件时就碰到了困难。如果大家使用cat initramfs.img | cpio -imd将该 CPIO 文件解开,会发现只有少许几个文件,文件数量和文件大小都远远低于我们的预期,如下图:

问题究竟出在哪里呢?这是因为 Fedora 21 采用了最新的 Early User Space 技术。这个问题在网上还暂时搜不到答案,我也是看到解压出的文件中有一个early_cpio, 然后以 early 为关键词去看内核的代码,才知道的。但是关于该技术的具体信息,我也没搞懂。不过这不是重点,重点是如何正确地将该文件解包。首先,我找到了 Fedora 21 系统中用来构建 initramfs 文件的工具是 dracut,采用的方法论请看我该系列的第一篇,如下图:

然后,我发现 dracut 软件包中提供的lsinitrd工具可以查看 initramfs 中的内容,而且该工具是一个脚本。所以我就把它打开看了一下,如下图:

在这个脚本中,我发现它需要用到一个skipcpio程序,跳过 initramfs 文件的头部后,再将剩下的部分当成压缩的 CPIO 文件进行解包。看来,会读脚本程序有时也很重要,所以我的《Bash脚本编程语言中的美学与哲学》这一篇没有白写。最后,解包 initramfs 文件的命令是这样的:

sudo /usr/lib/dracut/skipcpio initramfs-$(uname -r).img | zcat | cpio -imd

如下图:

接下来,我们就可以愉快地研究/init程序都干些什么了。

基于systemd的init系统

其实 systemd 在开源社区是有争论的,有很多人反对 systemd,反对的理由是 systemd 太复杂了,它干的事太多了,它除了是一个 init 程序外,同时还负责管理硬件、日志、定时任务等,甚至连用户登录都接管了。很多人仍为 Unix 世界的哲学是只做一件事并做好,对于 systemd 这种什么都要管庞大软件包,很多人是不喜欢的。systemd 的另外一个违反 Unix 哲学的地方就是它的日志系统使用了二进制格式,而不是 Unix 中广泛使用的纯文本格式。

但是我们不要被这些反对意见误导。systemd 真的是一个非常优秀的初始化系统,至少我是在使用过后就喜欢上了它。systemd 对传统所做出的每一个改变都是有道理的,即使是二进制格式的日志,也是为了更好地管理和解析日志,同时也提供了非常完善的查看日志的工具。Linux 之父也说他对 systemd 没有看法,他认为所谓的“只做一件事并做好”的哲学早已过时,该哲学只适合于那些可以通过流水线将各程序组合起来的场景,而现实中有很多复杂的应用场景不 能通过流水线解决。不管怎么样,systemd 的使用是越来越普遍,Fedora、RedHat EL 7均以采用 systemd,Ubuntu 的下一个版本也将采用 systemd。所以,学习 systemd 是必须的。

学习 systemd 的最好的途径是查看它的文档man systemd,然后根据该文档的提示查看其它相应的文档,比如man systemd.serviceman systemd.target等等。要完整地讲述 systemd 估计都可以写一本书,我下面只说说它的一些基本概念。

系统初始化要做很多工作,如挂在文件系统,启动sshd服务,配置交换分区等,systemd 把每一个工作作为一个 unit,每一个 unit 对应一个配置文件。systemd 又将 unit 分成不同的类型,每一种类型的 unit 其配置文件具有不同的扩展名,常见的 unit 类型有:

  • service 对应一个后台服务进程,如 httpd、mySQLd 等;
  • soket 对应一个套接字,之后对应到一个service,类似于xinetd的功能;
  • device 对应一个用 udev 规则标记的设备;
  • mount 对应系统中的一个挂载点,systemd 据此进行自动挂载,为了与 SysVinit 兼容,目前 systemd 自动处理 /etc/fstab 并转化为 mount;
  • automount 自动挂载点;
  • swap 配置交换分区;
  • target 配置单元的逻辑分组,包含多个相关的配置单元,可以当成是 SysVinit 中的运行级;
  • timer 定时器,用来定时触发用户定义的操作,它可以用来取代传统的 atd,crond 等;

systemd 的配置文件存放于/usr/lib/systemd/system目录中,initramfs 中的配置文件自然是位于相对于其解压目录的usr/lib/systemd/system目录中了。initramfs 中的/init程序以及硬盘上的/sbin/init程序都只是/usr/lib/systemd/systemd程序的链接而已。systemd 启动后,先根据default.target配置文件中的规则初始化系统。其实default.target也是一个链接,如果它链接到multi-user.target,则启动字符界面,如果它链接到graphical.target,则启动图形界面。这个和传统的 SysVinit 中的运行级别有一定的相似性。systemd 中的 target 很多,它和传统的 SysVinit 中的运行级别对应关系如下:

SysVinit中的运行级别 systemd中的 target 意义
0 runlevel0.target, poweroff.target 关闭系统。
1, s, single runlevel1.target, rescue.target 单用户模式。
2, 4 runlevel2.target, runlevel4.target, multi-user.target 用户定义/域特定运行级别。默认等同于 3。
3 runlevel3.target, multi-user.target 多用户,非图形化。用户可以通过多个控制台或网络登录。
5 runlevel5.target, graphical.target 多用户,图形化。通常为所有运行级别 3 的服务外加图形化登录。
6 runlevel6.target, reboot.target 重启。
emergency emergency.target 紧急 Shell。

一个 target 会依赖其它的 unit,被依赖的 unit 又会依赖更多的 unit,系统启动时这些 unit 都会同时被运行,和拔出萝卜带出泥的原理是一样的,而且 systemd 是并行启动各个 unit,这样可以加快系统启动的速度。每一个 unit 的配置文件都是纯文本的,语法格式极其简单,可以打开直接阅读。这些配置的细节我就不讲了,文档里面都写得很清楚。对于用户来讲,只要会使用 systemd 的管理工具systemctl命令就可以解决大部分的问题。

我们经常和系统启动打交道的地方是设置某些服务在系统启动时自动运行。回忆一下 RedHat EL 5 之前的系统,要让某个服务在系统启动时运行,我们需要把一些脚本文件放到/etc/rc.d/init.d目录中,然后在相应的/etc/rc.d/rcX.d目录中建立符号链接,并且这些脚本的命名还有讲究。同时,RedHat EL 5 之前的系统还提供了几个工具来简化我们的工作,它们是service命令和chkconfig命令。在现在的新系统中,这几个命令就只能成为回忆了,systemctl命令接管了这些工作。如下表:

SysVinit 命令 Systemd 命令 意义
service foo start systemctl start foo.service 用来启动一个服务 (并不会重启现有的)。
service foo stop systemctl stop foo.service 用来停止一个服务 (并不会重启现有的)。
service foo restart systemctl restart foo.service 用来停止并启动一个服务。
service foo reload systemctl reload foo.service 当支持时,重新装载配置文件而不中断等待操作。
service foo condrestart systemctl condrestart foo.service 如果服务正在运行那么重启它。
service foo status systemctl status foo.service 汇报服务是否正在运行。
ls /etc/rc.d/init.d/ systemctl list-unit-files –type=service 用来列出可以启动或停止的服务列表。
chkconfig foo on systemctl enable foo.service 在下次启动时或满足其他触发条件时设置服务为启用。
chkconfig foo off systemctl disable foo.service 在下次启动时或满足其他触发条件时设置服务为禁用。
chkconfig foo systemctl is-enabled foo.service 用来检查一个服务在当前环境下被配置为启用还是禁用。
chkconfig –list systemctl list-unit-files –type=service 输出在各个运行级别下服务的启用和禁用情况。
chkconfig foo –list ls /etc/systemd/system/*.wants/foo.service 用来列出该服务在哪些运行级别下启用和禁用。
chkconfig foo –add systemctl daemon-reload 当您创建新服务文件或者变更设置时使用。
telinit 3 systemctl isolate multi-user.target 改变至多用户运行级别。

其实systemctl命令也只是把相应的*.service文件放到相应的目录中而已,这也说明,要使用 systemd,并不只是将 SysVinit 或者 upstart 替换成 systemd 就可以了的,所有的其它的和服务相关的软件包,都需要针对 systemd 进行修改。所以,Ubuntu 要走的路还有点长。

在使用了 systemd 的系统中,电源管理也是由 systemd 接管的。之所以提到电源管理,是因为我在笔记本上使用 Linux 时碰到的一个非常奇葩的问题,那就是在我的笔记本电脑上,进入桌面后,总是提示“无线网卡已通过硬件禁用”,只有先将系统挂起,然后再唤醒,无线网卡才能 启用。systemd 电源管理的命令如下:

命令 操作
systemctl reboot 重启机器
systemctl poweroff 关机
systemctl suspend 挂起
systemctl hibernate 休眠
systemctl hybrid-sleep 混合休眠模式(同时休眠到硬盘并挂起)

最后补充一下,systemd 自带日志服务 journald,并且用二进制格式保存所有日志信息,用户使用journalctl命令来查看日志信息。

进入图形界面

如果作为服务器运行,Linux 系统的启动就仅仅只包含以上过程。如果是使用 Linux 桌面系统,最后还需要进入图形界面。进入图形界面的工作是由显示管理器来完成的,在 Fedora 中,显示管理器程序是 gdm,在 Ubuntu中,是 lightdm。显示管理器负责启动 X Server 并显示登录界面,用户登录后,显示管理器启动 gnome-session 从而进入整个桌面环境。关于 X Window 方面的内容,可以看我写的这一篇《X Window的奥秘》。最后要说一下图形界面的新进展,那就是 Wayland 将要取代 Xorg 成为新的图形技术。关于 Wayland 的优点,请看这两篇《揭开Wayland的面纱(一):X Window的前生今世》《 揭开Wayland的面纱(二):Wayland应运而生》。这两篇文章发布于2010年,现在已经是2015年了,但是 Wayland 还是没有普及。不过在 Fedora 21 中,已经可以试用 Wayland 了,我试用了一下,貌似还不太稳定。

好了,关于 Linux 系统启动过程的东西就写这么多吧。从以上内容可以看到,Linux 中的技术也是一直不断在进步,我们需要拥抱变化,而不能墨守成规。很多时候,图书和网络教程并不能跟上最新的进展,这是就需要我们自己去思考和探索,因 此,掌握正确的方法论尤其重要。

(京山游侠于2015-04-10发布于博客园,转载请注明出处。)