Linux操作系统
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

6.3 进程管理与调度命令

6.3.1 可执行文件的setuid、setgid权限和目录的sticky属性

在UNIX/Linux系统中,用户级别和执行权力是相关的。但是有些工作,如修改/etc/passwd和/etc/shadow文件等,对于普通用户也是必需的,因为它要修改自己的密码。但是,系统中的这些文件对于普通用户是不允许有写操作的,于是就出现了让普通用户通过某种机制行使超级用户权限的问题。

1.suid和sgid权限

1)概念

对于可执行文件,有些文件还有另外两个与执行有关的属性:

(1)设置用户ID权限:setuid/suid。

(2)设置组ID权限:setgid/sgid。

当一个程序具有suid属性时,它执行时的uid将是该程序所有者的uid,即有效uid,记为euid;而执行者原来的uid称为真实uid,记为ruid。此时,程序的执行者具有程序所有者的权限。

当一个程序具有sgid属性时,它执行时的gid将是该程序的gid,即有效gid,记为egid;而执行者原来的gid称为真实gid,记为rgid。此时,程序的执行者具有程序所有者所在组的权限。

suid/sgid属性只对二进制可执行文件有效,对可执行的脚本文件无效。

当一个属于超级用户的可执行二进制程序具有suid属性时,在一般用户执行它时,也就相当于有了超级用户的身份和权限。同样,当一个属于超级用户所在组的可执行二进制程序具有sgid属性时,则当一般用户执行它时,相当于有了该组员的身份和权限。由此可见,suid和sgid权限的存在可能会给某些程序的执行带来方便,因为权限被放大了,但这也是一种危险,因为过大的权力可能会给系统的安全带来隐患,因此设置这种权限的可执行二进制程序不宜太多。

常用设置suid的程序是/bin/ping、/usr/bin/passwd、/usr/bin/newgrp、/usr/sbin/suexec、/usr/bin/sudo和/usr/sbin/traceroute等被公用的但又必须具有较高权限的程序。

2)管理

按照文件的权限,若用八进制表示,它的u、g、o的读、写和执行权限可分别表示为400、40、4,200、20、2和100、10、1,对应于suid和sgid的权限则为4000和2000。

suid和sgid对应的权限也是用chmod来管理的。设myp1、myp2、myp3的权限分别为:-rwxr-xr-x(755)、-rwxrwxr-x(775)和-rwxrwxrwx(777),则可用以下方法设置suid和sgid权限。

(1)字符方式:

                #chmod u+s myp1      #为myp1设置suid。修改后权限为rwsr-xr-x
                #chmod g+s myp2      #为myp2设置sgid。修改后权限为rwxrwsr-x
                #chmod ug+s myp3     #为myp3同时设置suid和sgid。修改后权限为rwsrwsrwx

(2)数字方法:

                #chmod 4755 myp1     #为myp1设置suid
                #chmod 2775 myp2     #为myp2设置sgid
                #chmod 6777 myp3     #为myp3同时设置suid和sgid

2.sticky位

1)概念

在4位八进制权限的最高1位的3位二进制数中,已经被suid和sgid各用去了1位,还剩下1位,这1位在UNIX/Linux系统中可以分别用来描述目录或文件的另一个属性。当用于描述目录时,被叫做限制删除位;当用于描述可执行文件时,被叫做黏着位。为了描述方便,把它统一称为sticky位或sticky权限。

当一个目录具有sticky权限时,它内部的文件只能被其所有者或超级用户删除、更名或移动。当一个可执行文件具有sticky权限时,在早期的系统中,程序执行时的正文段映像将被锁在交换区中,可使程序安装更快速。

设置黏着位的目录有/tmp、/usr/tmp和/var/spool/uucppublic等。

2)管理

sticky位的属性值为1000,可通过chmod命令设置。设目录mydir和文件myfile的权限为755,则可通过以下方法来设置它的sticky位。

                #chmod+t mydir myfile         #字符方式。修改后权限为1755或rwxr-xr-t
                #chmod 1755 mydir myfile      #数字方式

3.查找

用户可以使用find命令查找系统具有suid、sgid和sticky权限的文件,方法如下:

                find/-perm+1000-print     #仅查找具有sticky位者
                find/-perm+2000-print     #仅查找具有sgid权限者
                find/-perm+4000-print     #仅查找具有suid权限者
                find/-perm+6000-print     #仅查找具有suid和sgid权限者
                find/-perm+7000-print     #查找具有suid、sgid和sticky 3种权限者

6.3.2 进程管理与调度命令

1.查询进程状态(ps)

1)功能与用法

ps命令的功能是显示进程状态和信息,给出系统当前正在运行进程信息的快照,可以配合kill命令结束系统中失控或不必要的进程。ps命令支持多种UNIX系统格式的个性化显示方式,其用法为:

                ps [ options ]

2)参数说明

ps命令的参数繁多,部分参数如表6-2所示。

表6-2 ps命令的部分参数

3)进程的状态

在ps命令的输出信息中的状态标志如表6-3所示。

表6-3 在ps命令的输出信息中的状态标志

4)示例

                #ps-t tty1          #查询与终端(tty1)相关的进程信息
                #ps-ef|more       #查询系统中的所有命令及参数
                #ps-f-u gjshao     #查询与用户(gjshao)相关的信息
                #ps-axj            #查询整个系统内的进程信息情况

2.按名称向进程发信号或终止进程的执行(killall)

1)功能与用法

kill命令用于向指定进程或进程组发送指定信号(参见表6-1),如果未指定信号,则默认发送TERM(15)。若接收者收到TERM信号,没有对信号捕获并进行特殊处理,则它将被终止运行,也就是“被杀掉了”,因此把发送信号功能的命令称为kill。对那些不能用TERM“杀掉”的进程可以使用信号kill(9)来处理,因为kill是不能被捕获的。

killall命令是另一个用来向指定进程发信号或终止进程的命令,它不像kill命令使用进程的pid作为操作对象,而是使用进程名。由于在系统中可能有多个同名进程同时运行,因此使用killall命令终止进程时可能会有扩大化的倾向。其用法为:

                killall [-e] [-g] [-i] [-q] [-v] [-w] [-Ⅴ] [-s sig] [--] name …
                killall -l

killall发送的信号可以是信号名或值。

2)参数说明

killall命令的部分参数如表6-4所示。

表6-4 killall命令的部分参数

3)示例

                #killall-l       #查询可用信号
                #killall man     #终止所有名为man的进程
                #killall-9 bash   #终止所有名为bash的进程,将造成所有终端用户退出系统

3.确定使用指定文件或文件系统的进程(fuser)

1)功能与用法

在系统管理和shell编程时,经常需要确定使用某个设备或文件系统的进程和用户的情况,fuser命令可以完成此工作,其用法为:

                fuser[-a|-s][-n namespace][-signal][-kimuv]name …
                fuser -l

2)参数说明

fuser命令的部分参数如表6-5所示。

表6-5 fuser命令的部分参数

在fuser的默认输出中,进程号的后面可以跟有c、e、f、r、m等字符,意义如下:c,当前目录;e,进程正在执行;f,打开文件,默认情况下不输出此标志;r,根目录;m,内存映像文件或共享库。

3)示例

(1)杀死所有使用某设备的进程。有时需要让使用某设备或文件系统的所有进程终止,例如,要把使用终端/dev/tty5的用户踢出系统,可采用终止使用该设备所有进程的办法来实现。

#fuser -k /dev/tty5

(2)显示使用本地网络端口的进程。从网络监控的角度来讲,有时需要了解系统中哪个端口正在被哪个进程或用户使用,有多少个进程在使用。

                #fuser telnet/tcp ftp/tcp       #监视本地telnet和ftp端口的tcp活动情况
                #fuser-u 23/tcp 21/tcp       #监视本地telnet和ftp端口的tcp活动情况
                #fuser-n tcp 21            #监视本地ftp端口的tcp活动情况

4.让进程抗SIGHUP运行(nohup)

在UNIX/Linux环境下,进程可在前台运行,也可在后台运行。但不论哪种情况,只要启动进程的用户退出了系统或终端线路断路了,则他创建的所有进程都将终止,这是因为它们都将收到HUP信号。按照系统规定,收到HUP信号的进程都将被终止。为了保证用户退出后,启动的后台进程仍能继续运行,要求进程对信号HUP具有免疫能力。使用nohup命令启动的进程对信号HUP具有免疫力。nohup命令的用法为:

                nohup command [ arguments ]

后台进程是不能和终端交互的。如果输入没有被重定向,则定向到/dev/null。如果输出没有重定向,它将输出追加到当前目录的nohup.out文件。如果该文件没有写权限,则输出将被重定向到$HOME/nohup.out。如果两者均无写权限,则命令不能执行。如果有写权限且nohup.out不存在,则创建它,在创建时不对同组和其他人赋访问权(权限为600)。若文件已经存在,则不改变原文件的存取权限。程序未重定向的标准错误也将被追加到nohup.out文件。

nohup不能直接创建后台进程,创建后台进程应在回车前添加“&”符号。尽管没有输出,但仍可以通过其返回值来确定其执行情况,其返回值及意义为:126,命令不能执行;127,命令找不到;其他,所执行命令的返回值。

5.改变进程的优先级(nice)

1)功能与用法

nice命令可用于进程调度与管理,通过它可以改变进程的(静态优先数)NICE值,从而改变进程获得调度的机会。对于那些不急迫的任务,可以通过调高它的NICE值,来降低它的优先级,以保证紧急任务的执行。当然,也可以指定一个负的NICE值来提高进程的优先级。nice命令的用法为:

nice [options] [cmd [arg … ]]

当不带参数或变量运行nice时,将显示默认的NICE值,如果用nice对某个进程进行操作,而没有指定NICE值,则将NICE值设置为10,从而降低优先级。如果要设置负的NICE值,以提高进程的优先级,则要求用户必须具有超级用户权限。关于NICE值的设定可以使用以下形式:

              -n<NICE>             例如,-n5,NICE值为5;-n-5,NICE值为-5
              -<NICE>              例如,-5,NICE值为5;--5,NICE值为-5
              --adjustment=<NICE>    例如,--adjustment=5,NICE值为5

2)示例

                #设程序myp可以在前、后台运行
                #myp&            #以默认优先级运行
                #nice myp&        #以默认优先数(10)降低myp的运行级
                #nice-15 myp&     #以15为优先数运行程序myp
                #nice-n-15 myp&    #以-15为优先数运行程序myp
                #ps-l              #查询进程myp的运行级和NICE值
                #输出中的S栏为进程状态,PRI栏为优先级,NI为NICE值

6.进程挂起或作业的前/后台运行切换(fg、bg和jobs)

当一个交互式进程正在运行时,用户可以通过Ctrl+Z暂停其当前执行,将它挂起。之后,可让其在后台等待运行,也可让其从后台再重新回到前台运行。实现这些功能的命令是fg和bg,它们都是bash的内部命令。此外,还有jobs命令,用于作业队列查询和管理。它们的用法为:

                fg [job]
                bg [job]
                jobs [options] [job …]

fg命令(foreground)的功能是让被挂起的进程或作业到前台运行;bg命令(background)的功能是让被挂起的进程或作业在后台运行,就像用符号“&”启动的那样。若不指定job,则默认使用当前作业。

设用户正在运行man bash,此时可以通过Ctrl+Z将其挂起,屏幕显示以下信息后并出现提示符:

                [1]+  stopped  man bash

表明名为man的进程被挂起,作业编号为1,+表示此为当前作业,用户可以在提示符下继续做其他工作。如果再启动一个进程,如计算器bc,则此时进入bc交互界面,用户同样可以通过Ctrl+Z将其挂起。

                [2]+  stopped  bc

系统回到提示符。运行jobs命令可得以下输出:

                [1]-  stopped  man bash
                [2]+  stopped  bc

由此可见,有两个作业,编号分别为1和2,其中2为当前作业。此时可以通过命令:

                # fg [job]

或 # bg [job]

将作业调到前台或放在后台运行,job为jobs命令列出的作业号,如1、2等。如果输入:

                # fg

或 # fg 2

将使bc调到前台执行,而输入:

                # fg 1

将使作业man调到前台执行。同样,输入:

                bg 1

也可使man命令在后台运行,但交互式命令在后台运行无意义。

6.3.3 与进程身份和位置相关的命令

1.以其他用户身份执行程序(sudo)

1)功能与用法

前面已经介绍过su命令可以在用户不退出系统的情况下,将自己变换为其他用户,或以其他用户的身份工作,这里再介绍一个这方面的命令——sudo(supper user do)。sudo允许用户以超级用户或其他用户的身份执行shell命令。使用sudo用户必须在文件/etc/sudoers中定义,没有在/etc/sudoers中定义的用户试图使用sudo时将向管理员发出警告性邮件。其用法为:

                sudo -h|-k|-l|-v|-Ⅴ
                sudo [-bH]|[-p str] [-u username|#uid] {-s|cmd}

2)参数说明

sudo命令的常用参数如表6-6所示。

表6-6 sudo命令的常用参数

3)示例

(1)设当前用户test没有执行sudo的权力:

                $ sudo -l
                ##Sorry, user test may not run sudo on Fedora9.
                $ sudo /sbin/shutdown now
                ##test is not in the sudoers file.  This incident will be reported.

(2)以root身份修改/etc/sudoers文件,在其中增加一行内容:

                test  ALL=(ALL)/sbin/shutdown

(3)然后再以test用户的身份执行命令:

                $ sudo -l
                ##User test may run the following commands on this host:
                ##     (ALL)/sbin/shutdown
                $ sudo /sbin/shutdown -r -y now

4)配置文件/etc/sudoers

sudoers是sudo的配置文件,通常在/etc目录下。系统为此配置文件提供一个专用的编辑命令visudo,除了编辑修改sudoers文件外,它还会帮使用者检查配置文件内容的正确性。如果不正确,会在保存退出时提示用户哪里出了问题。visudo的用法与vi相同。

(1)变量类型及定义。配置文件/etc/sudoers中除了注释行外,有两类描述项:一类是alias(变量);另一类是用户,描述一个用户能做什么工作。

有四种类型的变量定义:User_Alias(用户定义)、Runas_Alias(使用身份定义)、Host_Alias(主机定义)和Cmnd_Alias(可用命令定义)。

变量的定义形式如下:

                Alias_Type NAME=item1,item2, …

Alias_Type为以上四类别名定义的一种,NAME是大写字母开头的,由大写字母、数字和下画线组成的字符串。一个变量定义行中可以同时定义多个变量,变量之间用“:”分隔,例如:

                Alias_Type NAME1 = item1, item2, item3 : NAME2 = item4, item5

以上四种变量的具体定义可分别描述如下。

① User_Alias,具有使用sudo权限的用户的列表,定义格式如下:

                User_Alias   USERs=user1,user2,user3

在User_Alias定义中,右端的user可以是用户名、由“#”引导的UID、由“%”引导的组和由“+”引导的网络组等,任何一类用户前还可以使用“!”表示非操作。例如:

                User_Alias  FULLTIMERS=zhangsan,lisi,wangwu
                User_Alias  PARTTIMERS=test1,arm,oracle
                User_Alias  WEBMASTERS=oauser,www

② Host_Alias,主机的列表,定义格式如下:

                Host_Alias   HOSTs=hostname1,hostname2,hostname3

上式的右端可以是主机名、IP地址、网络地址和由“+”引导的网络组等,也可以在主机前使用“!”表示非操作。例如:

                Host_Alias  CUNETS=128.138.0.0/255.255.0.0
                Host_Alias  CSNETS=128.138.243.0,128.138.204.0/24,128.138.242.0
                Host_Alias  SERⅤERS=master,mail,www,ns

③ Runas_Alias,用户可以使用的身份列表,定义格式如下:

                Runas_Alias RUNASs=operator1,operator2,operator3

Runas_Alias与User_Alias定义方法相似,例如:

                Runas_Alias  OPs=root,operator
                Runas_Alias  DBs=oracle,sybase

④ Cmnd_Alias,允许执行的命令的列表,“!”引导的命令表示不能执行。

命令列表中的命令要使用绝对路径,避免其他目录的同名命令被执行,造成安全隐患。定义格式如下:

                Cmnd_Alias COMMANDs=command1,command2,command3,!command4

示例如下:

                Cmnd_Alias  KILL=/usr/bin/kill
                Cmnd_Alias  SHUTDOWN=/sbin/shutdown,/usr/sbin/halt,/usr/sbin/reboot
                Cmnd_Alias  NETWORKING=/sbin/route,/sbin/ifconfig,/bin/ping
                Cmnd_Alias  SERⅤICES=/sbin/service,/sbin/chkconfig

(2)权限设置。有了前面的准备就可以配置用户的执行或操作权限了。权限配置的格式如下:

                USERs HOSTs=(RUNASs) COMMANDs

其中,每个项目均可配置为ALL,例如:

                ALL ALL=(ALL) ALL

这意味着任何用户在任何位置均可使用sudo执行任何命令,权限控制太松了。

如果要使某类用户在执行某种操作时不需要密码验证,则可按照以下格式来配置:

                USERs HOSTs=(RUNASs) NOPASSWD: COMMANDs

具体配置示例如下:

                ## 允许root在任何位置执行任何命令
                root ALL=(ALL)    ALL
                ## 允许sys组中的用户执行NETWORKING、SERⅤICES和SHUTDOWN
                %sys ALL = NETWORKING, SERⅤICES, SHUTDOWN
                # #允许wheel组中的用户执行所有命令
                %wheel ALL=(ALL)ALL
                # #允许wheel组中的用户执行所有命令,并且不需要密码
                %wheel ALL=(ALL)NOPASSWD:ALL
                # #允许test1用户在本地执行SHUTDOWN
                test1 localhost = SHUTDOWN
                # #允许WEBMASTERS在CSNETS上以任何人的身份执行SERⅤERS
                WEBMASTERS CSNETS = (ALL) SERⅤERS

2.改变进程的家目录(chroot)

1)功能与用法

chroot(change root)的作用是改变根目录。其用法为:

                chroot  newroot  [cmd]

参数newroot为chroot改变以后使用的“根目录”,可以使用绝对路径或相对路径。cmd为以newroot为新“根”文件系统内存放的命令,如果不提供cmd,则默认执行环境变量SHELL指定的shell。如果指定了cmd,则cmd不能是一个通过符号链接指向newroot外部的文件。如果是指向newroot内部的,应以相对路径的方式链接,因为如果此时chroot能够成功,则cmd已经不能再看到newroot以外的整个文件系统了,从而使符号链接失效。

若要成功执行chroot,必须事前有一定的准备。首先,新的根目录newroot必须存在;其次,cmd命令必须事先在newroot目录内的合适位置存在,并且最好是采用静态链接生成的,以便成功改变根目录后以newroot为根目录时能够找到它,并能够独立执行,否则执行时所需要的动态库也必须出现在新根目录内的合适位置。为了找到cmd的共享库,可以使用ldd cmd命令查询,然后按照查询结果创建相应目录,并将相关的库和命令复制到newroot对应的目录内。

2)准备

以chroot newroot cmd为例说明chroot命令的准备和使用过程:

(1)查询newroot目录是否存在,若无,则先创建。

(2)查询SHELL环境变量的值,并记住该值。

(3)查询cmd的位置,并记住该位置。

(4)查询cmd所使用的共享库,并记住库名和位置。

(5)在newroot下创建命令和共享库所用的每个目录。

(6)将命令、共享库文件和其他必需的文件复制或(硬)链接到相应位置。

(7)执行命令chroot newroot cmd。

(8)若成功,按Ctrl+D或Exit键退出chroot。

第(1)到(6)步是真正的准备过程,而第(7)、(8)步是对准备工作的检验。

3)示例

这里以仅将目录切换到nroot,而不执行任何命令为例说明准备过程。尽管不执行任何命令,但默认要执行的是环境变量$SHELL所指定的命令。准备过程如下所示。

                #chroot nroot           #执行chroot nroot,得到以下错误,说明目录不存在
                # #chroot: cannot change root directory to nroot: No such file or directory
                #mkdir nroot           #创建目录nroot
                #chroot nroot           #再次执行chroot nroot,得到以下错误
                # #chroot: cannot run command `/bin/bash': No such file or directory
                # #说明/bin/bash不存在。创建目录nroot/bin并复制文件/bin/bash到其中
                #mkdir nroot/bin;       #创建目录nroot/bin
                #cp$SHELL nroot/bin   #将/bin/bash复制到/nroot/bin
                #chroot nroot           #再次执行chroot nroot,仍得到以下错误
                # #chroot: cannot run command `/bin/bash': No such file or directory
                # #检查/bin/bash的支持库
                #ldd$SHELL              #检查/bin/bash的依赖库文件,得到以下输出
                    linux-gate.so.1=>  (0x00110000)
                    libtinfo.so.5 => /lib/libtinfo.so.5 (0x0280b000)
                    libdl.so.2 => /lib/libdl.so.2 (0x0093f000)
                    libc.so.6 => /lib/libc.so.6 (0x007d4000)
                    /lib/ld-linux.so.2 (0x007b4000)
                # #创建目录nroot/lib,并将以上依赖文件复制到其中
                # mkdir nroot/lib; cp /lib/libtinfo.so.5 nroot/lib; cp /lib/libdl.so.2 nroot/lib
                # cp /lib/libc.so.6 nroot/lib; cp /lib/ld-linux.so.2 nroot/lib
                #chroot nroot              #执行chroot nroot成功,输出以下提示符
                # # bash-3.2#

到此为止,准备工作完毕,下边执行几个命令以观察效果。

                #pwd                 #执行内部命令pwd,成功,得到输出为/
                #ls                   #执行外部命令ls,失败,得到以下错误提示
                # #bash: ls: command not found

因为ls为外部命令,没有为它准备,故在以nroot为根目录的新位置文件找不到它,因此不能执行。为了让/bin/ls能够执行,还需要继续准备,过程同前。

由上可见,为chroot做准备工作并不太容易,不过就一般的应用来讲,所需文件并不太多。以后将看到很多的网络应用程序是采用这种方式运行的,以保证系统的安全。