ITPub博客

首页 > Linux操作系统 > Linux操作系统 > 进程与信号(二)

进程与信号(二)

原创 Linux操作系统 作者:mylxiaoyi 时间:2009-08-10 19:16:00 0 删除 编辑

进程调度

ps输出的一个特点就是ps命令实体本身:

1357 pts/2 R 0:00 ps -ax

这表明进程1357处于运行状态(R)并且正在执行命令ps -ax。所以进程是在其自身的输出中被描述的。状态指示器只是表明程序已经准备好运行,并不一定是在实际运行。在单处理器的计算机上,每次只能运行一个进程,而其他的进程必须依次等待。这些轮序,就是所谓的时间片,非常短,从而给我们一种感觉,所有的程序是在同时运行的。状态R只是表示程序并没有等待其他的进程完成或是等待输入或输出来结束。这就是为什么我们会在ps输出中看到两个这样的进程。(另一个通常会看到的标识为运行的进程就是X显示服务器)

Linux内核使用一个进程调度器来决定哪一个进程将会接受下一个时间片。他是通过使用进程优先级(我们在第4章讨论了优先级)来做到的。具有高优先级的进程会具有更高的运行频率,而其他的,例如低优先级的后台任务,就具有较低的运行频率。在Linux中,进程不能超过分配给他们的时间片。老的系统,例如Windows 3.x,通常需要进程显示放弃,从而其他的进程可以重新运行。

在多任务系统中,例如Linux,多个程序也许会竞争同一个资源,那些执行大量任务并且暂停等待输入的程序被认为要比独占处理器来连续的计算一些值或是连续的查询系统来查看是否有新的输入可用的方式要好得多。从术语来说,我们称之为nice程序,而且从常识来说,这个"niceness"是可以度量的。操作系统依据一个"nice"值以及程序的行为来确定一个进程的优先级,其默认值为0。长时间运行而没有暂停的程序通常会具有较低的优先级。例如,程序暂停等待满足输入。这有助于保持程序与用户进行交互;当其等待用户的某些输入时,系统会增加其优先级,这样当他满足重新运行的条件时,他就具有一个较高的优先级。我们可以使用nice程序来设置进程的nice值,并且使用renice来重新调整进程的nice值。nice命令会为一个进程的nice值增加10,从而为其指定一个较低的优先级。我们可以使用ps命令的-l或是-f选项来查看活动进程的nice值。我们所感兴趣的值显示在NI列中。

$ ps -l
F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD
000 S 500 1259 1254 0 75 0 - 710 wait4 pts/2 00:00:00 bash
000 S 500 1262 1251 0 75 0 - 714 wait4 pts/1 00:00:00 bash
000 S 500 1313 1262 0 75 0 - 2762 schedu pts/1 00:00:00 emacs
000 S 500 1362 1262 2 80 0 - 789 schedu pts/1 00:00:00 oclock
000 R 500 1363 1262 0 81 0 - 782 - pts/1 00:00:00 ps

从这里我们可以看出oclock程序以一个默认的nice值在运行。如果他是由下面的命令来启动的

$ nice oclock &

那么他就已经被分配了一个+10的nice值。如果我们用下面的命令来进行调整

$ renice 10 1362
1362: old priority 0, new priority 10

那么oclock就会更少的运行频率。我们可以再次使用ps命令来查看修改的nice值:

F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD
000 S 500 1259 1254 0 75 0 - 710 wait4 pts/2 00:00:00 bash
000 S 500 1262 1251 0 75 0 - 714 wait4 pts/1 00:00:00 bash
000 S 500 1313 1262 0 75 0 - 2762 schedu pts/1 00:00:00 emacs
000 S 500 1362 1262 0 90 10 - 789 schedu pts/1 00:00:00 oclock
000 R 500 1365 1262 0 81 0 - 782 - pts/1 00:00:00 ps

状态列现在包含N来表明nice值已经由默认值进行修改。ps输出的PPID域表明父进程ID,使得PID进程启动的进程,如果这个进程不再运行,为init(PID 1)。

Linux调度器依据优先级来决定允许哪个进程运行。当然,各个实现会有所不同,但是高优先级具有更高的运行频率。在某些情况下,如果高优先级进程已经准备运行,低优先级进程根本就不会运行。

启动一个新进程

我们可以使得一个程序由另一个进程的内部来运行,从而通过使用system库函数来创建一个新的进程。

#include
int system (const char *string);

system函数运行作为字符串传递给他的命令并且等待其结束。这个命令的运行与下面命令的运行结果相同:

$ sh -c string

如果shell并没有启动来运行这个命令,system就会返回127,如果发生了其他错误则会返回-1。否则system返回这个命令的退出代码。

试验--system

我们可以使用system来编写一个程序为我们运行ps命令。尽管这个程序并不是十分有用,我们会在后面的例子中看到如何来开发这个技术。在这个例子中我们并不十分严格的检测system调用是否适用于这种情况。

#include
#include
int main()
{
printf(“Running ps with system\n”);
system(“ps -ax”);
printf(“Done.\n”);
exit(0);
}

当我们编译并运行这个程序时,system1.c,我们会得下面的输出:

$ ./system1
Running ps with system
PID TTY STAT TIME COMMAND
1 ? S 0:05 init
2 ? SW 0:00 [keventd]
...
1262 pts/1 S 0:00 /bin/bash
1273 pts/2 S 0:00 su -
1274 pts/2 S 0:00 -bash
1463 pts/1 S 0:00 oclock -transparent -geometry 135x135-10+40
1465 pts/1 S 0:01 emacs Makefile
1480 pts/1 S 0:00 ./system1
1481 pts/1 R 0:00 ps -ax
Done.

因为system1函数使用一个shell启动所要求的程序,我们可以通过修改system1.c中的函数调用将其放在后台运行:

system(“ps -ax &”);

当我们编译运行这个版本的程序时,我们会得到下面的输出:

$ ./system2
Running ps with system
PID TTY STAT TIME COMMAND
1 ? S 0:05 init
2 ? SW 0:00 [keventd]
...
Done.
$ 1246 ? S 0:00 kdeinit: klipper -icon klipper -miniicon klipper
1274 pts/2 S 0:00 -bash
1463 pts/1 S 0:00 oclock -transparent -geometry 135x135-10+40
1465 pts/1 S 0:01 emacs Makefile
1484 pts/1 R 0:00 ps -ax

工作原理

在第一个例子中,程序使用"ps -ax"字符串来调用system,这会运行ps程序。当ps命令已经完成时,我们的程序会这个调用返回到system。system程序十分有用,但却十分有限。因为我们的程序必须等待直到system调用所启动的进程结束,而我们不得进行其他的任务。

在第二个例子中,system调用在shell命令结束时立即返回。因为他要求在后台运行一个程序,当ps程序启动时shell就会立即返回,就如同我们在shell提示符下输入下面的命令一样:

$ ps -ax &

system2程序在ps命令有机会完成其所有的的输出之前输出Done.并退出。ps命令会在system2退出之后继续产生输出。这种进程的行为会使得用户十分迷惑。要更好的利用进程,我们需要更好的控制其动作。下面我们来看一下进程的底层接口,exec。

注:通常而言,system并不启动其他进程的一个完美方法,因为他使用一个shell来调用所要求的程序。这样的效率并不高,因为shell是在程序启动之前启动的,而且十分依赖于shell的安装与所用的环境。在这一节,我们将会看到调用程序的一个更好的方法,其使用总是优先于system调用。

替换一个进程映像

有一个以exec开头的相当函数族。他们的不同在于他们启动进程与表过程序参数的方式。一个exec函数使用由path与file参数所指定的新进程来替换当前的进程。

#include
char **environ;
int execl(const char *path, const char *arg0, ..., (char *)0);
int execlp(const char *file, const char *arg0, ..., (char *)0);
int execle(const char *path, const char *arg0, ..., (char *)0, char *const
envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execve(const char *path, char *const argv[], char *const envp[]);

这些函数属于两类。execl,execlp,execle带有多个参数,并以空指针结束。execv与execvp的第二个参数是一个字符串数组。在这两种情况下,出现在argv数组中的指定参数传递给main,并启动一个新程序。这些函数通常都是使用execve来实现的,尽管并没有要求以这种方式实现。

函数为以p为后缀的函数与其他函数的不同在于他们会查找PATH环境变量来查找新的程序可执行文件。如果可执行文件并不在这个路径中,就需要将一个包含目录的绝对文件名作为参数传递给函数。

全局变量environ可以为新程序环境传递一个值。相对应的,execle与execve的另一个参数可以传递一个字符串数组用作新程序环境。

如果我们希望使用exec函数来启动ps程序,我们需要在6个exec函数族中作出选择,如下面的代码段所示:

#include

/* Example of an argument list */
/* Note that we need a program name for argv[0] */
char *const ps_argv[] = {"ps","-ax",0};

/* Example evnironment, not terribly useful */
char *const ps_envp[] = {"PATH=/bin:/usr/bin","TERM=console",0};

/* Possible calls to exec functions */
execl("/bin/ps","ps","-ax",0);
execlp("ps","ps","-ax",0); /* assumes ps in /bin */
execle("/bin/ps","ps","-ax",0,ps_envp); /* passes own environment */

execv("/bin/ps",ps_argv);
execvp("ps",ps_argv);
execve("/bin/ps",ps_argv,ps_envp);

试验--execlp

下面我们来修改我们的例子来使用execlp调用。

#include
#include
int main()
{
printf(“Running ps with execlp\n”);
execlp(“ps”, “ps”, “-ax”, 0);
printf(“Done.\n”);
exit(0);
}

当我们运行这个程序,pexec.c,我们会是到通常的ps的输出,但是根本没有Done.信息。我们还要注意到,在输出并没有名为pexec的进程。

$ ./pexec
Running ps with execlp
PID TTY STAT TIME COMMAND
1 ? S 0:05 init
2 ? SW 0:00 [keventd]
...
1262 pts/1 S 0:00 /bin/bash
1273 pts/2 S 0:00 su -
1274 pts/2 S 0:00 -bash
1463 pts/1 S 0:00 oclock -transparent -geometry 135x135-10+40
1465 pts/1 S 0:01 emacs Makefile
1514 pts/1 R 0:00 ps

Link URL: http://mylxiaoyi.javaeye.com/blog/403522

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/13670711/viewspace-611809/,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论

注册时间:2008-07-09

  • 博文量
    25
  • 访问量
    11056