在传统的操作系统中,程序并不能独立运行,作为资源分配和独立运行的基本单位都是进程。操作系统所具有的四大特征也都是基于进程而形成的,并可从进程的观点来研究操作系统。显然,在操作系统中,进程是一个极其重要的概念。下面我们通过一个经典案例来谈一下进程。
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#include <ctype.h>
/* 允许建立的子进程个数最大值 */
#define MAX_CHILD_NUMBER 10
/* 子进程睡眠时间 */
#define SLEEP_INTERVAL 2
/* 子进程的自编号,从0开始 */
int proc_number=0;
void do_something();
main(int argc, char* argv[])
{
/* 子进程个数 */
int child_proc_number = MAX_CHILD_NUMBER;
int i, ch;
pid_t child_pid;
pid_t pid[10]={0}; /* 存放每个子进程的id */
if (argc > 1) /* 命令行参数第一个参数表示子进程个数*/
{
child_proc_number = atoi(argv[1]);
child_proc_number= (child_proc_number > 10) ? 10 : child_proc_number;
}
for (i=0; i<child_proc_number; i++)
{
/* 填写代码,建立child_proc_number个子进程要执行
* proc_number = i;
* do_something();
* 父进程把子进程的id保存到pid[i]
*/
child_pid = fork();
if (child_pid == -1) //如果出现错误,fork返回一个负值
{
perror("create error!\n");
return 1;
}
else if (child_pid == 0)
{
proc_number=i; //在子进程中,fork返回0
do_something();
}
else
{
pid[i] = child_pid; //在父进程中,fork返回新创建子进程的进程ID
}
}
/* 让用户选择杀死进程,数字表示杀死该进程,q退出 */
while ((ch = getchar()) != 'q')
{
if (isdigit(ch))
{
/* 向pid[ch-'0']发信号SIGTERM,
* 杀死该子进程
*/
kill(pid[ch-'0'], SIGTERM);
}
}
/* 杀死本组的所有进程 */
kill(0, SIGKILL);
return;
}
void do_something()
{
int k;
for(;;)
{
printf("This is process No.%d and its pid is %d, address锛?p\n", proc_number, getpid(), &proc_number);
sleep(SLEEP_INTERVAL); /* 主动阻塞两秒钟 */
}
}
1、你最初认为运行结果会怎么样?
会创建10个进程,进程号为0~9。若输入数字、回车会杀死指定进程。若输入q、回车,会杀死所有进程。
2、实际的结果什么样?有什么特点?试对产生该现象的原因进行分析。
运行程序,命令行第二个参数是20,但程序中的限制还是会创建10个进程,每隔SLEEP_INTERVAL秒刷新一次输出在命令行界面。直到输入数字、回车,将会杀死指定数字的进程,或直到输入q、回车,会杀死本组所有进程。
特点:每次输出一定数目的进程,但每次输出的进程的次序基本都不一样,循环随机输出。进程的pid也是随着进程号的序号而按序增长的,进程的地址都相同。分析:每创建一个子进程时,将其pid存储在pid[i]中,i存储在proc_number,然后调用死循环函数do_something(),输出该进程的编号proc_number,pid和地址。创建进程是利用循环创建的,其进程号也是按照循环中的i值由小到大的,依照创建的顺序pid也依次递增。进程地址相同是由于输出的是&proc_number,其指针指向的是同一存储单元,打印出的是逻辑地址,但是实际上它们的物理地址是不同的。
3、proc_number这个全局变量在各个子进程里的值相同吗?为什么?
相同,因为子进程相互独立,资源互不影响。
4、kill命令在程序中使用了几次?每次的作用是什么?执行后的现象是什么?
2次。kill(pid[ch=’0’], SIGTERM);kill(0,SIGTERM);第一个是杀死进程号pid[ch-‘0’],执行后输出的结果中不会再有该进程号。第二次是杀死本组所有进程。即主进程以及它所创建的所有子进程,执行后程序退出结束。
5、使用kill 命令可以在进程的外部杀死进程。进程怎样能主动退出?这两种退出方式哪种更好一些?
进程在main()中return,或调用exit()都可以主动退出。使用kill()则是强制性的异常退出。 主动退出更好,若在子进程退出前使用kill()杀死其父进程,则系统会让init进程接管子进程,该子进程就会成为孤儿进程。当使用kill()杀死子进程使得子进程先于父进程退出时,父进程又没有调用wait()函数等待子进程结束,子进程处于僵死状态,即僵尸进程。且一直会保持下去,直到系统重启。子进程处于僵死状态时内核只保存该进程的必要信息以备父进程所需,此时子进程始终占着资源,这也就减少了系统可以创建的最大进程数。
6、将do_something()中的死循环改为有穷资源后创建多少个进程?
创建很多很多个进程,但有一定的数目限制。是由于父进程刚开始创建的子进程中的第一个进程又会创建它自己的子进程,这样的规律下来就会创建很多很多个进程,但是进程数又是一定的。
7、输出proc_number的地址,观察并解释原因。
输出的所有的地址相同。是因为在C语言中“&proc_number”是一个指针,指针指向同一存储单元,打印出的是逻辑地址,是相同的,但物理地址不同。