进程优先级
进程cpu资源分配就是指进程的优先权(priority)。优先权高的进程有优先执行权利。
权限与优先级
权限(privilege)是指在多用户计算机系统的管理中,某个特定的用户具有特定的系统资源使用权力,像是文件夹,特定系统指令的使用或存储量的限制。权限是有或没有的问题,而优先级则是在已经具有了权限而讨论权限大小的问题。配置进程优先权对多任务环境的linux很有用,可以改善系统性能。还可以把进程运行到指定的CPU上,这样一来,把不重要的进程安排到某个CPU,可以大大改善系统整体性能。用“ps -l”指令查看系统进程:
注意到其中几个很重要的信息,PID(进程的代号)和PPID(父进程的代号)在前面的进程标识符里已经说过。UID代表执行者的身份。什么意思呐?比如说为什么持有身份证才可以在银行办理业务,因为身份证证明了你是中国合法公民,拥有合法权限,你以中国合法公民的身份在柜台办理业务;再比如学生可以在学校里上课、使用教学设施等等,那是他经过一定的手续成为本校的学生,他是以本校学生的身份在学校学习,而老师则以本校教师的身份在学校教书。当我们创建用户时,由我们为新建用户命名和设置密码,同时系统会为我们所创建的用户名关联一个号,就是所谓的用户uid,即用户的身份。通常用户的身份有普通用户和超级用户。进程uid与运行该进程的用户uid相同。PRI即进程的优先级,或者通俗点说就是程序被CPU执行的先后顺序,此值越小进程的优先级别越高,越早被执行。NI就是我们所要说的nice值了,其表示进程可被执行的优先级的修正数值。如前面所说,PRI值越小越快被执行,那么加入nice值后,将会使得PRI变为:PRI(new)=PRI(old)+nice。
由此看出,PR是根据NICE排序的,规则是NICE越小PR越前(小,优先权更大),即其优先级会变高,则其越快被执行。如果NICE相同则进程uid是root的优先权更大。这样,当nice值为负值的时候,那么该程序将会优先级值将变小,即其优先级会变高,则其越快被执行。到目前为止,更需要强调一点的是,进程的nice值不是进程的优先级,他们不是一个概念,但是进程nice值会影响到进程的优先级变化。如果原来的PRI是50,并不是我们给予一个nice=5,就会让PRI变成55.因为是系统“动态”决定的,所以,虽然nice值是可以影响PRI,但最终的PRI仍要经过系统分析后才能决定。
修改进程优先级
修改进程优先级的命令主要有两个: nice,renice
1、一开始执行程序就指定nice值: nice
# nice -n -5 /usr/local/mysql/bin/mysqld_safe &
linux nice 命令详解
功能说明:设置优先权。
语 法: nice [-n <优先等级>][--help][--version][执行指令]
补充说明: nice指令可以改变程序执行的优先权等级。
参 数: -n<优先等级>或-<优先等级>或–adjustment=<优先等级> 设置欲执行的指令的优先权等级。等级的范围从-20-19,其中-20最高, 19最低,只有系统管理者可以设置负数的等级。
范例一: 用root给一个nice值为-5,用于执行vi,并观察该进程。
# nice -n -5 vi &
注意:
(1)一般用户的nice值为0~19;
(2)root可用的nice值为-20~19;
(3)一般用户仅可将nice值越调越高,如果本来nice为5,则只能调整到大于5的nice:
(4)一般用户仅能调整属于自己的进程nice值。
也就是说,要调整某个进程的优先级,就是“调整该进程的nice值”。
2、调整已存在进程的nice: renice
renice -5 -p 5200
#PID为5200的进程nice设为-5
linux renice 命令详解
功能说明:调整优先权。
语 法: renice [优先等级] [-g <程序群组名称>...] [-p <程序识别码>...] [-u <用户名称>...]
补充说明: renice指令可重新调整程序执行的优先权等级。预设是以程序识别码指定程序调整其优先权,您亦可以指定程序群组或用户名称调整优先权等级,并修改所有隶属于该程序群组或用户的程序的优先权。等级范围从-20–19,只有系统管理者可以改变其他用户程序的优先权,也仅有系统管理者可以设置负数等级。
参 数:
-g <程序群组名称> 使用程序群组名称,修改所有隶属于该程序群组的程序的优先权。
-p <程序识别码> 改变该程序的优先权等级,此参数为预设值。
-u <用户名称> 指定用户名称,修改所有隶属于该用户的程序的优先权。
范例二 : 将4222那个PID修改nice为-5。
也可以用top命令更改已存在进程的nice:
top
#进入top后按“r”–>输入进程PID–>输入nice值
进程创建执行
当进程执行时,它会被装载进虚拟内存,为程序变量分配空间,并把相关信息添到task_struct中。
进程内存布局分为四个不同的段:
• 文本段,包含程序的源指令。
• 数据段,包含了静态变量。
• 堆,动态内存分区区域。
• 栈,动态增长与收缩的段,保存本地变量。
这里有两种创建进程的方法, fork()和execve()。它们都是系统调用,但它们的运行方式有点不同。要创建几个一进程可以执行fork()系统调用。 然后子进程会得到父进程中数据段,栈段和堆区域的一份拷贝。子进程独立可以修改这些内存段。但是文本段是父进程和子进程共享的内存段,不能被子进程修改。
fork函数用于从已存在进程中创建一个新进程,新进程成为子进程,原进程成为父进程。这两个进程分别返回他们各自的返回值,其中父进程的返回值是子进程的进程号,子进程则返回0,因此返回值大于0标识父进程,等于0标识子进程。所以我们可以通过返回值来判定该进程是父进程还是子进程。fork函数创建新进程后的父子进程模型如下:
下面用一个简单程序来观察父子进程之间的运行过程:
该代码运行结果如下:
fork调用的一个奇妙之处就是它仅仅被调用一次,却能够返回两次,它可能有三种不同的返回值:
1)在父进程中,fork返回新创建子进程的进程ID;
2)在子进程中,fork返回0;
3)如果出现错误,fork返回一个负值;
fork出错可能有两种原因:
1)当前的进程数已经达到了系统规定的上限,这时errno的值被设置为EAGAIN。
2)系统内存不足,这时errno的值被设置为ENOMEM。
创建新进程成功后,系统中出现两个基本完全相同的进程,这两个进程执行没有固定的先后顺序,哪个进程先执行要看系统的进程调度策略。
execve()函数创建一个新进程。这个系统调用会销毁所有的内存段去重新创建一个新的内存段。然后,execve()需要一个可执行文件或者脚本作为参数,这和fork()有所不同。注意,execve()和fork()创建的进程都是运行进程的子进程。
父进程fork一个子进程之后,在已存在的子进程中调用exec函数启动新的程序。新程序替换了当前进程的文本段、数据段、堆、栈。进程ID不改变。exec函数一共有七个,其中execve为内核级系统调用,其他(execl,execle,execlp,execv,execvp,fexecve)都是调用execve的库函数,我们可以使用7个函数中的任意一个。
函数定义: int execve(const char *filename, char *const argv[ ], char *const envp[ ]);
返回值: 函数执行成功时没有返回值,执行失败时的返回值为-1;
函数说明: execve()用来执行参数filename字符串所代表的文件路径,第二个参数是利用数组指针来传递给执行文件,并且需要以空指针(NULL)结束,最后一个参数则为传递给执行文件的新环境变量数组。
1 #include<unistd.h> 2 main() 3 { 4 char *argv[ ]={"ls", "-al", "/etc/passwd", NULL}; 5 char *envp[ ]={"PATH=/bin", NULL} 6 execve("/bin/ls", argv, envp); 7 }
运行结果为:-rw-r--r-- 1 root root 1659 Feb 27 20:13 /etc/passwd
这与在bin目录下执行 ls -al /etc/passwd 所得到的结果是一样的。
fork是分身,execve是变身。
exec系列的系统调用是把当前程序替换成要执行的程序,而fork用来产生一个和当前进程一样的进程(虽然通常执行不同的代码流)。通常运行另一个程序,而同时保留原程序运行的方法是,fork+exec。