2014年5月31日星期六

Linux_065:常用命令之二十一:scp

scp(secure copy)命令在用于在Linux下进行远程(跨机器)拷贝的,和它类似的命令有cp,不过cp只是在本机进行拷贝不能跨机器。
另外,scp传输是加密的,而且不会增加多少系统负荷。

1.命令格式:scp [参数] [原路径] [目标路径]
参数说明:
(1)-1  强制scp命令使用协议ssh1。
(2)-2  强制scp命令使用协议ssh2。
(3)-4  强制scp命令只使用IPv4寻址。
(4)-6  强制scp命令只使用IPv6寻址。
(5)-B  使用批处理模式(传输过程中不询问传输口令或短语)。
(6)-C  允许压缩。(将-C标志传递给ssh,从而打开压缩功能)
(7)-p 保留原文件的修改时间,访问时间和访问权限。
(8)-q  不显示传输进度条。
(9)-r  递归复制整个目录。
(10)-v 详细方式显示输出。scp和ssh(1)会显示出整个过程的调试信息。这些信息用于调试连接,验证和配置问题。
(11)-c cipher  以cipher将数据传输进行加密,这个选项将直接传递给ssh。
(12)-F ssh_config  指定一个替代的ssh配置文件,此参数直接传递给ssh。
(13)-i identity_file  从指定文件中读取传输时使用的密钥文件,此参数直接传递给ssh。  
(14)-l limit  限定用户所能使用的带宽,以Kbit/s为单位。  
(15)-o ssh_option  如果习惯于使用ssh_config(5)中的参数传递方式,
(16)-P port  注意是大写的P, port是指定数据传输用到的端口号
(17)-S program  指定加密传输时所使用的程序。此程序必须能够理解ssh(1)的选项。

2. 使用实例
(1)从本地服务器复制文件到远程服务器
scp local_file remote_username@remote_ip:remote_folder
(2)从本地服务器复制目录到远程服务器
scp -r local_folder remote_username@remote_ip:remote_folder
(3)从远程服务器复制文件到本地服务器
scp remote_username@remote_ip:remote_file  local_file
(4)从远程服务器复制目录到本地服务器
scp -r remote_username@remote_ip:remote_folder  local_folder

参考文献:
1. http://www.cnblogs.com/peida/archive/2013/03/15/2960802.html

Linux_064:常用命令之二十:iostat

环境:RHEL 6.5

iostat(I/O statistics)命令可以查看CPU、网卡、tty设备、磁盘、CD-ROM 等设备的活动情况。
iostat属于sysstat软件包,可以用yum install sysstat 直接安装。

命令格式:iostat[参数][时间][次数]
(1)-C:显示CPU使用情况。
(2)-d:显示磁盘使用情况。
(3)-k:以 KB 为单位显示。
(4)-m:以 M 为单位显示。
(5)-N:显示磁盘阵列(LVM)信息。
(6)-n:显示NFS 使用情况。
(7)-p[磁盘]:显示磁盘和分区的情况。
(8)-t:显示终端和CPU的信息。
(9)-x:显示详细信息。
(10)-V:显示版本信息。

使用举例:

1.iostat:显示所有设备负载信息
Linux 2.6.32-431.el6.x86_64 (localhost.localdomain) 2014年05月31日 _x86_64_(1 CPU)

avg-cpu:  %user   %nice %system %iowait  %steal   %idle
                      0.25    0.01    0.27            0.12        0.00   99.34

Device:            tps   Blk_read/s   Blk_wrtn/s   Blk_read   Blk_wrtn
scd0              0.02         0.11         0.00        420          0
scd2              0.02         0.12         0.00        440          0
sda               5.05       194.64        17.28     737452      65488
dm-0              8.57       187.42        17.28     710082      65456
dm-1              0.08         0.68         0.00       2576          0

1.1 CPU属性说明:
(1)%user:CPU处在用户模式下的时间百分比。
(2)%nice:CPU处在带NICE值的用户模式下的时间百分比。
(3)%system:CPU处在系统模式下的时间百分比。
(4)%iowait:CPU等待输入输出完成时间的百分比。
(5)%steal:管理程序维护另一个虚拟处理器时,虚拟CPU的无意识等待时间百分比。
(6)%idle:CPU空闲时间百分比。
备注:
(1)如果%iowait的值过高,表示硬盘存在I/O瓶颈。
(2)如果%idle的值高,表示CPU较空闲。
(3)如果%idle的值高但系统响应慢时,有可能是CPU在等待分配内存,表明此时的系统的瓶颈是内存。
(4)如果%idle的值持续低于10,那么系统的CPU处理能力相对较低,表明此时系统的瓶颈是CPU。

1.2 Device属性值说明:
(1)tps:设备每秒的传输次数。
(2)kB_read/s:每秒从设备读取的数据量(单位KB)。
(3)kB_wrtn/s:每秒向设备写入的数据量(单位KB)。
(4)kB_read:读取的总数据量(单位KB)。
(5)kB_wrtn:写入的总数据量(单位KB)。
(6)Blk_read/s: 每秒读扇区数量(一扇区为512bytes)。
(7)Blk_wrtn/s: 每秒写扇区数量(一扇区为512bytes)。
(8)Blk_read: 取样时间间隔内读扇区总数量(一扇区为512bytes)。
(9)Blk_wrtn: 取样时间间隔内写扇区总数量(一扇区为512bytes)。

2. iostat 2 3:每隔2秒显示一次所有设备负载信息,显示三次。

3. iostat -d -x -k:显示所有磁盘的负载详细信息。

Device:         rrqm/s   wrqm/s   r/s   w/s    rkB/s    wkB/s avgrq-sz avgqu-sz   await  svctm  %util
sda               0.44    38.59  0.40 22.32    21.85   243.71    23.37     0.04    1.78   4.20   9.54
sda1              0.00     0.00  0.00  0.00     0.00     0.00    18.90     0.00    8.26   6.46   0.00
sda2              0.36     0.43  0.11  0.01     1.87     1.76    63.57     0.01   63.75   1.94   0.02
sda3              0.00     1.24  0.04  0.95     0.31     8.75    18.42     0.04   39.77   8.73   0.86
sda4              0.00     0.00  0.00  0.00     0.00     0.00     2.00     0.00   19.67  19.67   0.00
sda5              0.00     6.65  0.00  6.94     0.06    54.37    15.67     0.26   36.81   4.48   3.11
sda6              0.00     1.71  0.01  2.19     0.09    15.61    14.29     0.03   12.40   5.84   1.28
sda7              0.08    28.56  0.25 12.24    19.52   163.22    29.28     0.27   21.46   5.00   6.25

说明:
(1)rrqm/s:每秒进行 merge 的读操作数目。
(2)wrqm/s:每秒进行 merge 的写操作数目。
(3)r/s:每秒完成的读 I/O 设备次数。
(4)w/s:每秒完成的写 I/O 设备次数。
(5)rsec/s:每秒读扇区数。
(6)wsec/s:每秒写扇区数。
(7)rkB/s:每秒读K字节数,是 rsect/s 的一半,因为每扇区大小为512字节。
(8)wkB/s:每秒写K字节数,是 wsect/s 的一半。
(9)avgrq-sz:平均每次设备I/O操作的数据大小(扇区)。
(10)avgqu-sz:平均I/O队列长度。
(11)await:平均每次设备I/O操作的等待时间(毫秒)。
(12)svctm:平均每次设备I/O操作的服务时间 (毫秒)。
(13)%util:  一秒中有百分之多少的时间用于 I/O 操作,即被IO消耗的CPU百分比。
备注:
(1)如果 %util 接近100%,说明产生的I/O请求太多,I/O系统已经满负荷,该磁盘可能存在瓶颈。
(2)如果 svctm 比较接近 await,说明 I/O 几乎没有等待时间;如果 await 远大于 svctm,说明I/O 队列太长,I/O响应太慢,需要进行优化。
(3)如果avgqu-sz比较大,表示有I/O在等待。

4. iostat -c 1 3:显示CPU信息。

参考文献:
1. http://www.cnblogs.com/peida/archive/2012/12/28/2837345.html

OpenShift_001:部署Java Web应用到OpenShift上

环境:MAC OS X:10.9.3 + OpenShift OnLine

OpenShift是Red Hat的PaaS,其中最小配置可以免费使用。
本文介绍如何部署一个Java Web应用到OpenShift上。

第1步:当然是要注册一个账号:https://www.openshift.com/,过程从略。

第2步:安装OpenShift RHC Client Tools。

1. 检查ruby是否安装了
$ruby -e 'puts "Welcome to Ruby"'
Welcome to Ruby

2. 检查git是否安装了
$ git --version
git version 1.8.5.2 (Apple Git-48)

3. 安装rhc:$ sudo gem install rhc
Password:
===========================================================================

If this is your first time installing the RHC tools, please run 'rhc setup'

===========================================================================
Successfully installed rhc-1.24.4
Parsing documentation for rhc-1.24.4
1 gem installed

4. 设置rhc:$ rhc setup

OpenShift Client Tools (RHC) Setup Wizard

This wizard will help you upload your SSH keys, set your application namespace,
and check that other programs like Git are properly installed.

Login to openshift.redhat.com: maping930883@hotmail.com
Password: ********

OpenShift can create and store a token on disk which allows to you to access the
server without using your password. The key is stored in your home directory and
should be kept secret.  You can delete the key at any time by running 'rhc
logout'.
Generate a token now? (yes|no) yes
Generating an authorization token for this client ... lasts about 1 month

Saving configuration to /Users/maping/.openshift/express.conf ... done

Your public SSH key must be uploaded to the OpenShift server to access code.
Upload now? (yes|no)
yes

Since you do not have any keys associated with your OpenShift account, your new
key will be uploaded as the 'default' key.

Uploading key 'default' ... done

Checking for git ... found git version 1.8.5.2 (Apple Git-48)

Checking common problems .. done

Checking for a domain ... maping930883

Checking for applications ... found 2

  jenkins http://jenkins-maping930883.rhcloud.com/
  php     http://php-maping930883.rhcloud.com/

  You are using 3 of 3 total gears
  The following gear sizes are available to you: small

Your client tools are now configured.

第3步:安装JBoss Devloper Studio 7.1.1 GA。
之所以安装JBDS,是因为这个开发工具,已经包含了JBoss Tools插件。
你也可以使用Eclipse,然后手动安装这个插件。

第4步:打开JBDS,新建应用,选择OpenShift->OpenShift 应用
它会询问你的OpenShift账号等信息。
创建好应用后,就可以发布了。
选择Team->Commit,提交修改。
选择Team->Push to Upstream,推送提交到服务器上。

第5步:访问你的应用。

使用ssh连接到你的应用
(1)rhc ssh -a testopenshift
(2)cd $OPENSHIFT_DATA_DIR
可以使用命令:env,查看都有哪些环境变量。

使用scp拷贝文件
(1)scp setJdk6Env.sh 5386f544e0b8cdfdd00000fb@testopenshift-maping930883.rhcloud.com:~/app-root/data/setJdk6Env.sh
(2)scp 5386f544e0b8cdfdd00000fb@testopenshift-maping930883.rhcloud.com:~/app-root/data/test.sh .

参考文献:
1. https://www.openshift.com/developers/rhc-client-tools-install
2. http://blog.csdn.net/fulinkster/article/details/6692595
3. http://blog.csdn.net/cherry_sun/article/details/7598721
4. http://www.lupaworld.com/article-234801-1.html
5. http://www.oschina.net/translate/how-to-run-apache-tomcat-8-on-openshift
6. http://www.360doc.com/content/13/0505/15/11978685_283129497.shtml
7. http://www.huiwei19.com/free/605.html
8. http://virtual.51cto.com/art/201211/365976.htm
9. https://www.youtube.com/watch?v=4Htd9vAxJAc

2014年5月30日星期五

MAC_015:你好,Docker!

环境: MAC OS X 10.9.3

最近Docker好像火得不得了,于是跟着Helloworld教程做了一遍。

What is Docker?
Docker is a container based virtualization framework. Unlike traditional virtualization Docker is fast, lightweight and easy to use. Docker allows you to create containers holding all the dependencies for an application. Each container is kept isolated from any other, and nothing gets shared.
Docker 是什么?
Docker 是一个基于容器的虚拟化框架。与传统的虚拟话产品不同,Docker更快、更轻、更易使用。Docker允许你创建这样的容器:与应用有关的所有依赖都打包在容器之中。各个容器之间保持隔离性,不会共享任何东西。

Docker 由下面这些组成:
(1)Docker 服务器守护程序(server daemon),用于管理所有的容器。
(2)Docker 命令行客户端,用于控制服务器守护程序。
(3)Docker 镜像:查找和浏览 docker 容器镜像。https://index.docker.io/

1. 安装
(1)安装VirtualBox,因为Docker 服务器要运行在VirtualBox中
(2)Docker 服务器:brew install boot2docker
(3)Docker 客户端:brew install docker
因为我的环境是MAC OS X,所以使用brew来安装最简单,关于brew的安装,请参考《MAC下安装Homebrew》。
手工安装的方法也有,请参考文献1。

2. 运行 Docker daemon
(1)初始化Docker daemon:boot2docker init
(2)启动Docker daemon:boot2docker up
boot2docker还有很多参数:{init|start|up|pause|stop|restart|status|info|delete|ssh|download}。

3. 运行 Docker客户端
Docker客户端命令就是docker。
(1)docker version
Client version: 0.11.1
Client API version: 1.11
Go version (client): go1.2.1
Git commit (client): fb99f99
Server version: 0.11.1
Server API version: 1.11
Git commit (server): fb99f99
Go version (server): go1.2.1
(2)docker info
Containers: 4
Images: 10
Storage Driver: aufs
 Root Dir: /mnt/sda1/var/lib/docker/aufs
 Dirs: 18
Execution Driver: native-0.2
Kernel Version: 3.14.1-tinycore64
Debug mode (server): true
Debug mode (client): false
Fds: 11
Goroutines: 13
EventsListeners: 0
Init Path: /usr/local/bin/docker

4. Using Docker port forwarding with boot2docker
In order to forward network ports from Docker with boot2docker we need to manually forward the port range Docker uses inside VirtualBox. To do this we take the port range that Docker uses by default with the -P option, ports 49000-49900, and run the following command.
这一步没太明白,照做就是了。注意,要先停掉Docker daemon。

for i in {49000..49900}; do
 VBoxManage modifyvm "boot2docker-vm" --natpf1 "tcp-port$i,tcp,,$i,,$i";
 VBoxManage modifyvm "boot2docker-vm" --natpf1 "udp-port$i,udp,,$i,,$i";
done

5. 升级 Docker daemon
(1)boot2docker stop
(2)boot2docker download
(3)boot2docker start

好了,Docker服务器和客户端都装好了,现在可以下载一个image了。

6. 启动Docker daemon后,下载busybox image:docker pull busybox

7. docker run busybox /bin/echo hello world
这个命令是运行一个简单的echo命令,不过是在busybox这个image中运行的。
(1)docker run 启动一个容器。
(2)busybox是image。
(3)/bin/echo是运行在容器中的命令。

说明:
在~/.boot2docker目录下有:boot2docker-vm.vmdk 和 boot2docker.iso。
打开virtualBox,会发现已经自动装载了虚机:boot2docker-vm。

参考文献:
1. https://www.docker.io/

2014年5月28日星期三

Linux_062:SUID与SGID的作用

使用命令:ls -l /etc/passwd,查看/etc/passwd文件详细信息时,发现只有root用户才有读写权限:
-rw-r--r-- 1 root root 1778 5月  26 23:38 /etc/passwd
再使用命令:ls -l /usr/bin/passwd,查看/usr/bin/passwd文件,发现只有root用户才有执行权限。
-rwsr-xr-x. 1 root root 30768 2月  17 2012 /usr/bin/passwd
等一下,那个“s”是干吗的?
这个s就是SUID,当设置了SUID时,进程的UID为该可执行文件的属主的UID。
这就是为什么普通用户使用passwd命令时,可以修改/etc/passwd文件内容,因为passwd命令的运行权限就是root!
可以使用“chmod u+s 文件名” 和“chmod u-s 文件名”来设置和取消SUID设置。
以此类推,可以使用“chmod g+s 文件名” 和“chmod g-s 文件名”来设置和取消SGID设置。那么,SGID有什么用处呢?

假如有多个用户都工作于一个项目,比如都需要在目录project1下编辑文件。此时,SGID就可以派上用场。
(1)使用mkdir project1,创建目录project1。
(2)使用chmod g+w ,让同组的其他人可以读写目录project1。
(3)使用chgrp devel project1,改变目录project1的属组为devel。
(4)使用usermod –G devel dev1,把用户dev1加到用户组devel中,以此类推加入其他用户,比如guo。
(4)使用chmod g+s,让该目录下创建的文件的属组与该目录的属组相同。

目录project1的权限如下:
drwxrwsr-x.   2 root project1  4096 5月  28 22:12 project1

每个用户都可以在project1目录下的文件,创建后的文件的属主是每个用户自己,属组则是project1,比如:
-rw-rw-r--. 1 dev1 project1 0 4月  16 00:44 dev1.txt
-rw-rw-r--  1 guo  project1 0 5月  28 22:12 guo.txt
因为属组有写权限,因此每个用户可以修改其他用户的文件。

参考文献:
1. http://hi.baidu.com/mcaeebbkarjknte/item/f25945493c2388ab60d7b95c

Linux_061:用户和组的基本管理

1. 添加新的用户:useradd 选项 用户名
选项含义如下:
-c comment:指定一段注释性描述。
-d 目录:指定用户主目录,如果此目录不存在,则同时使用-m选项,可以创建主目录。
-g 用户组:指定用户的属组。
-G 用户组:指定用户所属的附加组。
-s shell文件:指定用户的登录shell。
-u 用户号:指定用户的用户号,如果同时有-o选项,则可以重复使用其他用户的标识号。

新建的用户可以在/etc/passwd文件中找到:
(1)每一行的格式为:用户名:密码:UID:GID:用户描述:主目录:登录shell。
(2)root的UID和GID都为0。
(3)口令为x,表示其口令已经保存到/etc/shadow文件中。
(4)UID范围1~200为系统保留,有些用于伪用户(psuedo users),这些用户不能登录,因为它们的登录shell为空。
它们的存在主要是方便系统管理,满足相应的系统进程对文件属主的要求。

root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
uucp:x:10:14:uucp:/var/spool/uucp:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
games:x:12:100:games:/usr/games:/sbin/nologin
gopher:x:13:30:gopher:/var/gopher:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
nobody:x:99:99:Nobody:/:/sbin/nologin
dbus:x:81:81:System message bus:/:/sbin/nologin
usbmuxd:x:113:113:usbmuxd user:/:/sbin/nologin
vcsa:x:69:69:virtual console memory owner:/dev:/sbin/nologin
rpc:x:32:32:Rpcbind Daemon:/var/cache/rpcbind:/sbin/nologin
rtkit:x:499:497:RealtimeKit:/proc:/sbin/nologin
avahi-autoipd:x:170:170:Avahi IPv4LL Stack:/var/lib/avahi-autoipd:/sbin/nologin
abrt:x:173:173::/etc/abrt:/sbin/nologin
rpcuser:x:29:29:RPC Service User:/var/lib/nfs:/sbin/nologin
nfsnobody:x:65534:65534:Anonymous NFS User:/var/lib/nfs:/sbin/nologin
haldaemon:x:68:68:HAL daemon:/:/sbin/nologin
gdm:x:42:42::/var/lib/gdm:/sbin/nologin
ntp:x:38:38::/etc/ntp:/sbin/nologin
apache:x:48:48:Apache:/var/www:/sbin/nologin
saslauth:x:498:76:"Saslauthd user":/var/empty/saslauth:/sbin/nologin
postfix:x:89:89::/var/spool/postfix:/sbin/nologin
pulse:x:497:496:PulseAudio System Daemon:/var/run/pulse:/sbin/nologin
sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin
tcpdump:x:72:72::/:/sbin/nologin
maping:x:500:500:maping:/home/maping:/bin/bash


例1:# useradd –d /usr/sam -m sam
新建用户sam,其中-d和-m选项用来为登录名sam产生一个主目录/usr/sam。

例2:# useradd -s /bin/sh -g group –G adm,root gem
新建用户gem,该用户的登录shell是/bin/sh,它属于group用户组,同时又属于adm和root用户组,其中group用户组是其属组。

2. 用户口令的管理:passwd 选项 用户名
超级用户可以为自己和其他用户指定口令,普通用户只能修改自己的口令。
选项含义如下:
-l :锁定口令,即禁用账号。
-u:口令解锁。
-d:使账号无口令。
-f:强迫用户下次登录时修改口令。

3. 删除帐号:userdel 选项 用户名

例1:userdel -r sam
删除用户sam,删除在系统文件中(主要是/etc/passwd, /etc/shadow, /etc/group)的记录,-r 表示同时删除用户的主目录。

4. 修改帐号:usermod 选项 用户名
修改用户账号就是更改用户的有关属性,如用户号、主目录、用户组、登录hell等。
选项含义如下:
-c comment:指定一段注释性描述。
-d 目录:指定用户主目录,如果此目录不存在,则同时使用-m选项,可以创建主目录。
-g 用户组:指定用户的属组。
-G 用户组:指定用户所属的附加组。
-s shell文件:指定用户的登录shell。
-u 用户号:指定用户的用户号,如果同时有-o选项,则可以重复使用其他用户的标识号。

例1:usermod -s /bin/ksh -d /home/z –g developer sam
将用户sam的登录shell修改为ksh,主目录改为/home/z,用户组改为developer。

5. 增加用户组:groupadd 选项 用户组
选项含义如下:
-g GID 指定新用户组的组标识号(GID)。
-o 一般与-g选项同时使用,表示新用户组的GID可以与系统已有用户组的GID相同。

例1:groupadd group1
增加一个新组group1,新组的组标识号是在当前已有的最大组标识号的基础上加1。

例2:groupadd -g 101 group2
增加一个新组group2,同时指定新组的组标识号是101。

6. 删除用户组:groupdel 用户组

7. 修改用户组:groupmod 选项 用户组
参数含义如下:
-g GID 为用户组指定新的组标识号。
-o 与-g选项同时使用,用户组的新GID可以与系统已有用户组的GID相同。
-n 新用户组 将用户组的名字改为新名字

例1:groupmod -g 102 group2
将组group2的组标识号修改为102。

例2:groupmod –g 10000 -n group3 group2
将组group2的标识号改为10000,组名修改为group3。

8. 切换用户组:newgrp 用户组
如果一个用户同时属于多个用户组,那么用户可以在用户组之间切换,以便具有其他用户组的权限。
可以使用命令:groups,查看当前用户属于哪些组。

参考文献:
1. http://www.blogjava.net/hello-yun/archive/2012/05/16/378295.html
2. http://blog.csdn.net/new_abc/article/details/12203785
3. http://fangfang0717.blog.51cto.com/236466/130699

2014年5月26日星期一

Linux_060:shell 脚本的执行方式

执行一个脚本时,我们一般用source filename或者 . ./filename(第1个.和第2个.之间有个空格)。

相同点:
(1)使用这两种方式时,脚本不必是可执行的。
(2)这两种方式都是在当前shell中执行脚本。即source命令和.命令其实都只是简单地读取脚本里面的语句,然后依次在当前shell里面执行,并没有建立新的子shell。那么脚本里面所有新建、改变变量的语句都会保存在当前shell里面。

那么,在当前shell中执行和在子shell中执行的区别是什么呢?
在子shell中定义的变量和函数在执行结束后就消失了,而在当前shell中的定义的变量和函数在执行结束后可以保留下来。

不同点:
(1)使用source命令时,filename不必包含“./”,是因为souce命令会从PATH环境变量中找filename,如果在PATH中找不到,再从当前目录下找。
(2)使用.命令时,filename必须包含“./”,是因为.命令只从PATH环境变量中找filename,因为当前目录不在PATH环境中,所以必须加上“./”。

如果脚本是可执行的,那么直接运行./filename会怎么样呢?
(1)当shell脚本具有可执行权限时,用sh filename与./filename执行脚本是没有区别的。
(2)sh filename 重新建立一个子shell,在子shell中执行脚本里面的语句,该子shell继承父shell的环境变量,但子shell新建的、改变的变量不会被带回父shell,除非使用export。

举例说明:
(1)新建一个test.sh脚本,内容为:A=1
(2)然后使其可执行chmod +x test.sh。
(3)运行sh test.sh后,echo $A,显示为空,因为A=1并未传回给当前shell。
(4)运行./test.sh后,也是一样的效果。
(5)运行source test.sh 或者 . test.sh,然后echo $A,则会显示1,说明A=1的变量在当前shell中。

参考文献:
1. http://blog.csdn.net/mci2004/article/details/7182829
2. http://codingstandards.iteye.com/blog/837935
3. http://blog.chinaunix.net/uid-10435474-id-2957055.html
4. http://www.51testing.com/html/38/225738-206878.html

2014年5月25日星期日

Linux_059:RHEL Bash初始化过程

环境:RHEL 6.5

RHEL使用Bourne Again shell(Bash),本文介绍其初始化过程。
首先明确两个概念:
(1)交互式 shell:指用户在提示符下输命令的shell,而非执行脚本的shell。
(2)登录 shell:指在输入用户名和密码登录后得到的shell。

1. 交互式登录 shell
用户输入用户名和密码登录后,Bash初始化过程如下:
/etc/profile->~/.bash_profile->~/.bashrc—>/etc/bashrc—>~.bash_logout

(1)/etc/profile 文件内容:
# /etc/profile

# System wide environment and startup programs, for login setup
# Functions and aliases go in /etc/bashrc

# It's NOT a good idea to change this file unless you know what you
# are doing. It's much better to create a custom.sh shell script in
# /etc/profile.d/ to make custom changes to your environment, as this
# will prevent the need for merging in future updates.

pathmunge () {
    case ":${PATH}:" in
        *:"$1":*)
            ;;
        *)
            if [ "$2" = "after" ] ; then
                PATH=$PATH:$1
            else
                PATH=$1:$PATH
            fi
    esac
}


if [ -x /usr/bin/id ]; then
    if [ -z "$EUID" ]; then
        # ksh workaround
        EUID=`id -u`
        UID=`id -ru`
    fi
    USER="`id -un`"
    LOGNAME=$USER
    MAIL="/var/spool/mail/$USER"
fi

# Path manipulation
if [ "$EUID" = "0" ]; then
    pathmunge /sbin
    pathmunge /usr/sbin
    pathmunge /usr/local/sbin
else
    pathmunge /usr/local/sbin after
    pathmunge /usr/sbin after
    pathmunge /sbin after
fi

HOSTNAME=`/bin/hostname 2>/dev/null`
HISTSIZE=1000
if [ "$HISTCONTROL" = "ignorespace" ] ; then
    export HISTCONTROL=ignoreboth
else
    export HISTCONTROL=ignoredups
fi

export PATH USER LOGNAME MAIL HOSTNAME HISTSIZE HISTCONTROL

# By default, we want umask to get set. This sets it for login shell
# Current threshold for system reserved uid/gids is 200
# You could check uidgid reservation validity in
# /usr/share/doc/setup-*/uidgid file
if [ $UID -gt 199 ] && [ "`id -gn`" = "`id -un`" ]; then
    umask 002
else
    umask 022
fi

for i in /etc/profile.d/*.sh ; do
    if [ -r "$i" ]; then
        if [ "${-#*i}" != "$-" ]; then
            . "$i"
        else
            . "$i" >/dev/null 2>&1
        fi
    fi
done

unset i
unset -f pathmunge

(2)~/.bash_profile 文件内容:
# .bash_profile

# Get the aliases and functions
if [ -f ~/.bashrc ]; then
. ~/.bashrc
fi

# User specific environment and startup programs

PATH=$PATH:$HOME/bin

export PATH

(3)~/.bashrc 文件内容:
# .bashrc

# Source global definitions
if [ -f /etc/bashrc ]; then
. /etc/bashrc
fi

# User specific aliases and functions

(4)/etc/bashrc 文件内容:
# /etc/bashrc

# System wide functions and aliases
# Environment stuff goes in /etc/profile

# It's NOT a good idea to change this file unless you know what you
# are doing. It's much better to create a custom.sh shell script in
# /etc/profile.d/ to make custom changes to your environment, as this
# will prevent the need for merging in future updates.

# are we an interactive shell?
if [ "$PS1" ]; then
  if [ -z "$PROMPT_COMMAND" ]; then
    case $TERM in
    xterm*)
        if [ -e /etc/sysconfig/bash-prompt-xterm ]; then
            PROMPT_COMMAND=/etc/sysconfig/bash-prompt-xterm
        else
            PROMPT_COMMAND='printf "\033]0;%s@%s:%s\007" "${USER}" "${HOSTNAME%%.*}" "${PWD/#$HOME/~}"'
        fi
        ;;
    screen)
        if [ -e /etc/sysconfig/bash-prompt-screen ]; then
            PROMPT_COMMAND=/etc/sysconfig/bash-prompt-screen
        else
            PROMPT_COMMAND='printf "\033]0;%s@%s:%s\033\\" "${USER}" "${HOSTNAME%%.*}" "${PWD/#$HOME/~}"'
        fi
        ;;
    *)
        [ -e /etc/sysconfig/bash-prompt-default ] && PROMPT_COMMAND=/etc/sysconfig/bash-prompt-default
        ;;
      esac
  fi
  # Turn on checkwinsize
  shopt -s checkwinsize
  [ "$PS1" = "\\s-\\v\\\$ " ] && PS1="[\u@\h \W]\\$ "
  # You might want to have e.g. tty in prompt (e.g. more virtual machines)
  # and console windows
  # If you want to do so, just add e.g.
  # if [ "$PS1" ]; then
  #   PS1="[\u@\h:\l \W]\\$ "
  # fi
  # to your custom modification shell script in /etc/profile.d/ directory
fi

if ! shopt -q login_shell ; then # We're not a login shell
    # Need to redefine pathmunge, it get's undefined at the end of /etc/profile
    pathmunge () {
        case ":${PATH}:" in
            *:"$1":*)
                ;;
            *)
                if [ "$2" = "after" ] ; then
                    PATH=$PATH:$1
                else
                    PATH=$1:$PATH
                fi
        esac
    }

    # By default, we want umask to get set. This sets it for non-login shell.
    # Current threshold for system reserved uid/gids is 200
    # You could check uidgid reservation validity in
    # /usr/share/doc/setup-*/uidgid file
    if [ $UID -gt 199 ] && [ "`id -gn`" = "`id -un`" ]; then
       umask 002
    else
       umask 022
    fi

    # Only display echos from profile.d scripts if we are no login shell
    # and interactive - otherwise just process them to set envvars
    for i in /etc/profile.d/*.sh; do
        if [ -r "$i" ]; then
            if [ "$PS1" ]; then
                . "$i"
            else
                . "$i" >/dev/null 2>&1
            fi
        fi
    done

    unset i
    unset pathmunge
fi
# vim:ts=4:sw=4

2. 交互式非登录 shell
如何得到一个交互式非登录 shell ? 有两种最简单的方式:
(1)在图形界面下,打开一个终端窗口。
(2)在登录shell提示符下再输入bash命令。
以这样的方式得到的互式非登录 shell ,初始化过程是这样的:~/.bashrc—>/etc/bashrc。
这里特别要强调的是,交互式非登录 shell没有执行/etc/profile和~/.bash_profile,但是继承了登录 shell的初始化设置(因为毕竟第一次是登录进来的嘛)。

3. 为什么登录shell和非登录shell的初始化过程不一样呢?
从字符终端或者远程登录得到的登录shell是该用户的所有其它进程的父进程,也是其它子shell的父进程,所以环境变量在登录shell的启动脚本里设置一次就可以自动带到其它非登录shell里。
而非登录shell的本地变量、函数、alias等设置没有办法带到子shell里,需要每次启动非登录shell时再设置一遍。

结论:
(1)在 /etc/proflie 中设置全局环境变量,对所有用户生效,只在用户登录时执行一次。
(2)在 ~/.bash_profile 中设置本地环境变量,对单一用户生效,只在用户登录时执行一次。
~/.bash_profile和/etc/profile的关系可以类比Windows下的用户环境变量设置和系统环境变量设置。
(3)在 ~/.bashrc 中设置本地变量、函数、alias等,对单一用户生效,在用户登录或打开新shell时执行。
(4)/etc/bashrc 在用户登录或打开新shell时执行。
(5)~.bash_logout 在用户退出shell时被执行。

参考文献:
1. http://blog.csdn.net/ctthuangcheng/article/details/17161463
2. http://blog.csdn.net/guosha/article/details/3334477
3. http://www.linuxde.net/2013/10/15352.html
4. http://blog.csdn.net/chlstar/article/details/7170601
5. http://linux.chinaitlab.com/administer/929819.html

Linux_058:常用命令之十九:umask

环境:RHEL 6.5

以普通用户身份登录,然后
(1)使用touch命令创建一个文件时,默认的权限是这样的:
-rw-rw-r-- 1 maping maping    0 5月  25 11:10 2.txt
(2)使用mkdir命令创建一个目录时,默认的权限是这样的:
drwxrwxr-x 2 maping maping 4096 5月  25 11:11 folder1

有没有想过,这是为什么呢?
答案就在umask,umask 可以指定目前使用者在创建文件或目录权限预设值。

运行 umask,你可以看到当前用户的预设值,普通用户一般为:0002。
我们先只看后三位:002,规则是这样的:
(1)创建文件时,默认权限 = 666-umask 的数值,即664。
这也就是为什么普通用户创建文件后,默认权限是rw-rw-r--的原因。
(2)创建目录时,默认权限 = 777-umask 的数值,即775。
这也就是为什么普通用户创建目录后,默认权限是rwxrwxr-x的原因。

以 root 用户身份登录,然后
(1)使用touch命令创建一个文件时,默认的权限是这样的:
-rw-r--r-- 1 root   root      0 5月  25 11:31 3.txt
(2)使用mkdir命令创建一个目录时,默认的权限是这样的:
drwxr-xr-x 2 root   root   4096 5月  25 11:32 folder2
运行umask,你可以看到root用户的预设值为:0022。
使用上面的规则,我们同样可以得出为什么root用户默认的权限是这样的。
因为写权限是比较重要的权限,所以对与root用户同组的用户和其他用户都拿掉了写权限。

系统默认的 umask 定义在 /etc/profile 和 /etc/bashrc 文件中,如果想要umask设置永久有效,这两个文件都要修改。
比如:禁止其他用户有任何权限,可以设置 umask = 007。
普通用户可以覆盖系统的umask设置,方法是修改 ~/.bash_profile 或 ~/.bashrc 文件中的 umask 值。 

参考文献:
1. http://billie66.github.io/TLCL/book/zh/chap10.html
2. http://linux.vbird.org/linux_basic/0220filemanager/0220filemanager.php
3. http://blog.csdn.net/lmh12506/article/details/7281910

Linux_057:常用命令之十八:chgrp

chgrp命令可以改变文件或目录的属组,使用权限一般是超级用户。
要被改变的组名必须要在/etc/group文件中存在才行。

命令格式:chgrp [选项] [组] [文件]

常用命令:

1. chgrp  bin log2012.log
把“log2012.log” 的属组更改为 bin。

2. chgrp --reference=log2012.log log2013.log
改变文件log2013.log 的属组,使得文件log2013.log的属组和参考文件log2012.log的属组相同。

3. chgrp -R bin test6
把test6目录及其子目录下的所有文件的属组更改为bin。

参考文献:
1. http://www.cnblogs.com/peida/archive/2012/12/03/2799003.html

2014年5月24日星期六

Linux_056:常用命令之二十一:chown

chown命令将指定文件的属主或属组改为指定的用户或组。
用户可以是用户名或者用户ID;组可以是组名或者组ID;文件是以空格分开的要改变权限的文件列表,支持通配符。
系统管理员经常使用chown命令,在将文件拷贝到另一个用户的名录下之后,让用户拥有使用该文件的权限。

命令格式:chown [选项]... [属主][:[组]] 文件...

常用例子:

1. chown mail:mail log2012.log
改变文件的属主和属组为mail。

2. chown root:log2012.log
改变文件的属主和属组为root。
注意,如果只想改变文件的属主,要把冒号去掉。

3. chown :mail log2012.log
改变文件的属组为mail。

4. chown -R -v root:mail test6
改变目录以及子目录的属主和属组。

参考文献:
1. http://www.cnblogs.com/peida/archive/2012/12/04/2800684.html

Linux_055:常用命令之二十:chmod

chmod命令用于改变linux系统文件或目录的访问权限。
通过《常用命令之十三:ls》,我们知道两个基本事实:
(1)有三种不同类型的用户可对文件访问:文件属主,与属主同组的用户、其他用户。
(2)文件的访问权限分为三组,每组用三位表示,分别为文件属主的读、写和执行权限;与属主同组的用户的读、写和执行权限;其他用户的读、写和执行权限。
以文件为例,
(1)读权限表示只允许读其内容,禁止对其做任何的更改操作,但可以复制这个文件。
(2)写权限一般是在具有读权限之后才能体现作用的,同时具有读和写权限,可以对文件进行编辑。
如果只有写权限而无读权限,则只能对文件增加数据,而不能浏览文件内容。
(3)执行权限表示允许将该文件作为一个程序执行。
文件被创建时,文件属主自动拥有对该文件的读、写和执行权限,以便于对文件的阅读和修改。

chmod命令有两种用法:
(1)包含字母和操作符表达式的文字设定法。
字母“u”代表文件的属主,“g”代表与属主同组的用户,“o”代表其他用户,“a”代表所有用户。
字母“r”代表读权限,“w”代表写权限,“x”代表执行权限。
字母“+”代表增加(权限),“-”代表删除(权限),“=”代表等于(权限)。
(2)包含数字的数字设定法。
数值“4”代表读权限,“2”代表写权限,“1”代表执行权限,而“0”代表无任何权限。

常用例子:

1. chmod a+x log2012.log
给所有用户增加执行权限。

2. chmod ug+w,o-x log2012.log
同时修改不同用户权限:给文件属主和与属主同组的用户增加写权限,同时删除其他用户的执行权限。

3. chmod u=x log2012.log
使用“=”设置权限:撤销文件属主原来所有的权限,然后使文件属主具有执行权限。

4. chmod -R u+x test4
对一个目录及其子目录所有文件添加权限:给test4目录及其子目录中所有文件的属主增加执行权限。

5. chmod 751 file   
给file的属主分配读、写、执行(7)的权限,给file的所在组分配读、执行(5)的权限,给其他用户分配执行(1)的权限。

6. 为所有用户分配读权限
(1)chmod =r file
(2)chmod 444 file
(3)chmod a-wx,a+r file

参考文献:
1. http://www.cnblogs.com/peida/archive/2012/11/29/2794010.html

Linux_054:常用命令之十九:service

上文介绍的chkconfig命令可以快速地设置开机时需要自动启动的服务,但是我想马上启动/停止服务,应该怎么办呢?
答案是使用service命令。

位于/etc/init.d/目录下的服务可以使用如下方式启动/停止:
(1)/etc/init.d/mysql start/stop
(2)service mysql start/stop
有些服务还支持restart,比如
(1)/etc/init.d/mysql restart
(2)service mysql restart
要想查看某个服务支持哪些参数,可以使用:service [服务名]。
比如:service httpd,输出如下:
Usage: httpd {start|stop|restart|condrestart|try-restart|force-reload|reload|status|fullstatus|graceful|help|configtest}

参考文献:
1. http://my.oschina.net/phptiger86/blog/137656

Linux_053:常用命令之十八:chkconfig

上一篇文章介绍了RHEL系统启动过程,那么怎样让某些服务开机自启动呢?
答案就是使用chkconfig命令。

chkconfig可以用来设置(启动/停止/查询)系统服务的运行级别信息。
注意,chkconfig并不是立即启动/停止一个服务,它只是简单的改变了符号链接。
命令格式:
(1)chkconfig [--add][--del][--list][系统服务]
(2)chkconfig [--level 等级代号][系统服务][on/off/reset]

常用命令介绍:

1. chkconfig —list nfs
查看nfs服务在各个运行级别上的开关状态。

2. chkconfig --level 35 nfs on
在运行级别3和5上开机自启动nfs服务。

3. chkconfig --add httpd
增加httpd服务,前提是httpd服务的脚本在/etc/init.d/目录下已经存在。
add操作只是在当前运行级别下,新增一个httpd符号链接。

4. chkconfig --del httpd        
删除httpd服务,前提是httpd服务的脚本在/etc/init.d/目录下已经存在。
del操作只是在当前运行级别下,删除原有的httpd符号链接。

5. 如何增加一个新服务(/etc/init.d目录下原来没有的服务)?
以glassfish4为例,
(1)glassfish4服务脚本必须存放在/etc/ini.d/目录下。
(2)给脚本增加执行权限:chmod +x /etc/ini.d/glassfish4。
(3)chkconfig --add glassfish4
    增加此服务,此时服务会被在/etc/rc.d/rcN.d中赋予K/S入口了。
(4)chkconfig --level 35 glassfish4 on
    修改服务的默认启动等级。
具体步骤请参考《CentOS6.5下开机自启动GlassFish4》。

6. 关闭不需要的服务
(1)如果没有打印机:chkconfig --level 235 cups off
(2)如果没有局域网:chkconfig --level 235 smb off
(3)如果不需要远程用户登录:chkconfig --level 235 sshd off
(4)如果不需要定时任务:chkconfig --level 235 crond off
(5)如果不需要添加新硬件:chkconfig --level 235 kudzu off

参考文献:
1. http://www.cnblogs.com/panjun-Donet/archive/2010/08/10/1796873.html

Linux_052:RHEL系统启动过程

环境: RHEL 6.5

RHEL完整的系统启动过程如下图:

本文只从init进程说起,前面的启动过程以后另文专述。

因为init是第一个运行程序,所以它的PID永远是1,可以用top命令查看。

init进程启动后,会读取/etc/inittab文件,并根据其内容按照顺序创建进程,文件内容如下:
# inittab is only used by upstart for the default runlevel.
#
# ADDING OTHER CONFIGURATION HERE WILL HAVE NO EFFECT ON YOUR SYSTEM.
#
# System initialization is started by /etc/init/rcS.conf
#
# Individual runlevels are started by /etc/init/rc.conf
#
# Ctrl-Alt-Delete is handled by /etc/init/control-alt-delete.conf
#
# Terminal gettys are handled by /etc/init/tty.conf and /etc/init/serial.conf,
# with configuration in /etc/sysconfig/init.
#
# For information on how to write upstart event handlers, or how
# upstart works, see init(5), init(8), and initctl(8).
#
# Default runlevel. The runlevels used are:
#   0 - halt (Do NOT set initdefault to this)
#   1 - Single user mode
#   2 - Multiuser, without NFS (The same as 3, if you do not have networking)
#   3 - Full multiuser mode
#   4 - unused
#   5 - X11
#   6 - reboot (Do NOT set initdefault to this)
#
id:5:initdefault:

系统一共有7个运行级别,从0~6,在注释部分对这7个运行级别做了描述。
0 - 关机(请不要切换到此等级)
1 - 单用户模式的文字界面
2 - 多用户模式的文字界面,不具有网络档案系统(NFS)功能
3 - 多用户模式的文字界面,具有网络档案系统(NFS)功能
4 - 未使用
5 - X11
6 - 重新启动(请不要切换到此等级)

这个文件的最后一行:id:5:initdefault:,告诉我们系统的当前运行级别是5。

init程序具体执行过程是这样的:
(1)系统开始初始化,执行脚本:/etc/init/rcS.conf,该脚本会再执行脚本:/etc/rc.d/rc.sysinit。
(2)接着会根据系统的运行级别来运行对应目录里的脚本,以启动相关服务。比如,这里的运行级别是5,init就根据这个级别号运行/etc/rc.d/rc5.d目录里的脚本。
在/etc/rc.d/目录下,有3个普通文件(rc、rc.local、rc.sysinit)和8个目录文件(init.d,rc0.d,rc1.d,rc2.d,rc3.d,rc4.d,rc5.d,rc6.d)。
其中,init.d目录中存放了各种程序和脚本,而rc0.d~rc6.d这7个目录里的文件都是init.d目录里程序文件的链接文件,只是被赋于了相应的运行级别。
(3)以 rc5.d目录中的脚本为例,目录中的所有文件都是以K或S开头(S即start,开启服务;K即kill,关闭服务),然后跟一个数字,然后是文件名。
系统运行级别N确定以后,进入相应的rcN.d目录,执行以S开头的文件以启用相关服务进程,而且是按照S后面的那个数字的顺序依次执行;退出系统时,就依次执行以K开头的文件关闭相关服务。
在rc5.d目录中看以看到,K60nfs表示nfs服务默认是关闭的,关闭顺序是60;S11portreserve表示portreserve服务是开启的,开启顺序是11。
需要说明的是,对于每个运行等级,每个服务只能有一个启动脚本或者停止脚本。
当切换运行等级时,init不会重新启动已经启动的服务,也不会再次去停止已经停止的服务。
(4)运行级别是2、3、5的时候,执行完对应的rcN.d目录中的脚本后,最后都会执行rc.local。
也就是说,rc.local是初始化的最后一个执行脚本。
如果你想增加自己要执行的脚本,可以加在rc.local中。

那么,某个服务默认定义在哪个运行级别?默认开启和关闭的顺序是怎样的?
答案就在每个服务的脚本自身之中。
前面说过,rc0.d~rc6.d这7个目录里的文件都是init.d目录里程序文件的链接文件,所以去看一下init.d目录下这些脚本吧。
(1)以nfs服务为例:cat /etc/init.d/nfs,可以看到:# chkconfig: - 30 60。
这表示nfs服务不在任何运行级别上启用(服务不在任何运行级别上启用时,运行级别那个字段用-),启用顺序为第30,关闭顺序为第60。
(2)以portreserve服务为例:cat /etc/init.d/portreserve,可以看到:# chkconfig: 2345 11 89
这表示portreserve服务在运行级别2、3、4、5上启用,启用顺序为第1,关闭顺序为第89。

参考文献:
1. http://share.blog.51cto.com/278008/244106
2. http://blog.csdn.net/wjx515628/article/details/10001623
3. http://www.jb51.net/article/3567.htm

2014年5月23日星期五

MAC_014:安装MacPorts

环境:MAC OS X:10.9.3

MacPorts是另一个跟Homebrew有同样类似功能的软件。
为什么安装MacPorts?因为不是所有的软件都能用Homebrew找到。

MacPorts官网:http://www.macports.org/。

虽然有编译好的包,但这一次我想下载源码包,然后编译安装MacPorts。

1. 下载
wget https://distfiles.macports.org/MacPorts/MacPorts-2.2.1.tar.gz

2. 解压
tar zxvf MacPorts-2.1.1.tar.gz

3. 编译
cd MacPorts-2.2.1
./configure、make、sudo make install 是编译三部曲,这里在一行执行这三个命令。
./configure && make && sudo make install
在一行上执行多个Linux命令可以用;、&&、||,其中
(1)cmd1 ; cmd2  表示 cmd1 和 cmd2 都会被执行。
(2)cmd1 && cmd2 表示如果 cmd1 执行成功才执行 cmd2。
(3)cmd1 || cmd2 表示如果 cmd1 执行失败才执行 cmd2。

编译成功的话,会显示:
Congratulations, you have successfully installed the MacPorts system. To get the Portfiles and update the system, add /opt/local/bin to your PATH and run:

sudo port -v selfupdate

Please read "man port", the MacPorts guide at http://guide.macports.org/ and Wiki at https://trac.macports.org/ for full documentation.

4. 编辑/etc/profile文件,修改$PATH

export PATH=$JAVA_HOME/bin:/opt/local/bin:/opt/local/sbin:$PATH

5. 执行:sudo port -v selfupdate,进行自我更新
在下载ports.tar时,似乎下不动了:
Synchronizing local ports tree from rsync://rsync.macports.org/release/tarballs/ports.tar
receiving file list ... done
ports.tar
原来是被墙了,运行:sudo vi /opt/local/etc/macports/sources.conf
注释最后一行,并增加一行:
#rsync://rsync.macports.org/release/tarballs/ports.tar [default]
https://distfiles.macports.org/ports.tar.gz [default]

重新运行:sudo port -v selfupdate,进度条出现了,
我去!居然需要两个小时的等待。

参考文献:
1. http://www.ccvita.com/434.html
2. http://blog.csdn.net/chunfengd/article/details/8536336
3. http://trac.macports.org/wiki/howto/PortTreeTarball

MAC_013:安装Homebrew

环境:MAC OS X 10.9.3

Homebrew之于MAC,就相当于yum之于RHEL。

Homebrew官网:http://brew.sh/index_zh-cn.html。

1. 安装
只需在终端中执行:ruby -e "$(curl -fsSL https://raw.github.com/Homebrew/homebrew/go/install)"
如果找不到上面的地址,试试这个:ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

输出如下:

==> This script will install:
/usr/local/bin/brew
/usr/local/Library/...
/usr/local/share/man/man1/brew.1
==> The following directories will be made group writable:
/usr/local/.
/usr/local/bin
/usr/local/etc
/usr/local/share
/usr/local/share/man
/usr/local/share/man/man1
/usr/local/share/info
==> The following directories will have their group set to admin:
/usr/local/.
/usr/local/bin
/usr/local/etc
/usr/local/share
/usr/local/share/man
/usr/local/share/man/man1
/usr/local/share/info

Press RETURN to continue or any other key to abort
按下回车键,开始下载并安装,这个过程有些慢,耐心等待下。

==> /usr/bin/sudo /bin/chmod g+rwx /usr/local/. /usr/local/bin /usr/local/etc /usr/local/share /usr/local/share/man /usr/local/share/man/man1 /usr/local/share/info
Password:
==> /usr/bin/sudo /usr/bin/chgrp admin /usr/local/. /usr/local/bin /usr/local/etc /usr/local/share /usr/local/share/man /usr/local/share/man/man1 /usr/local/share/info
==> /usr/bin/sudo /bin/mkdir /Library/Caches/Homebrew
==> /usr/bin/sudo /bin/chmod g+rwx /Library/Caches/Homebrew
==> Downloading and installing Homebrew...
remote: Counting objects: 174662, done.
remote: Compressing objects: 100% (48636/48636), done.
remote: Total 174662 (delta 124888), reused 174648 (delta 124877)
Receiving objects: 100% (174662/174662), 33.81 MiB | 9.00 KiB/s, done.
Resolving deltas: 100% (124888/124888), done.
From https://github.com/Homebrew/homebrew
 * [new branch]      master     -> origin/master
HEAD is now at f550366 opengrm-ngram 1.2.1
==> Installation successful!
==> Next steps

Run `brew doctor` before you install anything

Run `brew help` to get started

2. 使用brew安装软件
比如安装tomcat,在终端输入:brew install tomcat
==> Downloading http://www.apache.org/dyn/closer.cgi?path=tomcat/tomcat-7/v7.0.5
==> Best Mirror http://mirrors.hust.edu.cn/apache/tomcat/tomcat-7/v7.0.53/bin/ap
######################################################################## 100.0%
/usr/local/Cellar/tomcat/7.0.53: 623 files, 14M, built in 4 seconds

可以看到tomcat被安装到了/usr/local/Cellar/目录下。

运行: ls -l bin | grep tomcat,发现bin目录中增加了一个catalina命令,并且指向了tomcat安装目录下的catalina。
lrwxr-xr-x  1 maping  admin      36  5 23 18:25 catalina -> ../Cellar/tomcat/7.0.53/bin/catalina

运行:catalina run,tomcat就启动了。

3. 使用brew卸载软件
运行:brew uninstall tomcat
Uninstalling /usr/local/Cellar/tomcat/7.0.53...

怎么样,使用brew安装和卸载软件很简单吧,你也来试试吧。

参考文献:
1. http://snowolf.iteye.com/blog/774312
2. http://ksmx.me/blog/2013/10/05/homebrew-cask-cli-workflow-to-install-mac-applications/

Linux_050:常用命令之十六:yum

环境:RHEL 7

1. 搜索软件包
(1)yum search KEYWORD 搜索在 name 和 summary 字段中含有 KEYWORD 的包。
(2)yum search all KEYWORD 搜索在 name、summary 和 description 字段中含有 KEYWORD 的包。
例如:# yum search all 'web server'
(3)yum info PACKAGENAME 查看一个软件包的信息
例如:# yum info httpd
(4)yum groupinfo GROUPNAME 查看一个软件包组的信息
例如:# yum groupinfo "Identity Management Server"
输出结果中 PACKAGE 的 Marker 信息含义如下:
= 软件包已安装,作为 group 的一部分被安装。
+ 软件包未安装,但如果 group 被安装或升级,那么此软件包也会被安装。
- 软件包未安装,即使 group 被安装或升级,此软件包也不会被安装。
  软件包已安装,但不是作为 group 的一部分被安装。
(5)yum list 列出所有已安装和仓库中可用的软件包
(6)yum grouplist 列出所有已安装和仓库中可用的软件包组
(7)yum list PACKAGENAME 列出包含指定信息的软件包
(8)yum list available 列出仓库中所有可用的软件包
(9)yum list updates 列出仓库中比当前系统更新的软件包
(10)yum list recent 列出新加入仓库的软件包
(11)yum list installed 列出已安装的软件包
(12)yum list extras 列出不是通过软件仓库安装的软件包
(13)yum list '*http*' 列出标题包含 http 的软件包
(14)yum provides PATHNAME 显示安装到指定目录位置的包信息。
例如:# yum provides /var/www/html
(15)yum whatprovides 命令 根据命令反查是哪个软件包安装的这个命令
例如:# yum whatprovides */ip
输出结果显示:提供 ip 命令的是 iproute 软件包。

2. 安装软件包
(1)yum install PACKAGENAME 或 yum -y install PACKAGENAME(支持*) 安装软件包,包括依赖包
-y :表示自动选择 y,全自动获取并安装软件包。
例如:# yum install httpd
(2)yum localinstall PACKAGENAME 从本机目录安装软件包
(3)yum groupinstall GROUPNAME 安装软件包组,包括 mandatory 和 default 软件包。
(4)yum group mark install GROUPNAME // TODO

3. 更新软件包
(1)yum update 升级所有软件包
(2)yum update PACKAGENAME 升级指定软件包
(3)yum update kernel 升级 kernal
由于升级 kernal 必须要重启机器,所以 kernal 包被特别设计,允许按照多个 kernal 版本。
这样,万一新升级的 kernal 有问题,可以切换回旧的 kernal。 // TODO 怎么切回旧的 kernal
yum list kernel 显示所有已安装的和可获得的 kernals。
查看当前使用的 kernal 版本,需要使用 uname 命令,-r 只显示 kernal 版本和 release 信息,-a 显示所有信息。
(4)yum check-update 列出所有可更新的软件包
(5)yum list updates 列出所有可以更新的软件包
(6)yum list updates mysql* 查找 mysql 的更新
(7)yum groupupdate GROUPNAME 更新某个组件的所有软件包

4. 删除软件包
(1)yum remove PACKAGENAME 卸载软件包。
注意,yum remove 将会卸载指定的软件包,以及相关的软件包,因此可能会导致其它软件工作不正常,使用时,请小心确认。
(2)yum groupremove GROUPNAME 删除某个组件的全部软件包

5. 清除软件包
(1)yum clean packages 清除遗留在缓存里的包文件。
(2)yum clean metadata 清除遗留在缓存里的元数据。
(3)yum clean headers 清除遗留在缓存里的头文件。
(4)yum clean all 清除包文件,元数据,头文件。

6. 查看安装日志
yum 安装的日志文件是 /var/log/yum.log
# tail -5 /var/log/yum.log
查看 yum 安装事务历史
# yum history
回滚指定的 yum 安装事务
# yum history undo 6

2014年5月22日星期四

Linux_049:RHEL快捷键

环境:RHEL 6.5

1. 界面快捷键
(1)ALT + F1:打开gnome 的应用程序
(2)ALT + F2:打开运行应用程序窗口
(3)ALT+F4:关闭窗口
(4)ALT+F5:取消最大窗口
(5)ALT+F7:移动窗口
(6)ATL+F8:改变大小
(7)ALT+F9:最小化当前窗口
(8)ALT+F10:在当前窗口大小和最大化当前窗口之间切换
(9)ALT + TAB:切换打开程序
(10)ALT + Print:当前窗口抓图
(11)ALT + CTRL + L:启动屏幕保护程序
(12)ATL + CTRL + D:显示桌面
(13)CRL+SHIFT+N:新建文件夹
(14)CTRL+X:剪切
(15)CTRL+C:复制
(16)CTRL+V:粘贴
(17)CTRL+Z:撤销

2. 终端快捷键
(1)CTRL+SHIFT + N :新建一个终端窗口
(2)CTRL+SHIFT + T :新建一个终端标签页
(3)CTRL + SHIFT + W:关闭终端标签页
(4)CTRL+SHIFT + Q :关闭终端窗口
(5)ALT + 1:切换到标签页1
(6)ALT + 2:切换到标签页2
(7)ALT + 3:切换到标签页3
(8)CTRL+SHIFT + C :复制
(9)CTRL+SHIFT + V :粘贴
(10)F11 :全屏
(11)CTRL + SHIFT + = :字体放大
(12)CTRL + -:字体缩小
(13)CTRL + 0:回到字体原始大小
(14)选中你要复制的内容,然后按下 Shift + Insert,会把内容复制到当前光标处。
(15)选中你要复制的内容,然后按下 鼠标中键,会把内容复制到当前光标处。

3. 终端命令编辑快捷键
(1)CTRL+U:删除光标左边的所有字符
(2)CTRL+K:删除光标右边的所有字符
(3)CTRL+A:把光标移动到字符之首
(4)CTRL+E:把光标移动到字符尾部
(5)CTRL+W:删除光标左边的单词
(6)CTRL+D:删除光标后的单个字符
(7)CTRL+H:删除光标前的一个字符

Linux_048:RHEL使用iso镜像文件配置本地yum源

环境:MAC OS X 10.9.3 + 安装在VirtualBox 4.3.12 中的 RHEL 6.5

问题的起因是我想在主机(MAC)和客机(RHEL)之间共享文件夹。
共享文件夹需要安装Virtual Guest CD image,该文件位于:/Applications/VirtualBox.app/Contents/MacOS/VBoxGuestAdditions.iso。
VirtualBox会自动挂载它,如果没有自动挂载,你可以手动挂载,方式如下:
在RHEL虚机的存储选项设置中增加一个IDE,指向该iso文件。

1. 进入挂载目录:/media/VBOXADDITIONS_4.3.12_93733,执行./VBoxLinuxAdditions.run 
运行后,会报出如下错误:
Building the VirtualBox Guest Additions kernel modules
The gcc utility was not found. If the following module compilation fails then
this could be the reason and you should try installing it.
很明显,是因为没有安装gcc工具。
我本地已经有了RHEL6.5的iso镜像文件,为了节省时间,我把该iso镜像文件配置为本地yum源。

2. 进入/etc/yum.repos.d/,生成本地的yum源文件
(1)首先备份原有的rhel-source.repo:cp rhel-source.repo rhel-source.repo.origin

(2)然后再生成新的rhel-iso.repo:cp rhel-source.repo rhel-iso.repo,并修改内容如下:
[rhel-iso]
name=Red Hat Enterprise Linux $releasever - $basearch - Source
baseurl=file:///media/Server
enabled=1
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release
参数项目说明:
(a)[rhel-iso] :yum源的名字,做到全局唯一不重复 。
(b)name:名称,可以自定义。
(c)baseurl:yum源的路径,支持三种协议:http、ftp、file,其中file表示本地文件,/media/Server是真实路径。
(d)enable:是否启动本yum源,1表示启用;0表示禁用。
(e)gpgcheck:是否进行指纹校验,1表示校验;0表示不校验。

(3)在media目录下,创建Server目录,并执行:cp -r RHEL_6.5\ x86_64\ Disc\ 1/* ./Server
这样做的目的是因为默认挂载的RHEL iso镜像有很多空格,写在配置文件里会报错。
为了简单起见,我把其中的内容全部复制到Server目录下。

3. 运行:yum grouplist,验证本地yum源起作用了。
如果配置正确的话,会输出(我的语言环境是中文的):
Installed Groups:
   Java 平台
   NFS 文件服务器
   Perl 支持
   SNMP 支持
   X 窗口系统
   万维网服务器
   互联网浏览器
   图形管理工具
   基本
   字体
   安全性工具
   性能工具
   打印客户端
   打印服务器
   拨号网络支持
   服务器平台
   桌面
   桌面平台
   桌面调试和运行工具
   电子邮件服务器
   目录客户端
   硬件监控工具
   继承 UNIX 兼容性
   继承 X Windows 系统的兼容性
   网络基础设施服务器
   网络文件系统客户端
   联网工具
   调试工具
   输入法
   通用桌面
   附加开发

4. 运行:yum groupinstall "开发工具",安装包括gcc在内的开发工具。
这一步要花些时间,所有提示选项选择Y就OK了。

5. 好,现在可以重新运行:./VBoxLinuxAdditions.run。

一切正常的话,接下来就可以设置共享目录啦。

参考文献:
1. http://blog.csdn.net/justdb/article/details/9708049
2. http://blog.csdn.net/zhouhong1026/article/details/8176104
3. http://blog.itpub.net/25313300/viewspace-708509
4. http://www.linuxidc.com/Linux/2013-06/85971.htm
5. http://www.oschina.net/code/snippet_54100_10351

2014年5月21日星期三

Cloud_021:RAID基础知识

RAID(Redundant Array of Inexpensive Disks),廉价磁盘冗余阵列。
RAID使用多块物理硬盘组成一个具有加速读写、自动备份、数据损毁恢复等功能的逻辑硬盘。
为了满足不同工作环境的需要,RAID技术分为了以下RAID 0-7计合8种,每种阵列都各自有其自身优点与缺点。

1. RAID 0
RAID 0 是数据分条(Data Stripping)阵列,其主要的特点是存取的数据都被分割成为条状(stripped)分布存放在各个物理磁盘上。
优点:并行存取,从而获得双倍或多倍存取速度。
其中最简单的RAID 0阵列,使用两块硬盘提供双倍传输速度,假如阵列卡能支持多块硬盘组成RAID 0,那么则可以获得N倍(N为加入阵列的硬盘数量)传输速度。
缺点:数据安全比较脆弱,只要阵列内某一硬盘出现故障,所有的数据将全部丢失。
因此,RAID 0 不可应用于需要数据高可用性的关键应用。
为了在数据脆弱性与速度之间取得较好的平衡,实际使用时RAID 0通常只使用两块硬盘,获得双倍传输速度同时稳定性下降一半,用于存放视频点播文件、临时文件等对安全性要求不高的数据。
 2. RAID 1
RAID 1 是数据镜像(Data Mirror)阵列,其主要特点在提供了较为优异的数据安全保障。
整个阵列至少需要两块硬盘组建,在写入时同时将数据备份至另一块硬盘,所以即使其中一块硬盘出现故障而造成数据损坏时,文件也不会丢失。
但是其代价就是的阵列内半数硬盘用于即时镜像备份,容量为阵列内硬盘总容量的一半,而且速度没有任何提升。
因此,RAID1虽然不能提高存储性能,但由于其具有的高数据安全性,使其尤其适用于存放重要数据,如服务器和数据库存储等领域。


3. RAID 0+1
RAID 0+1 把RAID 0的快速存取特点与RAID 1数据安全的优点结合起来,先做分条,再做镜像。
这种阵列最少需要4个硬盘才能创建,其中两块硬盘用来存取数据(分条并行存取),其余两块硬盘用于镜像前面两块硬盘的数据。
以4块硬盘(Disk1~4)为例,当Disk1损坏时,在剩下的3块盘中,只要Disk3或Disk4两个硬盘中任何一个损坏,都会导致整个RAID失效,因此故障率为2/3。
具体原因分析:Disk1坏了,此时无法从Disk1和Disk2组成的RAID0上存取数据,只能从Disk3和Disk4构成的RAID0上存取数据,所以如果Disk3或Disk4也坏了,那么整个RAID也就失效了。

4. RAD 1+0
RAID 1+0 把RAID 1数据安全与RAID 0的快速存取特点的优点结合起来,先做镜像,再做分条。
以4块硬盘(Disk1~4)为例,当Disk1损坏时,在剩下的3块盘中,只有当Disk2一个盘发生故障时,才会导致整个RAID失效,因此故障率为1/3。
具体原因分析:Disk1坏了,Disk2是Disk1的镜像,只能从Disk2上存取数据,所以如果Disk2也坏了,那么整个RAID也就失效了。


5. RAID 0+1与RAID 1+0的异同
在正常的情况下RAID 0+1和RAID 1+0是完全一样的,而且每一个读写操作所产生的I/O数量也是一样的,所以在读写性能上两者没什么区别。
当有磁盘出现故障时,比如前面假设的Disk1损坏时,安全性上RAID 0+1比RAID 1+0要差一些,读写性能RAID 0+1比RAID 1+0要强一些。
原因是当Disk1损坏时,RAID 0+1中Disk3和Disk4组成的RAID 0还有效;而RAID 1+0中Disk3和Disk4组成的RAID 1还有效。
RAID 0+1和RAID 1+0的特点使其特别适用于既有大量数据需要存取,同时又对数据安全性要求严格的领域,如银行、金融、商业超市、仓储库房、各种档案管理等。


6. RAID 3
RAID 3把数据分成多个“块”,按照一定的容错算法,存放在N+1个硬盘上,实际数据占用的有效空间为N个硬盘的空间总和,而第N+1个硬盘上存储的数据是校验容错信息,当这N+1个硬盘中的其中一个硬盘出现故障时,可以从其它N个硬盘中的数据也可以恢复原始数据。
由于RAID 3采用了一块物理硬盘专门来存放校验的数据,其它硬盘同时并行工作。
这样,不管是对哪一个硬盘的数据进行修改,校验的数据硬盘也会同时跟着改动,这就导致了校验硬盘工作负担过重,影响数据的存储效率。
对于那些经常需要执行大量写入操作的应用来说,校验盘的负载将会很大,无法满足程序的运行速度,从而导致整个RAID系统性能的下降。
鉴于这种原因,RAID 3更加适合应用于那些写入操作较少,读取操作较多的应用环境。

7. RAID 5
RAID 5也采用了校验的数据存储方式,所不同的是,RAID 5把校验数据也切成“块”存放在各个硬盘上,这样就可以解决校验数据存放的瓶颈问题。
由于RAID 5的阵列卡价格较为高昂,所以该种阵列目前仅在大型企业中使用。

8. RAID 1+0 与RAID 5的异同
以4块硬盘为例:
(1)安全性
RAID 1+0的安全性高于RAID 5。
具体原因分析:
当Disk1损坏时,对于RAID1+0,只有当Disk1对应的镜象盘Disk2也损坏时,才会导致整个RAID失效。
当Disk1损坏时,对于RAID5,剩下的3块盘中,任何一块盘损坏,都将导致RAID失效。
在恢复数据的时候,RAID 1+0的恢复速度也快于RAID 5。
(2)空间利用率
RAID 1+0的利用率是50%,RAID5的利用率是75%。
硬盘数量越多,RAID 5的空间利用率越高,因为RAID 5相当于只有1块物理硬盘做校验。
(3)读写性能
RAID1+0可供读取有效数据的磁盘是4个,RAID 5可供读取有效数据的磁盘也是4个(校验信息分布在所有的盘上),所以两者的读的性能应该是基本一致的。
在连续读写方面,RAID 5比RAID 1+0好一些。因为,RAID 5 可以同时写入3个数据+1个校验;而RAID 1+0只能同时写入2个数据+2个镜像。
在离散读写方面,RAID 1+0比RAID 5好一些。因为,同样的单个操作,RAID 5 需要4个IO,RAID 1+0 需要2个IO。

参考文献:
1. http://www.linuxidc.com/Linux/2012-02/53267.htm
2. http://diy.pconline.com.cn/cpu/study_cpu/1008/2197524.html
3. http://wenku.baidu.com/link?url=6qffQXzwGBp9tLddOOoWWLMw80LX_6JrYdJ-qpG1XpQhQPHDOYlNFOpgiWPeRkB2VwTft4F6bY0TeDke5FSA5UxDqQpp7kQOtLdWtEFyw0C
4. http://baike.baidu.com/view/1367898.htm?fr=aladdin
5. http://blog.csdn.net/dinglang_2009/article/details/7960598

2014年5月20日星期二

Linux_047:RHEL 6 忘了 root 密码怎么办?

环境:RHEL 6.5

你是否也有过忘记root密码的时候,长叹一声,然后只好无可奈何的重装。
其实,进入单用户模式可以重新设置root密码。

1. 重启RHEL,在4秒倒计时按下回车键,进入GRUB引导系统界面。

2. 按界面上的提示,按下a键,进入下一界面。

3. 按下一次空格键,然后输入1,这样就进入了单用户模式。

4. 现在你可以用passwd命令修改root密码了。

5. 重启,进入正常模式。


参考文献:
1. http://blog.sina.com.cn/s/blog_3fd1fa470100z457.html

VirtualBox_006:Oracle VM VirtualBox 使用指南之六:如何自动挂载U盘

环境:MAC OS X 10.9.3 (主机)+ RHEL 6.5(客机)

启动虚机后,选择菜单Devices->USB Devices,然后勾上你的U盘设备。
这时,客机应该能够自动挂载上你的U盘。
如果你的U盘选项是disable的状态(无法选择),说明你的U盘已经自动挂载到主机上了。
此时,在主机上右键点击U盘,然后选择“推出U盘”,注意,是推出,而不是拔下。
然后,客机就能自动识别并挂载你的U盘,如果不能自动挂载,你再勾上你的U盘设备。

另外说明一下卸载(umount)和弹出(eject)的区别,
(1)卸载是执行umount命令,卸载后,还可以使用用mount命令重新挂载。
此时,使用fdisk -l命令还能看到这个设备,比如:
Disk /dev/sdb: 16.0 GB, 15997075456 bytes
255 heads, 63 sectors/track, 1944 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0xcad4ebea

   Device Boot      Start         End      Blocks   Id  System
/dev/sdb4   *           1        1945    15622016    c  W95 FAT32 (LBA)

此时,使用mount /dev/sdb4 /home/maping/Test2,可以重新把该设备挂载到/home/maping/Test2目录下。

(2)弹出后,就不能使用mount命令在挂载了。
此时,使用fdisk -l命令就看不到这个设备了。

2014年5月19日星期一

EAP_004:修改系统时间后导致AJAX工作不正常的问题

环境:RHEL 6.3 + JBoss EAP 6.1

问题现象如下:
用户使用命令:date -s "2013-08-03 14:15:00",把RHEL系统时间调前了。
重新启动JBoss,访问应用,发现很多按钮和过滤功能都不正常了。
使用Firefox Debug发现,工作不正常的功能都是与AJAX有关的,POST过去的参数都莫名的丢掉了。

一度怀疑是因为源码文件时间比系统时间新,于是导致页面中的组件不断被刷新。
于是使用命令:find ./ -name "*.xhtml" -exec touch {} \; 更新所有源代码文件的时间。
但是问题依旧。

最后,终于从不起眼的日志中看到如下信息:
INFO [facelets.facelet] (http-localhost/127.0.0.1:8080-1) Facelet[/META-INF/template/datatable.xhtml] was modified @ 5:42:46, flushing component applied @ 5:42:45

原来问题还是出在Facelets视图处理框架上!
每次HTTP请求时,Facelets会检查xhtml的修改时间,与系统时间对比,如果页面代码时间新,则Facelets重新更新xhtml视图。
如果JSF组件树上有状态,也都会被一并flush,就会导致应用上页面功能不正常。

那么,是哪些xhtml文件比系统时间新呢?因为已经用touch命令都修改过了呀。
原来问题出现在jar包中的xhtml页面,它们的时间没有改过来,比系统时间新。
虽然jar包的修改时间改过来了,但里面的xhtml,JBoss EAP在解压加载时读到的还是最新的时间(比系统时间新)。

问题清楚了,解决方法也就有了:

方法1. 把jar里的xhtml页面修改成系统当前时间。

方法2. 在web.xml中,修改Facelets配置,禁用检查xhtml的修改时间功能,具体如下:
    <context-param>
      <param-name>facelets.REFRESH_PERIOD</param-name>
      <param-value>-1</param-value>
   </context-param>

2014年5月18日星期日

Linux_046:RHEL下创建NFS共享目录

环境:MAC OS X: 10.9.3 (NFS客机)+ RHEL 6.5(NFS主机)

本文介绍如何从MAC电脑访问RHEL中的NFS共享目录。

1. RHEL 端(NFS主机)的设置
默认情况下,RHEL 6已经安装了NFS服务,如果不确定有没有安装,运行 rpm -qa | grep nfs 查看。
如果没有安装,可以挂载RHEL 6 iso,进入Packages目录,运行 rpm -ivh nfs-utils*.rpm安装。

(1)以root身份修改/etc/exports文件,增加你要共享的目录。
比如:/home/maping/Share *(rw,root_squash,no_all_squash,sync,insecure)
(2)以root身份执行:/etc/rc.d/init.d/nfs restart 或 service nfs restart,重启NFS服务。
(3)以root身份执行:showmount -e,查看本机当前共享的资源。
看到以下输出,就说明NFS共享目录设置成功了:
Export list for localhost.localdomain:
/home/maping/Share *

这里顺便完整介绍一下showmout命令。
showmount –a IP    显示指定NFS服务器的客户端以及服务器端在客户端的挂载点。
showmount –d IP    显示指定NFS服务器在客户端的挂载点。
showmount –e IP    显示指定NFS服务器上的共享目录列表。

(4)以root身份执行:chmod 777 /home/maping/Share,允许所有人读写。
(5)以root身份执行:chkconfig --level 35 nfs on,开机自启动nfs服务。
nfs服务默认是停止的,因此按照上面配置后,重启机器后,nfs需要手工启动,比较麻烦。
使用chkconfig命令可以让服务开机自启动。

2. MAC端(NFS客机)的设置
(1)以root身份执行:showmount -e 192.168.0.104
(2)以root身份执行:mount -t nfs 192.168.0.104:/home/maping/Share /Users/maping/Mount/vm104
注意,目录/Users/maping/Mount/vm104要存在才可以mount成功。

好了,现在执行:cd /Users/maping/Mount/vm104,应该能看到RHEL下的NFS共享目录内容了。
试一试,创建一个文件。

参考文献:
1. http://larry-wang.com/?p=48
2. http://blog.csdn.net/hopezhangbo/article/details/19112241
3. http://www.oschina.net/question/54100_26364
4. http://share.blog.51cto.com/278008/244106
5. http://share.blog.51cto.com/278008/243798

MAC_012:创建NFS共享目录

环境:MAC OS X: 10.9.3 (NFS主机)+ RHEL 6.5(NFS客机)

本文介绍如何让别的机器访问MAC电脑中的NFS共享目录。

1. MAC端(NFS主机)的设置
(1)以root身份创建/etc/exports文件,增加你要共享的目录。
比如:/Users/maping/Mount/self。
(2)以root身份执行:nfsd enable,确认NFS服务启动了。
如果有必要,还可以使用命令:nfsd update, 刷新NFS共享资源。
(3)以root身份执行:showmount -e,显示当前共享的资源。
看到以下输出,就说明NFS共享目录设置成功了:
Exports list on localhost:
/Users/maping/Mount/self            Everyone
(4)以root身份执行:chmod 777 /Users/maping/Mount/self,允许所有人读写。

2. RHEL端(NFS客机)的设置
(1)以root身份执行:showmount -e 192.168.0.102
(2)以root身份执行:mount 192.168.0.102:/Users/maping/Mount/self /home/maping/Mount/mac/
注意,目录/home/maping/Mount/mac/要存在才可以mount成功。

好了,现在执行:cd /home/maping/Mount/mac/,应该能看到MAC下的共享目录内容了。
现在试一试,创建一个文件。

参考文献:
1. http://mac.pcbeta.com/thread-135894-1-1.html
2. http://mac.pcbeta.com/thread-93959-1-1.html

Linux_045:effective uid、real uid、saved uid

与进程相关的有三个 user id:real user id,effective user id,saved set-user-id。
对于 group 也有相同的情况,这里我们以 user 为例说明,group 可以类推。

real user id 是真正执行 process 的用户的 user id。
当判断一个进程是否对于某个文件有权限的时候,要验证的 user id 是effective user id,而不是 real user id。
那 effective user id 是如何被设置的呢?答案就是 SUID 和 SGID。
比如 /etc/passwd 文件,
-rwsr-xr-x. 1 root root 30768 2月  17 2012 /usr/bin/passwd
这个 s 就是SUID,当设置了SUID时,进程的 uid 为该可执行文件的属主的 uid。

搞清楚 real uid 和 effective uid 后,那 saved uid 的作用是什么?
// TODO

参考文献:
1. http://blog.csdn.net/fmeng23/article/details/23115989

Linux_044:常用命令之十五:ls

输入命令ls -l后,会得到一个含有9个字段的输出列表,比如:
总用量 72
drwxrwxr-x. 4 maping maping 4096 4月  16 11:35 Apache
drwxr-xr-x. 4 root   root   4096 4月  16 16:26 Code
drwx------. 3 maping maping 4096 4月  16 12:01 Documents
drwxr-xr-x. 3 root   root   4096 4月  16 11:53 Redhat
-rwxr-x---. 1 root   root    197 4月  16 11:47 setJdk6Env.sh
-rwxr-x---. 1 root   root    275 4月  16 11:59 setJdk7Env.sh
-rwxr-x---. 1 root   root    277 4月  16 11:50 setJdk8Env.sh

在解释各个输出字段前,先解释一下“总用量 72” 是什么意思,怎么计算出来的。
这个数值表示的是该目录的本身大小,而不是该目录下的文件和子目录下文件的总和大小。
把该目录看成一个特殊的文件,你就能理解什么是目录的大小。
这个数值是该目录下所有文件及目录列表的第5个字段的总和(以k为单位)。
可以用awk命令来验证:
(1)ls -l |awk 'BEGIN{sum=0}{sum+=$5}END{print sum}'
(2)ls -l |awk 'BEGIN{sum=0}{sum+=$5}END{print sum/1024}'
注意,累加后的数值可能与显示的值略有差异。

下面介绍按顺序介绍9个字段的含义:

1. 文件属性字段
文件属性字段总共有10个字母组成。
(1)第1个字母表示文件类型,如果这个字母是
-,说明该文件是一个普通文件。
d,表示该文件是一个目录(directory)。
l,表示该文件是一个符号链接(link)。
b,表示块设备文件(block),设备文件是普通文件和程序访问硬件设备的入口,一次传输数据大小为一个数据块(512字节),比如硬盘hda1、光盘都是设备文件。
c,表示该文件是一个字符设备文件(character),一次传输一个字节的设备被称为字符设备,比如键盘,字符终端。
(2)第1字段的后面9个字母表示文件的权限。
其中,r表是读(Read),w表示写(Write),x表示执行(eXecute)。
其中,前3个字母表示文件属主的权限,中间3个字母表示组用户权限,最后3个字母表示其他用户权限。

2. 文件硬链接数或目录的子目录数
(1)如果是文件,那么这一字段表示这个文件所具有的硬链接数,即这个文件总共有多少个文件名。
所谓硬链接,指的是这些文件指向的是同一个文件,并且无论是修改哪一个文件,另一个里也做相应的变化,因为实际上它们指向同一个文件。
使用命令:ln [文件名] [指向该文件的硬链接名],可以为文件增加硬链接。
使用命令:ls -i [文件名],可以查看该文件的文件节点(inode),具有相同文件节点的文件说明彼此互为硬链接。
知道文件节点后,可以使用命令:find /etc -inum [文件节点],查看该文件节点的所有硬链接文件位置。
(2)如果是目录,那么这一字段表示这个目录的子目录数。
注意,即使一个目录下面没有任何子目录,这个字段也会显示2。
这是因为每一个目录都有一个指向它本身的子目录:.,和指向它上级目录的子目录:..,这两个默认子目录是隐藏的,用ls -a可以看到。

3. 文件拥有者
该字段表示这个文件是属于哪个用户的。
只有文件的拥有者才具有改动文件属性的权利,当然,root 用户具有改动任何文件属性的权利。
对于一个目录来说,只有拥有该目录的用户,或者具有写权限的用户才有在目录下创建文件的权利。
如果某一个用户因为某种原因被删除,而该用户的文件还存在,那么用 ls -l 查看该文件将显示一个代表用户存在前 ID 号的数字。

4. 文件拥有者所在的组
组的概念可以想像成是一个共同完成一个项目的团队。
一个用户可以加入很多个组,但是其中有一个是主组,就是显示在第4字段的名称。
使用 adduser 命令时,参数 -g 指定该用户所在的主组,参数 -G 指定该用户要加入的其它组。

5. 文件或目录的大小(以字节为单位)
默认是以字节为单位显示,如果觉得可读性不好,可以增加 -h 参数,显示可读性大小。
注意,如果是目录,表示的是该目录本身的大小,而不是目录以及它下面的文件的总大小!

6. 文件创建月份

7. 文件创建日期

8. 文件创建时间
文件创建的时间可以通过 touch 命令来修改。

9. 文件名
如果是一个符号链接,那么会有一个 "->" 箭头符号,后面根一个它指向的文件名。

10. 常用例子
(1)ls -ltr
对目录和文件按照时间进行反向排序。

参考文献:
1. http://hi.baidu.com/sfzhaoif/item/3e2653c9426f28d497445220
2. http://www.9usb.net/201005/linux-ls.html

2014年5月17日星期六

Architect_010:微信网页版二维码登陆原理(摘录+整理)

登录手机微信后,使用微信的扫一扫功能扫描wx.qq.com上的二维码,成功后,会在手机显示如下画面:
同时在网页上,会显示如下画面:

确认后,就可以自动登录网页版的微信了,整个过程感觉挺有意思的,但它的原理是什么呢?
用其它条形码扫描软件扫一下微信登陆的二维码,发现是这个样子:

现在,可以猜一猜它背后的原理了。

1. 用户访问微信网页版,微信服务器使用二维码技术为这次对话生成一个唯一会话ID。
完整的URL就是上图显示的样子:https://login.weixin.qq.com/l/[会话ID]。

2. 用户打开自己已经登录的手机微信并扫描这个二维码,得到完整的URL后,然后发送给URL所指向的服务器,得到access_token
所谓access_token,可以理解为访问用户微信账户的许可令牌,因为用户已经在移动端微信登录,所以可以根据已经登录的账户获取该账户的access_token。
有了access_token,登录时就不必输入账号+口令了。

3. 微信服务器根据会话ID和access_token确认此用户确实是他自己本人,然后询问客户是否要登录网页版微信,同时刷新网页版页面提示扫描成功。
道理和步骤2一样,因为用户已经在移动端登录,具有合法身份。

4. 用户确认后,自动在网页版登录用户微信。
此时已经具备了自动登录的两个条件:
(1)本次会话的唯一ID。
(2)得到用户授权的access_token。
因此,得到用户确认后,已经在移动端登录的用户就可以在本次会话中使用access_token自动登录网页版微信。

至于为什么在手机上点击确认后web页面会自动登录,我想可能是使用了AJAX技术不断地向服务器询问用户是否确认,确认了就自动登录。
当然,也不排除是使用了比较新的WebSocket技术来实现轮询,但是WebSocket对浏览器的版本要求较高。

微信网页版二维码登陆原理大概就是这样吧,不对之处,不吝赐教。

参考文献:
http://www.zhihu.com/question/22991085/answer/24073398
http://www.oschina.net/question/568208_67170?sort=time
http://www.cnblogs.com/xusir/p/3473554.html
http://www.itlvs.com/news/opensource/2014/0305/189.html

Tips_022:三星S4 9508手机使用技巧

1. 截屏:电源键 + Home 键 同时按下
注意,出现白框才说明截屏成功,图片保存在然后在文件夹Pictures/Screenshots下。

2. 进入三星手机测试模式指令:*#0011# 


Architect_009:goagent工作原理(摘录+整理)

虽然使用goagent翻墙成功了,但不知道其原理,于是研究了一下下。
想翻还没有翻墙的同学,请参考《使用goagent为电脑翻墙》。


1. http请求发送到本地代理服务器上:127.0.0.1:8087。

2. 本地代理器封装http请求数据,然后调用GAE fetch服务,即调用你之前部署在GAE 上的应用,比如:http://xxx.appspot.com/fetch.py。
这里有一个问题:就是http://xxx.appspot.com本身是被墙的,那么在这一步是如何出去的呢?
查看proxy.ini,查看iplist部分:
[iplist]
google_cn = 203.208.46.131|203.208.46.132|203.208.46.133|203.208.46.134|203.208.46.135|203.208.46.136|203.208.46.137|203.208.46.138
google_hk = www.google.com|mail.google.com|www.google.com.hk|www.google.com.tw|www.l.google.com
google_talk =talk.google.com|talk.l.google.com|talkx.l.google.com
google_ipv6 = 2404:6800:4005:c00::64|2404:6800:4005:c00::65|2404:6800:4005:c00::5e|2404:6800:4005:c00::67|2404:6800:4005:c00::2f

;google_tw = www.google.com.tw|www.google.com.sg|www.google.co.jp|www.google.sg|www.google.cat|mail.google.com|mail.l.google.com
可以发现,其中内置了两套Hosts,一套是谷歌北京的IP(203打头),一套是谷歌香港的域名,注意,这些地址是没有被墙的。
GFW还没有丧心病狂到封杀这些地址,呵呵。
因此,本地代理服务器是通过这些地址(此时还在墙内),把你的请求通过类似于VPN的东东传送给美国谷歌总部服务器(这就出去了),而GAE PaaS平台就是运行在这些服务器上,至少有几十万台服务器,你的应用就是部署并运行在GAE PaaS平台上。
另外,有一种说法是,走谷歌香港出去时,使用https加密请求,墙无法解密,也就无法墙,只好放行。
这种说法,我没有验证,除非使用sniffer之类的嗅探器查看一下数据包是不是被加密了。
验证过的朋友告诉我一下啊。

3. GAE向目标服务器发送请求,然后再一路回来。
剩下的事,就简单了,谷歌美国总部服务器向目标服务器发送请求,目标服务器返回结果给谷歌总部服务器,谷歌总部服务器再返回谷歌北京或谷歌香港服务器,然后再一路回来返回到你的浏览器。

4. 翻墙后你的机器是美国IP吗?
有人说,翻墙后用浏览器去某测IP的网站,发现IP地址是位于美国山景城就对了。
我的实验结果是,我的机器依然是在国内的,60.207.193.197,来自于北京。

5. 最后附上访问我的博客地址时,goagent代理的输出,方便分析与查看。
INFO - [May 17 11:13:33] 127.0.0.1:52226 "DIRECT GET http://csi.gstatic.com/csi?v=3&s=blogger&action=item_blogspot&it=wtsrt_.1019,tbsd_.2924,tbnd_.2&srt=1019&tbsrt=3944&tran=15&blogId=6169420854489130819&e=templatesV1&rt=headEnd.5236,widgetJsBefore.5252,widgetJsStart.5260,widgetJsEnd.5282,prt.5282,ol.15162 HTTP/1.1" 204 0
INFO - [May 17 11:13:35] 127.0.0.1:52192 "URL GET http://maping930883.blogspot.com/2014/05/cloud020.html HTTP/1.1" 304 0
INFO - [May 17 11:13:35] 127.0.0.1:52366 "MOCK GET http://www.google.com/uds/css/gsearch.css HTTP/1.1" 301 0
INFO - [May 17 11:13:37] 127.0.0.1:52141 "URL GET https://www.blogger.com/dyn-css/authorization.css?targetBlogID=6169420854489130819&zx=14bce8c5-1b49-473b-985e-fc32ee9c7eac HTTP/1.1" 200 89
WARNING - [May 17 11:13:41] create_tcp_connection to 'www.google.com' with [('74.125.128.94', 443), ('74.125.128.17', 443), ('74.125.128.147', 443), ('74.125.128.94', 443)] return timeout('timed out',), try again.
INFO - [May 17 11:13:41] 127.0.0.1:52368 "FWD CONNECT www.google.com:443 HTTP/1.1" - -
INFO - [May 17 11:13:42] 127.0.0.1:52402 "FWD CONNECT csi.gstatic.com:443 HTTP/1.1" - -
INFO - [May 17 11:13:43] 127.0.0.1:52397 "FWD CONNECT accounts.google.com:443 HTTP/1.1" - -
INFO - [May 17 11:13:43] 127.0.0.1:52392 "FWD CONNECT apis.google.com:443 HTTP/1.1" - -
INFO - [May 17 11:13:44] 127.0.0.1:52359 "URL GET http://maping930883.blogspot.com/2014/05/cloud020.html?action=backlinks&widgetId=Blog1&widgetType=Blog&responseType=js&postID=4196305914791045908 HTTP/1.1" 200 333
INFO - [May 17 11:13:44] 127.0.0.1:52141 "URL GET https://www.blogger.com/navbar.g?targetBlogID=6169420854489130819&blogName=%E7%83%AD%E7%88%B1Java+%EF%BC%8C%E7%83%AD%E7%88%B1%E7%94%9F%E6%B4%BB&publishMode=PUBLISH_MODE_BLOGSPOT&navbarType=TAN&layoutType=LAYOUTS&searchRoot=http://maping930883.blogspot.com/search&blogLocale=zh_CN&v=2&homepageUrl=http://maping930883.blogspot.com/&targetPostID=4196305914791045908&blogPostOrPageUrl=http://maping930883.blogspot.com/2014/05/cloud020.html&vt=5704430368953175731&usegapi=1&jsh=m%3B%2F_%2Fscs%2Fapps-static%2F_%2Fjs%2Fk%3Doz.gapi.zh_CN.SmwPUR6ZzZM.O%2Fm%3D__features__%2Fam%3DAQ%2Frt%3Dj%2Fd%3D1%2Fz%3Dzcms%2Frs%3DAItRSTNjvLOR7hGyEIN5zmy19Buv4UHWRQ HTTP/1.1" 200 2978
INFO - [May 17 11:13:48] 127.0.0.1:52411 "URL GET http://andrejusb.blogspot.com/favicon.ico HTTP/1.1" 200 412

参考文献:
1. http://www.zhihu.com/question/20370059
2. http://goagent.ldsf.info/goagent的工作原理/
3. http://www.dewen.org/q/8739/goagent翻墙的原理是什么呢?
4. https://code.google.com/p/goagent/issues/detail?id=714

Cloud_020:网络基础知识(摘录+整理)

1. IP地址 = 网络号+主机号
如果把整个Internet网看成一个单一的网络的话,IP地址就是给每个连在Internet网的主机分配一个在全世界范围内唯一的标示符。
Internet管理委员会定义了A、B、C、D、E五类地址,在每类地址中,还规定了网络编号和主机编号。在 TCP/IP协议中,IP地址是以二进制数字形式出现的,共32bit,1bit就是二进制中的1位,但这种形式非常不适用于人阅读和记忆。因此Internet管理委员会决定采用一种点分十进制表示法表示IP地址:由4段构成的32bit的IP地址被直观地表示为四个以圆点隔开的十进制整数,其中,每一个整数对应一个字节。
A、B、C类最常用,下面加以介绍。本文介绍的都是版本4的IP地址,称为IPv4。
(1)A类地址的网络标识由第1段8位二进制数表示(第1段换算成10进制表示为1~126),其余3段用来表示主机标识。
A类地址的第1段首地址为00000001,末地址为01111111,换算成十进制就是1~127。
注意,数字0和127不作为A类地址,数字127保留给内部回送函数,而数字0则表示该地址是本地宿主机,不能传送。
A类地址允许有126个网段,每个网络允许有16,777,216台主机(减2是因为全0地址为网络地址,全1为广播地址,这两个地址一般不分配给主机)。
A类地址通常分配给拥有大量主机的网络。
(2)B类地址的网络标识由前2段8位二进制数表示(第1段换算成10进制表示为128~191),其余2段用来表示主机标识。
B类地址的第1段的前两位二进制数取值必须为10,首地址为10000000,末地址为10111111,换算成十进制B就是128~191。
B类地址允许有16384个网段,每个网络允许有65533台主机,适用于结点比较多的网络。
(3)C类地址的网络标识由前3段8位二进制数表示(第1段换算成10进制表示为192~223),其余1段用来表示主机标识。
C类地址的第1段的前3位二进制数取值必须为110,首地址为11000000,末地址为11011111,换算成十进制就是192~223。
C类地址允许有2,097,152个网段,每个网络允许有254台主机,适用于结点比较少的网络。

2. 回送地址
A类地址127是一个保留地址,用于网络软件测试以及本地机进程间通信,叫做回送地址(loopback address)。
无论什么程序,一旦使用回送地址发送数据,协议软件立即返回之,不进行任何网络传输。
网络号为127的IP地址不能出现在任何网络上。
127.0.0.1可以帮助我们排错,比如 ping 127.0.0.1,如果反馈信息失败,说明IP协议栈有错,必须重新安装TCP/IP协议。
如果成功,再ping本机IP地址,如果反馈信息失败,说明你的网卡不能和IP协议栈进行通信。
即使网卡没接网线,用本机的一些服务如Sql Server、IIS等就可以用127.0.0.1这个地址。

3. 私有地址
上面提到IP地址在全世界范围内唯一,看到这句话你可能有这样的疑问,像192.168.0.1这样的地址在许多地方都能看到,并不唯一,这是为何?
Internet管理委员会规定如下地址段为私有地址,私有地址可以自己组网时用,但不能在Internet上用,Internet上也没有这些地址的路由,有这些地址的计算机要上网必须转换成为合法的IP地址,也称为公网地址。
下面是A、B、C类网络中的私有地址段,你自己组网时可以使用这些地址。
(1)A类:10.0.0.0~10.255.255.255
(2)B类:172.16.0.0~172.131.255.255
(3)C类:192.168.0.0~192.168.255.255
比如,我现在机器的IP地址是192.168.0.102,这是ISP分配给我的,一般ISP会购买一些公网IP,然后划分为多个子网,我的IP就是应该是某个子网中动态分配的IP。
那么如何查看你上网时的公网地址呢?访问 http://www.whatismyip.com/ 或者 http://www.linkwan.com/gb/broadmeter/visitorinfo/qureyip.asp 就可以知道啦。
whatismyip 说我的 公网IP是:124.202.191.101,来自北京,通州。
linkwan 说我的公网IP是60.207.193.197,来自于北京。
为什么不一样?

4. 广播地址
TCP/IP规定,主机号全为1的IP地址用于广播之用,称为广播地址。所谓广播,指同时向同一子网所有主机发送报文。

5. 网络地址
TCP/IP协议规定,网络号全为0的网络号被解释成“本”网络。
因此,网络号全为0或全为1的IP地址在TCP/IP协议中有特殊含义,不能用作一台主机的有效地址。

6. 子网掩码
子网掩码的作用就是和IP地址与运算后得出网络地址,用于判断两个IP地址是否在同一网段。
子网掩码也是32bit,并且是一串1后跟随一串0组成,其中1表示在IP地址中的网络号对应的位数,而0表示在IP地址中主机对应的位数。
(1)标准子网掩码
A类网络(1~126)缺省子网掩码:255·0·0·0
B类网络(128~191)缺省子网掩码:255·255·0·0
C类网络(192~223)缺省子网掩码:255·255·255·0
如果使用的是标准子网掩码,那么21.0.0.0.1和21.240.230.1都是A类地址,且属于同一个网段。
同样,对于192·168·0·1和192·168·0·200,都是C类地址,如果使用默认的掩码,那这两个地址就是一个网络的。
(2)特殊的子网掩码
标准子网掩码都是255和0的组合,在实际的应用中还有下面的子网掩码
    255·128·0·0
    255·192·0·0
    255·255·192·0
    255·255·240·0
    255·255·255·248
    255·255·255·252
这些子网掩码又是什么意思呢?这些子网掩码的出现是为了把一个网络划分成多个网络。
还是以192·168·0·1和192·168·0·200为例,如果掩码变为255.255.255.192,那这两个地址就不属于一个网络了。

参考文献:
1. http://www.cnblogs.com/li-hao/archive/2012/04/06/2434563.html
2. http://vod.sjtu.edu.cn/help/Article_Print.asp?ArticleID=631

2014年5月15日星期四

Linux_043:常用命令之十四:ifconfig

ifconfig 命令用来查看和配置网络设备。通常需要root权限使用该命令。
输入ifconfig命令后,输出如下:

eth0      Link encap:Ethernet  HWaddr 08:00:27:58:1D:EE
          inet addr:10.0.2.15  Bcast:10.0.2.255  Mask:255.255.255.0
          inet6 addr: fe80::a00:27ff:fe58:1dee/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:22 errors:0 dropped:0 overruns:0 frame:0
          TX packets:32 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:12101 (11.8 KiB)  TX bytes:3389 (3.3 KiB)

eth1      Link encap:Ethernet  HWaddr 08:00:27:CD:CA:62
          inet addr:10.0.3.15  Bcast:10.0.3.255  Mask:255.255.255.0
          inet6 addr: fe80::a00:27ff:fecd:ca62/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:5 errors:0 dropped:0 overruns:0 frame:0
          TX packets:11 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:1522 (1.4 KiB)  TX bytes:1358 (1.3 KiB)

lo        Link encap:Local Loopback
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:16436  Metric:1
          RX packets:8 errors:0 dropped:0 overruns:0 frame:0
          TX packets:8 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:480 (480.0 b)  TX bytes:480 (480.0 b)

输出说明:
(1)eth0 表示第1块网卡, eth1 表示第2块网卡,这里的网卡不一定是物理网卡。
lo 是表示主机的回送地址,这个一般是用来测试一个网络程序,但又不想让局域网或外网的用户能够查看,只能在此台主机上运行和查看所用的网络接口。比如把 httpd服务器的指定到回坏地址,在浏览器输入 127.0.0.1 就能看到你的web网站了。但只是你能看得到,局域网的其它主机或用户无从知道。
(2)第一行:Link encap 表示连接类型:Ethernet(以太网),HWaddr 表示网卡的物理地址,即MAC地址。
(3)第二行:inet addr 表示网卡的IP地址,Bcast 表示广播地址,Mask 表示掩码地址。
(4)第三行:UP 代表网卡开启状态,RUNNING 代表网卡的网线被接上,MULTICAST 表示支持组播 MTU:1500 代表 最大传输单元是1500字节。
(5)第四、五行:接收、发送数据包情况统计。
(6)第六行:
(7)第七行:接收、发送数据字节数统计信息。

其它常用带参数命令

1. 启动/关闭指定网卡
(1)ifconfig eth0 up
(2)ifconfig eth0 down
练习的时候,ssh登陆linux服务器操作要小心,网卡关闭了就不能开启/连接了,除非你有多网卡。

2. 为网卡配置和删除IPv6地址
(1)ifconfig eth0 add 33ffe:3240:800:1005::2/64
(2)ifconfig eth0 del 33ffe:3240:800:1005::2/64
练习的时候,ssh登陆linux服务器操作要小心,关闭了就不能开启/连接了,除非你有多网卡。

3. 修改MAC地址
(1)ifconfig eth0 down
(2)ifconfig eth0 hw ether 00:AA:BB:CC:DD:EE
(3)ifconfig eth0 up
(4)ifconfig

4. 配置IP地址
(1)ifconfig eth0 192.168.120.56
(2)ifconfig eth0 192.168.120.56 netmask 255.255.255.0
(3)ifconfig eth0 192.168.120.56 netmask 255.255.255.0 broadcast 192.168.120.255

5. 启用/关闭ARP协议
(1)ifconfig eth0 arp
(2)ifconfig eth0 -arp

6. 设置最大传输单元
(1)ifconfig eth0 mtu 1500

说明:用ifconfig命令配置的网卡信息,在网卡重启后机器重启后,配置就不存在。
要想将上述的配置信息永远的存的电脑里,那就要修改网卡的配置文件了。

参考文献:
1. http://www.cnblogs.com/peida/archive/2013/02/27/2934525.html
2. http://blog.csdn.net/huichengongzi/article/details/5504365

2014年5月14日星期三

EAP_003:HornetQ集群 jms session fail over失败问题

JBoss版本:JBoss EAP 6.2.0。

集群配置环境和《HornetQ集群fail over失败问题》一样,唯一不同的是,消费者接收消息时使用transaction=true的session方式,具体场景是这样的:
(1)向Queue中发一条消息。
(2)消费者1使用transaction=true的session方式接受消息,消息处理过程较长。
(3)把server1 shutdown,server2上的backup server开始接管Queue,此时消费者1仍在处理消息中。
(4)消费者1消息处理完毕,执行到session.commit(),此时抛出异常,消息被rollback。
客户希望在服务器切换之后,session能够自动切换到备节点,当前的jms transaction可以commit成功。

1. 运行消费者1后,会抛出如下的异常:


2.  消费者1代码如下:

import java.io.Serializable;
import java.util.HashMap;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageListener;
import javax.jms.MessageProducer;
import javax.jms.ObjectMessage;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;

public class JmsSynConsumerClient {

    private Context getContext() throws NamingException {

        final Properties env = new Properties();
        env.put(Context.INITIAL_CONTEXT_FACTORY, "org.jboss.naming.remote.client.InitialContextFactory");
        env.put(Context.PROVIDER_URL, "remote://10.0.2.15:4447,remote://10.0.2.15:4547");
        env.put(Context.SECURITY_PRINCIPAL, "democlient");
        env.put(Context.SECURITY_CREDENTIALS, "password1!");

        return new InitialContext(env);
    }

    private Context context;

    private void execute() throws Exception {

        context = getContext();

        ConnectionFactory connectionFactory = null;
        Connection connection = null;
        Session session = null;
        MessageConsumer consumer = null;
        Destination destination = null;

        try {
            connectionFactory = (ConnectionFactory) context.lookup("jms/RemoteConnectionFactory");
            System.out.println("Acquiring connection factory success, " + connectionFactory);

            destination = (Destination) context.lookup("jms/queue/TransactionQueue");
            System.out.println("Acquiring destination success, " + destination);

            connection = connectionFactory.createConnection("democlient", "password1!");
            System.out.println("Creating connection success, " + connection);

            session = connection.createSession(true, Session.AUTO_ACKNOWLEDGE);
            System.out.println("Creating session success, " + session);

            consumer = session.createConsumer(destination);

            System.out.println("Creating consumer success, " + consumer);

            connection.start();

            Message message = consumer.receive();
            int count = 1;
            ObjectMessage objectMessage = (ObjectMessage) message;
            Serializable object = objectMessage.getObject();
            @SuppressWarnings("unchecked")
            HashMap map = (HashMap) object;
            String text = (String) map.get("message");
            count = (Integer) map.get("count");
            long delay = (Long) map.get("delay");
            System.out.println(count + ": " + text);
            Thread.sleep(delay);

//            consumer.setMessageListener(new MessageListener() {
//                @Override
//                public void onMessage(Message message) {
//                    try {
//                        ObjectMessage objectMessage = (ObjectMessage) message;
//                        Serializable object = objectMessage.getObject();
//                        @SuppressWarnings("unchecked")
//                                HashMap map = (HashMap) object;
//                        String text = (String) map.get("message");
//                        int count = (Integer) map.get("count");
//                        long delay = (Long) map.get("delay");
//                        System.out.println(count + ": " + text);
//                        try {
//                            Thread.sleep(delay);
//                        } catch (InterruptedException ex) {
//                            Logger.getLogger(JmsSynConsumerClient.class.getName()).log(Level.SEVERE, null, ex);
//                        }
//                    } catch (JMSException ex) {
//                        Logger.getLogger(JmsSynConsumerClient.class.getName()).log(Level.SEVERE, null, ex);
//                    }
//                }
//            });
//            
//            Thread.sleep(30000);
            
            session.commit();
            System.out.println("Commit the session transaction");

            System.out.println("JMSClient exit");
        } catch (Exception e) {
            throw e;
        } finally {

            if (consumer != null) {
                consumer.close();
            }

            if (session != null) {
                session.close();
            }

            if (connection != null) {
                connection.close();
            }
        }

    }

    public static void main(String[] args) throws Exception {
        new JmsSynConsumerClient().execute();
    }
}

3. 仔细翻阅了HornetQ文档,我发现HornetQ的工作方式就是这样的
具体请参看下文,我把重点的地方翻译一下:
HornetQ无法确定message的acknowledgments在fail-over时是否已经丢失,所以当执行session.commit()时,一律rollback事务。
这就是为什么consumer却无法透明地继续消费fail over到 backup server 上的消息的原因。
我觉得这是有道理的,因为处于安全性的考虑,出现异常时,rollback是正确的,这样可以防止消息丢失或被重复消费。
如果一定要实现该用户的场景,可以捕捉javax.jms.TransactionRolledBackException异常,然后进行重试。
try
         {
            session.commit();
         }
         catch (TransactionRolledBackException e)
         {
            System.err.println("transaction has been rolled back: " + e.getMessage());
            // TODO,写你的重试逻辑代码
         }

也就是说,出现异常后,HornetQ回滚消息,然后把如何继续处理的方式交给了开发人员。
这里,要提醒的是,如果Queue上有多个消费者,被回滚的消息有可能被其它消费者收到了。

4. HornetQ 有关fail over With Transactions的文档:

37.2.1.2. Handling fail-over With Transactions
If the session is transactional and messages have already been sent or acknowledged in the current transaction, then the server cannot be sure that messages sent or acknowledgments have not been lost during the fail-over.
Consequently the transaction will be marked as rollback-only, and any subsequent attempt to commit will throw a javax.jms.TransactionRolledBackException (if using JMS), or a HornetQException with error code HornetQException.TRANSACTION_ROLLED_BACK if using the core API.
It is up to the user to catch the exception, and perform any client side local rollback code as necessary. There is no need to manually rollback the session - it is already rolled back. The user can then just retry the transactional operations again on the same session.
If fail-over occurs when a commit call is being executed, the server, as previously described, will unblock the call to prevent a hang, since no response will come back. In this case it is not easy for the client to determine whether the transaction commit was actually processed on the live server before failure occurred.
To remedy this, the client can enable duplicate detection (Chapter 35, Duplicate Message Detection) in the transaction, and retry the transaction operations again after the call is unblocked. If the transaction had indeed been committed on the live server successfully before fail-over, then when the transaction is retried, duplicate detection will ensure that any durable messages resent in the transaction will be ignored on the server to prevent them getting sent more than once.

Note
By catching the rollback exceptions and retrying, catching unblocked calls and enabling duplicate detection, once and only once delivery guarantees for messages can be provided in the case of failure, guaranteeing 100% no loss or duplication of messages.

参考文献:
1. https://access.redhat.com/site/documentation/en-US/JBoss_Enterprise_Application_Platform/5/html/HornetQ_User_Guide/failover.html
2. http://hornetq.sourceforge.net/docs/hornetq-2.0.0.GA/user-manual/en/html/ha.html