分类:Linux

Linux

Linux系统下通过策略路由实现多默认路由

No Comments IT必备工具, Linux

问题简述

一般地说,在Linux系统路由表内只能有一条默认路由。当出站数据包根据目的IP地址选路失败后,执行默认路由,交默认路由指向的下一跳路由器(默认网关)转发数据包。

现需要同时存在两条默认路由。数据包通过何种默认路由,由程序指定(或根据规则)。数据包通过特定的路由规则转发到对应的路由器。

在下文中,我们以如下的拓扑为例,介绍如何通过策略路由来实现上述需求。

  • 服务器上安装有两块网卡,分别为p7p1和p7p2;

  • 网卡p7p1:192.168.1.1/24,连接至路由器R1;

  • 网卡p7p2:192.168.2.1/24,连接至路由器R2;

  • 路由器R1:192.168.1.254/24;

  • 路由器R2:192.168.2.254/24。

我们要实现的选路策略:

  • 根据源IP地址选路,所有源IP地址为192.168.1.1的报文,通过eth0转发到路由器R1,所有源IP地址为192.168.2.1的报文,通过eth0转发到路由器R2;

  • 进一步地,本机程序发送IP报文,由程序选择从何出口转发到对应的路由器。

实现思路

通过多张路由表和策略路由实现上述的配置需求。

  • 路由表1:默认路由指向R1,即192.168.1.254;

  • 路由表2:默认路由指向R2,即192.168.2.254

  • 策略路由,优先级高于local路由表:

    • 源IP为192.168.1.1的报文,执行路由表1;

    • 源IP为192.168.2.1的报文,执行路由表2;

路由表配置

------中间广告---------

1.创建路由表

# echo "10 eth1table" >> /etc/iproute2/rt_tables
# echo "20 eth2table" >> /etc/iproute2/rt_tables

2.配置路由表,添加默认路由

# 本机与默认网关的路由,否则会显示路由不可达
# ip route add 192.168.1.0/24 dev eth1 table eth1table
# ip route add 192.168.2.0/24 dev eth2 table eth2table
# 默认网关
# ip route add default via 192.168.1.254 table eth1table
# ip route add default via 192.168.2.254 table eth2table

3.配置策略路由

# ip rule add from 192.168.1.1/32 table eth1table
# ip rule add from 192.168.2.1/32 table eth2table

测试

为了方便,通过静态ARP配置,模拟下一跳路由器。

# arp -s 192.168.1.254 aa:bb:cc:dd:ee:ff
# arp -s 192.168.2.254 11:22:33:44:55:66

利用NC工具发送UDP报文,设置源IP地址为192.168.1.1,即

# nc -s 192.168.1.1 -u 202.202.202.202

在网卡p7p1上运行tcpdump命令捕包。

# tcpdump -i p7p1 -e

结果:

15:39:36.225020 e8:61:1f:18:ef:24 (oui Unknown) > aa:bb:cc:dd:ee:ff (oui Unknown), ethertype IPv4 (0x0800),
length 51: 192.168.1.1.46399 > 202.202.202.202.31337: UDP, length 9

类似地,设置源IP地址为192.168.2.1,通过tcpdump在p7p2上捕获得

15:42:11.157252 e8:61:1f:18:ef:25 (oui Unknown) > 11:22:33:44:55:66 (oui Unknown), ethertype IPv4 (0x0800), 
length 48: 192.168.2.1.39107 > 202.202.202.202.31337: UDP, length 6

参考文献

[1] https://networkengineering.st…

Linux之Shell的算术运算

No Comments Linux

在Bash的算术运算中有以下几种方法:
名称                语法                    范例
算术扩展            ((算术式))r=<?XML:NAMESPACE PREFIX = "[default] http://www.w3.org/1998/Math/MathML" NS = "http://www.w3.org/1998/Math/MathML" />((算术式))r=((1+2*3))
使用外部程序expr    expr 算术式              r=`expr 1+2*3`
使用[][][算术式]                r=$[1+2]
使用内置命令        declare -i 变量=算术式    declare -i r=1+2*3
使用内置命令let     let 算术式                let r=1+2

i++运算后加1,i–运算后减1
++i运算前加1,–i运算前减1

一、算术扩展
算术扩展的语法是:((表达式)),如果表达式中有变量,该变量之前最好不要加((表达式)),如果表达式中有变量,该变量之前最好不要加这个符号,以免变量不存在造成语法错误,例:
unset i
echo ((2+((2+i))
由于变量i不存在,所以会变成 echo ((2+)),这样语法就错误了。但如果写成echo((2+)),这样语法就错误了。但如果写成echo((2+i)),((2+i))是一个合法的算术式,就算变量不存在,仍可正确计算。
a=5
a=$((++1)) a的值加1后,在赋值给a
a=$((–1)) a的值减1后,在赋值给a
a=$((3+a–))运算后减1
二、使用外部程序expr做算术运算
外部程序expr本来的作用,是在“标准输出”显示表达式的值。这个是外部程序,和Shell没有关系,所以移植性非常好,
如果注重跨平台,那么可以在脚本本使用expr代替其他算术表达式。

expr 3 + 4  
它会在屏幕上显示7,注意"+"两边有空格。
在使用expr时,要特别"表达式"中是否包含shell的特殊字符,如*、|、<、>、!、&、(、),要使用"\"来转义。
1、r=`expr 参数1 \|参数2`
|代表"或"之意.如果”参数1“存在、非空、不是0,则传回”参数1“的值,否则传回"参数2"的值。
r=`expr 3 \| 0` r的值为3
r=`expr 0 \| 2` r的值为2
2、r=`expr 参数1 \&参数2`
如果"参数1"和"参数2"都存在、非空、不是0,则传回"参数1"的值,否则传回0.
r=`expr 3 \& 0` r的值为0
3、比较
算术            是否成立    r的值
r=`expr 1 \< 2`      是        1 
r=`expr 3 \<= 2`     否        0 
r=`expr 2 \= 2`      是        1 
r=`expr 3 \!= 2`     是        1 
r=`expr 3 \>= 2`     是        1 
r=`expr 3 \> 2`      是        1 
4、加
r=`expr 4 + 5` r的值为9
5、减
r=`expr 3 – 5` r的值为-2
6、乘
r=`expr 3 \* 5` r的值为15
7、除
r=`expr 32 / 5` r的值为6(余数无条件舍去)
8、乘方
expr没有乘方的功能
9、求余数
r=`expr 32 % 5` r的值为2
10、计算字符串长度
r=`expr length "hello"`  r的值为5
三、用$[]做算术运算
使用[]做算术运算和[]做算术运算和(())类似
加:r=[4+5]r的值为9减:r=[4+5]r的值为9减:r=[4-5]    r的值为-1
乘:r=[4∗5]r的值为20除:r=[4∗5]r的值为20除:r=[5/2]    r的值为2
余数:r=[5[5[2**3] r的值为8

四、使用declare、let做算术运算
declare为bash shell的内置命令。
declare -i I
I=5+4 
echo $I 
因为I事先被定义为整数(算术运算),所以$I的值为9,而不再是字符串5+4
当被定义为整数后,便可以进行加、减、乘、除..等操作
五、用let做算术运算

let为bash shell的内置命令。

let I=8+4*5
echo $I 
此时输出的值为28
let表明后边的直接说一个算术式。
也可使用空格符让表达式可读性高一点,但这时必须使用引号包括表达式才行。
let "i = i + 5"

本文出自 “生命不息,奋斗不止!” 博客,请务必保留此出处

azure linux vm ssh增加私有key

No Comments Linux

azure推荐用证书的方式来登陆ssh

 

1.生成 公钥  看最下详细

 

2.将生成的公钥导入被登陆的服务器

进入目录

/home/用户/.shh/authorized_keys

-rw-r–r–. 1 用户 用户组 381 Sep  3 08:32 authorized_keys

将pub 写入文件保存

 

3.将私钥导入登陆客户端

 

chmod 600 私钥

 

ssh -i AZURE-PRI 用户@被登陆的服务器

 

4.结束

 

 

———–公钥,私钥,以下是生成方式

ssh-keygen 可用来生成ssh公钥认证所需的公钥和私钥文件。

使用 ssh-keygen 时,请先进入到 ~/.ssh 目录,不存在的话,请先创建。并且保证 ~/.ssh 以及所有父目录的权限不能大于 711

生成的文件名和文件位置

使用 ssh-kengen 会在~/.ssh/目录下生成两个文件,不指定文件名和密钥类型的时候,默认生成的两个文件是:

  • id_rsa
  • id_rsa.pub

第一个是私钥文件,第二个是公钥文件。

生成ssh key的时候,可以通过 -f 选项指定生成文件的文件名,如下:

[huqiu@101 .ssh]$ ssh-keygen -f test   -C "test key"
                             ~~文件名   ~~~~ 备注

如果没有指定文件名,会询问你输入文件名:

[huqiu@101 .ssh]$ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/home/huqiu/.ssh/id_rsa):

你可以输入你想要的文件名,这里我们输入test

密码

之后,会询问你是否需要输入密码。输入密码之后,以后每次都要输入密码。请根据你的安全需要决定是否需要密码,如果不需要,直接回车:

[huqiu@101 .ssh]$ ssh-keygen -t rsa -f test -C "test key"
Generating public/private rsa key pair.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
结果

如果文件名是test,结果是:

[huqiu@101 .ssh]$ ll test*
-rw------- 1 huqiu huqiu 1675 Sep 15 13:24 test
-rw-r--r-- 1 huqiu huqiu  390 Sep 15 13:24 test.pub
备注

上面生成的命令中,-C选项是公钥文件中的备注:

[huqiu@101 .ssh]$ cat test.pub
ssh-rsa
AAAAB3NzaC1yc2EAAAABIwAAAQEAlgjiMw7AskxbvpQY9rmZPQxQBzh9laxFvbaini2EgmQkNsXBA9WJOXn2YBJauoiVsdUKBWA97avjsobrTxsCYvFr1yQQvTfTlbqlqGNIhQc/3HjTl2pIkClpDWvBrRN+jpyESS4MNbfOL1qjT4c/QhGvj6U6HrN6kUyn58oyyJpTzOLG74AZELJ2Led57QvTw1yJXZuAMWioR0A3BGd25fdocLX3ebux6ya8AsloOVYfsAqGlggrARe6FXjLfMH4a/nxaAdiDYVXU/Vr1ybK9P7SfyEDGJi3JtgiPUlA6vPxUC
E+9IJPQaqqeqCGzrJ6G/XO7om1v9YLLG/H/ZN2tQ== test key
                                           ~~~~备注
文件的权限

为了让私钥文件和公钥文件能够在认证中起作用,请确保权限正确。

对于.ssh 以及父文件夹,当前用户用户一定要有执行权限,其他用户最多只能有执行权限。

对于公钥和私钥文件也是: 当前用户一定要有执行权限,其他用户最多只能有执行权限。

对于利用公钥登录,对其他用户配置执行权限是没有问题的。但是对于git,公钥和私钥, 以及config等相关文件的权限,其他用户不可有任何权限。

Azure Linux VM 建立 Swap 分区

No Comments Linux

Azure Linux VM 建立 Swap 分区

———–以下两步就OK
[root@cc ~]# lsblk
NAME   MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
fd0      2:0    1    4K  0 disk
sda      8:0    0   31G  0 disk
├─sda1   8:1    0  500M  0 part /boot
└─sda2   8:2    0 29.5G  0 part /
sdb      8:16   0   16G  0 disk
└─sdb1   8:17   0   16G  0 part /mnt/resource
sdc      8:32   0  500G  0 disk /data
sr0     11:0    1 1024M  0 rom 

一,查看盘sdb就是临时盘
[root@cc ~]# fdisk -l

Disk /dev/sdb: 17.2 GB, 17179869184 bytes, 33554432 sectors

   Device Boot      Start         End      Blocks   Id  System
/dev/sdb1             128    33552383    16776128   83  Linux

二,修改 vim /etc/waagent.conf

# Create and use swapfile on resource disk.
ResourceDisk.EnableSwap=y

# Size of the swapfile.
ResourceDisk.SwapSizeMB=17000   #这个<= /dev/sdb: 17.2 GB

保存,重启

———-可看可不看
创建Swap分区文件

sudo fallocate -l [Swap文件大小,例如:5g] [Swap文件完整路径,例如:/mnt/myswapfile]

mkswap /mnt/resource
swapon /mnt/resource
swapoff /mnt/resource

linux添加跨网段网关

No Comments Linux

1.eth0的ip地址为10.1.1.1/24

路由信息如下

# route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
192.168.150.0   0.0.0.0         255.255.255.0   U     0      0        0 mgmt
10.1.1.0        0.0.0.0         255.255.255.0   U     0      0        0 eth0
169.254.0.0     0.0.0.0         255.255.0.0     U     1007   0        0 mgmt
0.0.0.0         192.168.150.254 0.0.0.0         UG    0      0        0 mgmt

2.直接添加路由网关和出接口相同网段会失败

# route add -net 2.0.0.0/8 gw 20.1.1.1 eth0
SIOCADDRT: No such process

3.正确配置方式:

#route add -net 0.0.0.0 eth0

#route add -net 2.0.0.0/8 gw 20.1.1.1 eth0

4.显示路由信息,配置成功

# route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
192.168.150.0   0.0.0.0         255.255.255.0   U     0      0        0 mgmt
10.1.1.0        0.0.0.0         255.255.255.0   U     0      0        0 eth0
169.254.0.0     0.0.0.0         255.255.0.0     U     1007   0        0 mgmt
2.0.0.0         20.1.1.1        255.0.0.0       UG    0      0        0 eth0
0.0.0.0         0.0.0.0         0.0.0.0         U     0      0        0 eth0
0.0.0.0         192.168.150.254 0.0.0.0         UG    0      0        0 mgmt

ps

一种使用场景:

拓扑:

linux1-eth0——–eth0-linux2-eth1

linux1:

eth0 10.1.1.1/24

linux2:

eth0 20.1.1.1/24

eth1 2.2.2.2/24

1.linux1

route add -net 0.0.0.0 eth0

route add -net 2.0.0.0/8 gw 20.1.1.1 eth0

2.linux2

route add -net 0.0.0.0 eth0

route add -net 10.0.0.0/8 gw 10.1.1.1 eth0

3配置如上后linux1 ping 2.2.2.2可通

HTTP 请求头中的 X-Forwarded-For,X-Real-IP

No Comments Linux

小结:

1.通过以上几种情况我们可以了解到设置X-Forwarded-For是一个可叠加的过程,后面的代理会把前面代理的IP加入X-Forwarded-For,类似于python的列表append的作用.

2.我们看到在三层代理情况下无论如何设置,应用服务器不可能从$http_x_forwarded_for拿到与它直连的这台服务器的ip(proxy03 ip),此时我们可以使用$remote_addr(远程ip,表示直连的那台代理).一句话,当前服务器无法通过$http_x_forwarded_for获得上级代理或者客户端的ip,应该使用$remote_addr.

3.在代理过程中至少有一个代理设置了proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;否则后面代理或者应用服务器无法获得相关信息.

4.注意,应用服务器可以通过$proxy_add_x_forwarded_for客户端IP(只要至少proxy01代理设置了proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;我们取第一IP就好了)

 

X-Real-IP


下面我们看一下有多级代理存在时如何获取客户端真实IP.

首先要明确在header里面的 X-Real-IP只是一个变量,后面的设置会覆盖前面的设置(跟X-Forwarded-For的追加特性区别明显),所以我们一般只在第一个代理设置proxy_set_header X-Real-IP $remote_addr;就好了,然后再应用端直接引用$http_x_real_ip就行.

jenkins迁移和备份

No Comments Linux

首先找到JENKINS_HOME,只要备份了JENKINS_HOME的目录,就可以全部迁移备份了。jenkins不需要连接数据库,没有数据库概念。

1、JENKINS_HOME确认方式:

点击jenkins中的系统设置,然后有一个主目录路径,这个就是JENKINS_HOME。如图:

blob.png

2、迁移:

建议将JENKINS_HOME打包后在拷贝,windows上可以用zip,rar等,Linux上有zip,tar等。然后将打包的文件解压到新的JENKINS_HOME目录就行了。

3、备份:

参考Jenkins进阶系列之——08Jenkins纳入版本控制。如果是临时备份,整个压缩文件就行了。

4、其它:

1)如果jobs 和fingerprints文件太大,可以不需要备份下来,但是里面所有的项目构件就会都没有。只要迁移好以后,直接建两个这个名称的空文件即可。

2)在项目配置中,如果 源码管理–Git中输入账号就报错,可能是git版本过低。如果jenkins是2.12的,那么git需要1.8及以上版本。

Primary script unknown" while reading response header from upstream

No Comments Linux

今天下午搞了一个下午的LNMP环境搭建,N,M,P都安装成功了,静态页面也正常,就差最后一步了,挡的我好心累。

【解决步骤】
1、使用wget从本机获取php页面,返回的是状态码是404.
2、查找错误日志如下(可以放大看):

2018/08/17 05:13:57 [error] 11451#0: *11 FastCGI sent in stderr: "Primary script unknown" while reading response header from upstream, client:  , server: www3.ccie.wang, request: "GET / HTTP/1.1", upstream: "fastcgi://127.0.0.1:9000", host: " "

 

3、网上说的解决错误的方法都试了,大多都是粘贴复制,说的是修改Nginx配置文件中找到定义调用脚本文件的地方,修改下面的代码

location / {
root   /usr/local/nginx/html;
index  index.php index.html index.htm;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}

结果还是不行,结果还是404错误。

4.既然静态页面解析没问题,肯定是Nginx和PHP的结合程序FastCGI出了问题,查看PHP服务php-fpm

#ps -ef|grep php-fpm


红色笔圈住的地方,本来用户默认是nobody,(nobody用户应该没有多少权限)。正常应该用户是和Nginx进程一致

5、修改php-fpm进程的用户和用户组试试:

#vi /application/php/etc/php-fpm.d/www.conf
这是我的服务器上php-fpm的配置文件路径(我是编译安装的)

 

找到user = nobody

和group = nobody

将其改为何Nginx一致的用户,我这里是chenxuliang用户,如图

修改为下面配置

/etc/php-fpm.d/www.conf

user = nginx

group = nginx


然后使php-fpm服务重启,(我是编译安装的,不知道怎么重启服务,使用ps -ef 命令查找到php-fpm进程号然后将其kill掉,命令#kill -9 process_id,然后执行/application/php/sbin/php-fpm,启动服务,注意这是在我的环境下是这个路径)

6、测试。linux本机执行wget,或者在浏览器中获取php文件,看其是否解析成功。如下图:

Linux 技巧:让进程在后台可靠运行的几种方法

No Comments Linux

我们经常会碰到这样的问题,用 telnet/ssh 登录了远程的 Linux 服务器,运行了一些耗时较长的任务, 结果却由于网络的不稳定导致任务中途失败。如何让命令提交后不受本地关闭终端窗口/网络断开连接的干扰呢?下面举了一些例子, 您可以针对不同的场景选择不同的方式来处理这个问题。

nohup/setsid/&

场景:

如果只是临时有一个命令需要长时间运行,什么方法能最简便的保证它在后台稳定运行呢?

hangup 名称的来由

在 Unix 的早期版本中,每个终端都会通过 modem 和系统通讯。当用户 logout 时,modem 就会挂断(hang up)电话。 同理,当 modem 断开连接时,就会给终端发送 hangup 信号来通知其关闭所有子进程。

解决方法:

我们知道,当用户注销(logout)或者网络断开时,终端会收到 HUP(hangup)信号从而关闭其所有子进程。因此,我们的解决办法就有两种途径:要么让进程忽略 HUP 信号,要么让进程运行在新的会话里从而成为不属于此终端的子进程。

1. nohup

nohup 无疑是我们首先想到的办法。顾名思义,nohup 的用途就是让提交的命令忽略 hangup 信号。让我们先来看一下 nohup 的帮助信息:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

NOHUP(1)                        User Commands                        NOHUP(1)

NAME

nohup - run a command immune to hangups, with output to a non-tty

SYNOPSIS

nohup COMMAND [ARG]...

nohup OPTION

DESCRIPTION

Run COMMAND, ignoring hangup signals.

--help display this help and exit

--version

output version information and exit

可见,nohup 的使用是十分方便的,只需在要处理的命令前加上 nohup 即可,标准输出和标准错误缺省会被重定向到 nohup.out 文件中。一般我们可在结尾加上"&"来将命令同时放入后台运行,也可用">filename 2>&1"来更改缺省的重定向文件名。

nohup 示例

1

2

3

4

5

6

7

[root@pvcent107 ~]# nohup ping www.ibm.com &

[1] 3059

nohup: appending output to `nohup.out'

[root@pvcent107 ~]# ps -ef |grep 3059

root      3059   984  0 21:06 pts/3    00:00:00 ping www.ibm.com

root      3067   984  0 21:06 pts/3    00:00:00 grep 3059

[root@pvcent107 ~]#

2。setsid

nohup 无疑能通过忽略 HUP 信号来使我们的进程避免中途被中断,但如果我们换个角度思考,如果我们的进程不属于接受 HUP 信号的终端的子进程,那么自然也就不会受到 HUP 信号的影响了。setsid 就能帮助我们做到这一点。让我们先来看一下 setsid 的帮助信息:

1

2

3

4

5

6

7

8

9

10

SETSID(8)                 Linux Programmer’s Manual                 SETSID(8)

NAME

setsid - run a program in a new session

SYNOPSIS

setsid program [ arg ... ]

DESCRIPTION

setsid runs a program in a new session.

可见 setsid 的使用也是非常方便的,也只需在要处理的命令前加上 setsid 即可。

setsid 示例

1

2

3

4

5

[root@pvcent107 ~]# setsid ping www.ibm.com

[root@pvcent107 ~]# ps -ef |grep www.ibm.com

root     31094     1  0 07:28 ?        00:00:00 ping www.ibm.com

root     31102 29217  0 07:29 pts/4    00:00:00 grep www.ibm.com

[root@pvcent107 ~]#

值得注意的是,上例中我们的进程 ID(PID)为31094,而它的父 ID(PPID)为1(即为 init 进程 ID),并不是当前终端的进程 ID。请将此例与nohup 例中的父 ID 做比较。

3。&

这里还有一个关于 subshell 的小技巧。我们知道,将一个或多个命名包含在“()”中就能让这些命令在子 shell 中运行中,从而扩展出很多有趣的功能,我们现在要讨论的就是其中之一。

当我们将"&"也放入“()”内之后,我们就会发现所提交的作业并不在作业列表中,也就是说,是无法通过jobs来查看的。让我们来看看为什么这样就能躲过 HUP 信号的影响吧。

subshell 示例

1

2

3

4

5

[root@pvcent107 ~]# (ping www.ibm.com &)

[root@pvcent107 ~]# ps -ef |grep www.ibm.com

root     16270     1  0 14:13 pts/4    00:00:00 ping www.ibm.com

root     16278 15362  0 14:13 pts/4    00:00:00 grep www.ibm.com

[root@pvcent107 ~]#

从上例中可以看出,新提交的进程的父 ID(PPID)为1(init 进程的 PID),并不是当前终端的进程 ID。因此并不属于当前终端的子进程,从而也就不会受到当前终端的 HUP 信号的影响了。

disown

场景:

我们已经知道,如果事先在命令前加上 nohup 或者 setsid 就可以避免 HUP 信号的影响。但是如果我们未加任何处理就已经提交了命令,该如何补救才能让它避免 HUP 信号的影响呢?

解决方法:

这时想加 nohup 或者 setsid 已经为时已晚,只能通过作业调度和 disown 来解决这个问题了。让我们来看一下 disown 的帮助信息:

1

2

3

4

5

6

7

8

9

10

11

disown [-ar] [-h] [jobspec ...]

Without options, each jobspec is  removed  from  the  table  of

active  jobs.   If  the -h option is given, each jobspec is not

removed from the table, but is marked so  that  SIGHUP  is  not

sent  to the job if the shell receives a SIGHUP.  If no jobspec

is present, and neither the -a nor the -r option  is  supplied,

the  current  job  is  used.  If no jobspec is supplied, the -a

option means to remove or mark all jobs; the -r option  without

a  jobspec  argument  restricts operation to running jobs.  The

return value is 0 unless a jobspec does  not  specify  a  valid

job.

可以看出,我们可以用如下方式来达成我们的目的。

灵活运用 CTRL-z

在我们的日常工作中,我们可以用 CTRL-z 来将当前进程挂起到后台暂停运行,执行一些别的操作,然后再用 fg 来将挂起的进程重新放回前台(也可用 bg 来将挂起的进程放在后台)继续运行。这样我们就可以在一个终端内灵活切换运行多个任务,这一点在调试代码时尤为有用。因为将代码编辑器挂起到后台再重新放回时,光标定位仍然停留在上次挂起时的位置,避免了重新定位的麻烦。

  • disown -h jobspec来使某个作业忽略HUP信号。
  • disown -ah 来使所有的作业都忽略HUP信号。
  • disown -rh 来使正在运行的作业忽略HUP信号。

需要注意的是,当使用过 disown 之后,会将把目标作业从作业列表中移除,我们将不能再使用jobs来查看它,但是依然能够用ps -ef查找到它。

但是还有一个问题,这种方法的操作对象是作业,如果我们在运行命令时在结尾加了"&"来使它成为一个作业并在后台运行,那么就万事大吉了,我们可以通过jobs命令来得到所有作业的列表。但是如果并没有把当前命令作为作业来运行,如何才能得到它的作业号呢?答案就是用 CTRL-z(按住Ctrl键的同时按住z键)了!

CTRL-z 的用途就是将当前进程挂起(Suspend),然后我们就可以用jobs命令来查询它的作业号,再用bg jobspec来将它放入后台并继续运行。需要注意的是,如果挂起会影响当前进程的运行结果,请慎用此方法。

disown 示例1(如果提交命令时已经用“&”将命令放入后台运行,则可以直接使用“disown”)

1

2

3

4

5

6

7

8

9

[root@pvcent107 build]# cp -r testLargeFile largeFile &

[1] 4825

[root@pvcent107 build]# jobs

[1]+  Running                 cp -i -r testLargeFile largeFile &

[root@pvcent107 build]# disown -h %1

[root@pvcent107 build]# ps -ef |grep largeFile

root      4825   968  1 09:46 pts/4    00:00:00 cp -i -r testLargeFile largeFile

root      4853   968  0 09:46 pts/4    00:00:00 grep largeFile

[root@pvcent107 build]# logout

disown 示例2(如果提交命令时未使用“&”将命令放入后台运行,可使用 CTRL-z 和“bg”将其放入后台,再使用“disown”)

1

2

3

4

5

6

7

8

9

10

11

12

[root@pvcent107 build]# cp -r testLargeFile largeFile2

[1]+  Stopped                 cp -i -r testLargeFile largeFile2

[root@pvcent107 build]# bg %1

[1]+ cp -i -r testLargeFile largeFile2 &

[root@pvcent107 build]# jobs

[1]+  Running                 cp -i -r testLargeFile largeFile2 &

[root@pvcent107 build]# disown -h %1

[root@pvcent107 build]# ps -ef |grep largeFile2

root      5790  5577  1 10:04 pts/3    00:00:00 cp -i -r testLargeFile largeFile2

root      5824  5577  0 10:05 pts/3    00:00:00 grep largeFile2

[root@pvcent107 build]#

screen

场景:

我们已经知道了如何让进程免受 HUP 信号的影响,但是如果有大量这种命令需要在稳定的后台里运行,如何避免对每条命令都做这样的操作呢?

解决方法:

此时最方便的方法就是 screen 了。简单的说,screen 提供了 ANSI/VT100 的终端模拟器,使它能够在一个真实终端下运行多个全屏的伪终端。screen 的参数很多,具有很强大的功能,我们在此仅介绍其常用功能以及简要分析一下为什么使用 screen 能够避免 HUP 信号的影响。我们先看一下 screen 的帮助信息:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

SCREEN(1)                                                           SCREEN(1)

NAME

screen - screen manager with VT100/ANSI terminal emulation

SYNOPSIS

screen [ -options ] [ cmd [ args ] ]

screen -r [[pid.]tty[.host]]

screen -r sessionowner/[[pid.]tty[.host]]

DESCRIPTION

Screen  is  a  full-screen  window manager that multiplexes a physical

terminal between several  processes  (typically  interactive  shells).

Each  virtual  terminal provides the functions of a DEC VT100 terminal

and, in addition, several control functions from the  ISO  6429  (ECMA

48,  ANSI  X3.64)  and ISO 2022 standards (e.g. insert/delete line and

support for multiple character sets).  There is a  scrollback  history

buffer  for  each virtual terminal and a copy-and-paste mechanism that

allows moving text regions between windows.

使用 screen 很方便,有以下几个常用选项:

  • screen -dmS session name来建立一个处于断开模式下的会话(并指定其会话名)。
  • screen -list 来列出所有会话。
  • screen -r session name来重新连接指定会话。
  • 用快捷键CTRL-a d 来暂时断开当前会话。
screen 示例

1

2

3

4

5

6

7

[root@pvcent107 ~]# screen -dmS Urumchi

[root@pvcent107 ~]# screen -list

There is a screen on:

12842.Urumchi   (Detached)

1 Socket in /tmp/screens/S-root.

[root@pvcent107 ~]# screen -r Urumchi

当我们用“-r”连接到 screen 会话后,我们就可以在这个伪终端里面为所欲为,再也不用担心 HUP 信号会对我们的进程造成影响,也不用给每个命令前都加上“nohup”或者“setsid”了。这是为什么呢?让我来看一下下面两个例子吧。

1. 未使用 screen 时新进程的进程树

1

2

3

4

5

6

7

8

9

[root@pvcent107 ~]# ping www.google.com &

[1] 9499

[root@pvcent107 ~]# pstree -H 9499

init─┬─Xvnc

├─acpid

├─atd

├─2*[sendmail]

├─sshd─┬─sshd───bash───pstree

│       └─sshd───bash───ping

我们可以看出,未使用 screen 时我们所处的 bash 是 sshd 的子进程,当 ssh 断开连接时,HUP 信号自然会影响到它下面的所有子进程(包括我们新建立的 ping 进程)。

2. 使用了 screen 后新进程的进程树

1

2

3

4

5

6

7

8

9

[root@pvcent107 ~]# screen -r Urumchi

[root@pvcent107 ~]# ping www.ibm.com &

[1] 9488

[root@pvcent107 ~]# pstree -H 9488

init─┬─Xvnc

├─acpid

├─atd

├─screen───bash───ping

├─2*[sendmail]

而使用了 screen 后就不同了,此时 bash 是 screen 的子进程,而 screen 是 init(PID为1)的子进程。那么当 ssh 断开连接时,HUP 信号自然不会影响到 screen 下面的子进程了。

总结

现在几种方法已经介绍完毕,我们可以根据不同的场景来选择不同的方案。nohup/setsid 无疑是临时需要时最方便的方法,disown 能帮助我们来事后补救当前已经在运行了的作业,而 screen 则是在大批量操作时不二的选择了。

Linux: su sudo sudoer 详解

No Comments Linux

日常操作中为了避免一些误操作,更加安全的管理系统,通常使用的用户身份都为普通用户,而非root。当需要执行一些管理员命令操作时,再切换成root用户身份去执行。

普通用户切换到root用户的方式有:su和sudo。

1su –

(su为switch user,即切换用户的简写)

格式:su -l USERNAME(-l为login,即登陆的简写)

-l可以将l省略掉,所以此命令常写为su – USERNAME

如果不指定USERNAME(用户名),默认即为root,所以切换到root的身份的命令即为:su -root或是直接 su –

实例1:普通用户user1知道root账户登录密码,要求用户user1在不注销登录的前提下查看/etc/shadow文件。

如下图,试图查看文件/etc/shadow时,提示拒绝访问,此时使用su – 命令切换成root身份后,即可正常查看。

wKioL1MYJeaAhcKsAAD9QQoiUrE786.jpg

之后,通过命令exit或logout,或者是快捷键Cry+D即可返回原用户身份。

2su – su

通过su切换用户还可以直接使用命令su USERNAME,与su – USERNAME的不同之处如下:

su – USERNAME切换用户后,同时切换到新用户的工作环境中

su USERNAME切换用户后,不改变原用户的工作目录,及其他环境变量目录

如下图,显示两个命令的执行结果:

wKiom1MYJlWxFr_lAAMqGjIx9ck020.jpg

3sudo

使用su切换用户时需知晓对应用户的登陆密码,即若切换成root用户身份,需知道root用户的登陆密码。作为root用户管理员,如何授权其他普通用户,在不需要知晓root密码的情况下,执行root权限的命令操作?此时即可使用sudo。

sudo是一种权限管理机制,依赖于/etc/sudoers,其定义了授权给哪个用户可以以管理员的身份能够执行什么样的管理命令;

格式:sudo -u USERNAME COMMAND

当普通用户通过sudo以root用户执行命令时,sudo后面的 -uUSERNAME可省略,即sudo COMMAND 即意为sudo以root用户执行

默认情况下,系统只有root用户可以执行sudo命令。需要root用户通过使用visudo命令编辑sudo的配置文件/etc/sudoers,才可以授权其他普通用户执行sudo命令。

如下图,假如使用普通用户帐号user4通过sudo以root用户身份执行命令tail /etc/shadow时,即被提示:user4未被定义在sudoers文件中,无法执行此命令。

wKioL1MYJq_gEwGLAACaypu3XLY540.jpg

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

sudo命令

语法:sudo [-bhHpV][-s ][-u <用户>][指令]

或 sudo [-klv]

参数:

-b  在后台执行指令。

-h  显示帮助。

-H  将HOME环境变量设为新身份的HOME环境变量。

-k  结束密码的有效期限,也就是下次再执行sudo时便需要输入密码。

-l  列出目前用户可执行与无法执行的指令。

-p  改变询问密码的提示符号。

-s  执行指定的shell。

-u <用户>  以指定的用户作为新的身份。若不加上此参数,则预设以root作为新的身份。

-v  延长密码有效期限5分钟。

-V  显示版本信息。

-S   从标准输入流替代终端来获取密码

4sudoers

sudo的配置文件为:/etc/sudoers。

sudoers文件中允许指定用户在不需要知道root用户的登陆密码的情况下,可以以root用户身份运行各种命令。此文件必须使用visudo命令编辑配置。(visudo命令可以提供basic sanitychecks和check for parse errors,即提供快速的正确性有效性检查,以及语法检查功能)

查看sudores文件,其中有一行如下图,定义了允许root用户从任何主机登陆,使用sudo可以切换成任何用户的身份,执行所有命令。

wKioL1MYJ0CRJZGSAAD9-EL6Zs4959.jpg

查看sudoers文件,其中有两行如下图,定义了组可以使用sudo命令的配置。

wKioL1MYKSXhpQ2gAAEpRR0S2BI482.jpg

实例2:设置普通用户user4,使其可以使用sudo命令以root用户身份修改其他所有用户登录密码,但不能修改root用户登陆密码

未被授权前,user4使用sudo以root用户修改user1的密码时,提示user4未被定义在sudoers文件中,无法执行,并且此事件将被报告给。如下图:

wKioL1MYKFqgSw4LAADSYt-W86g812.jpg

执行visudo命令,编辑sudoers文件,添加一行:用户帐号为user4;可以从任何主机登陆,执行三条命令(以root身份执行passwd命令后面不加用户名时,即代表修改root用户本身的密码,此即为第一条命令的作用),如下图,即可实现user4可以修改除root用户之外的其他所有用户的登录密码。

wKioL1MYKcuzFrpjAAFrMkDPLk8371.jpg

之后,user4通过sudo以root用户身份即可成功修改user1的密码(而不需要知道root的密码,只需要输入自己的密码即可),同时无法修改root的密码,如下图:

wKioL1MYKpSik_yhAAHf8r3lz9I443.jpg

实例3:设置组Administrators内所有成员都可以通过sudo以root用户身份执行所有命令,且不需要验证本身的账户密码。

通过visudo命令编辑sudoers文件,添加如下一行,即完成配置。

wKioL1MYKu-R3Q8bAADr7M4i6k8642.jpg

之后,成员一user1,即可通过sudo以root用户执行所有命令,且不需要验证本身账户密码。如下图:

wKiom1MYK1PSLHqMAAHGaD1SSqI883.jpg

附:man文档中susudo的解释:

su – run a shell with substitute user andgroup IDs

以替代的用户运行shell。(即su之后,在当前shell上的用户身份已转变)

sudo – excute a command as another user.

sudo allows a permitted user to execute acommand as the superuser or another user, as specified by security policy.

以其他用户身份执行命令。sudo依照安全策略中指定,允许授权用户以超级用户或是其他用户身份执行命令。(即sudo,只是临时以其他用户身份执行命令,并不会切换身份)

su -c

当然,su也可以在不切换用户身份的情况下,临时以其他用户执行命令。

通过选项-c,即可使用root身份临时执行命令,如下图:

wKioL1MYK6jhhTU9AAC4bcln_Q0792.jpg

/bin/su –

同时,也可以通过配置sudoers文件,授权其他普通用户,可以切换成其他用户身份去执行命令,而不必每次都加上sudo。如下图,只需在sudoers文件中定义普通用户user4

wKiom1MYLBTgm8QIAAB0yKoZYJY963.jpg

之后,用户user4只需执行一次sudo su – 即可切换成root身份了

wKioL1MYLCXQwUEUAADFU-xnFAg740.jpg

注:实例环境为Vmware Workstation 9、CentOS 6.4