一、实验目的
学习Linux中与进程控制相关的系统调用,加深对进程、进程树等概念的理解。
二、实验内容
1. 学习使用以下几类系统调用,进行编程练习
获取进程的信息,getpid(), getppid()
父子进程控制,fork(),wait()
2. 学习熟练地使用程序的编辑vi,编译gcc,运行
3. 学习熟练地使用ps命令观察进程的状态及其关系
4. 选做:使用联机手册man,查看上述系统调用的完整指南
三、实验步骤
完成以下3个实验样例程序的编辑、编译、运行和观察。
1 通过联机手册,深入学习相应的系统调用。
2 对3个样例程序进行自主改造和实验,或者构造新的程序进行实验。
(1) 程序getpid.c
#include <stdio.h>
main()
{
int pid;
pid = getpid();
printf("my pid is %d\n", pid);
pid = getppid();
printf("my parent’s pid is %d\n", pid);
sleep(40);
}
1.输入程序 vi getpid.c
2.编译连接 gcc getpid.c -o getpid
3.后台运行 ./getpid &
4. 使用 ps –l 查看进程状态,验证getpid()和getppid()系统调用,注意父子进程的关系。
5. (选做)使用 man 2 getpid命令,查看getpid()系统调用的联机手册。
(2)程序fork.c
#include <stdio.h>
int z = 0;
main()
{
int pid = getpid();
if (pid = fork()) {
z = pid;
printf(“z1 = %d\n”, z);
} else {
z = pid;
printf(“z2 = %d\n”, z);
}
printf(“all done, pid = %d\n”, pid);
}
1.先考虑一下,程序的输出结果是什么。
父进程:fork()返回子进程的PID,因此z被赋值为子进程的PID,然后打印出z1 = [子进程的PID]。
子进程:fork()在子进程中返回0,因此z被赋值为0,然后打印出z2 = 0。
在每个进程中,最后都打印all done, pid = [当前的pid]。
2. 输入程序 vi fork.c
3. 编译连接 gcc fork.c -o fork
4. 运行 ./fork
注意观察程序的输出
5. 验证自己原先的想法是否正确,想一想为什么。
6. (推荐)使用man 2 fork命令,查看fork的联机手册。
(3)程序mfork.c
#include <stdio.h>
main()
{
int i, pid;
for (i=0; i<10; i++) {
pid = fork();
if (pid == 0) {
printf("child %d starts running\n", i);
sleep(60+i*5); // every child sleeps for a different period
exit(0);
} else {
printf("parent: generate a new child with pid %d\n", pid);
sleep(1);
}
}
}
1. 先考虑一下,程序的输出结果是什么。
父进程创建一个子进程,然后返回子进程的PID并进行输出子进程的PID
父进程打印消息"parent: generate a new child with pid [PID]"
然后子进程打印消息"child 1 starts running"
以此类推,循环十轮
来看看GPT的解释
这段代码是一个C语言程序,其主要功能是创建10个子进程,每个子进程都会睡眠一段时间,不同的子进程睡眠时间不同。我们来详细分析一下:
### 包含的头文件
```c
#include <stdio.h>
```
这行代码包含了标准输入输出库,使得程序能够使用`printf`等函数输出信息到控制台。### main函数
程序的入口是`main`函数。这里没有指定返回类型,按照C语言的老标准,如果不指定返回类型,默认为`int`。### 变量定义
```c
int i, pid;
```
定义了两个整型变量`i`和`pid`。`i`用于循环计数,`pid`用于存储`fork`函数的返回值。### 循环结构
```c
for (i=0; i<10; i++) {
// 循环体内容
}
```
这个循环会执行10次,每次循环都尝试创建一个新的进程。### fork函数
```c
pid = fork();
```
`fork()`函数用于创建一个新的子进程,它几乎是父进程的完全复制品。`fork()`会在父进程中返回新创建的子进程的进程ID,在子进程中返回0。如果`fork`失败,它会返回一个负值。### 子进程的行为
```c
if (pid == 0) {
printf("child %d starts running\n", i);
sleep(60+i*5); // every child sleeps for a different period
exit(0);
}
```
在子进程中(`pid`等于0的情况下),程序会输出一个消息表示子进程开始运行,并调用`sleep`函数让子进程睡眠一段时间。睡眠时间是60秒加上循环变量`i`乘以5,这意味着每个子进程都有不同的睡眠时间,从60秒到105秒不等。之后,子进程通过调用`exit(0)`正常退出。### 父进程的行为
```c
else {
printf("parent: generate a new child with pid %d\n", pid);
sleep(1);
}
```
在父进程中,程序会输出一个消息表示已生成一个新的子进程,并显示其进程ID。然后父进程会睡眠1秒,这主要是为了给系统一些时间来调度新创建的子进程。### 结论
这个程序将会创建10个子进程,每个子进程睡眠的时间比前一个多5秒。父进程在每次创建子进程后等待1秒,然后继续循环直到创建了10个子进程。每个子进程在打印消息并睡眠相应的时间后正常退出。父进程在创建完所有子进程后,由于没有代码控制其等待子进程结束或退出,所以父进程的行为将依赖于其后续代码(该代码片段中未提供)以及操作系统的行为。
2. 输入程序 vi mfork.c
3. 编译连接 gcc mfork.c -o mfork
4. 运行 ./mfork
注意观察程序的输出
5. 每隔几秒到十秒钟,反复使用 ps -l 命令观察。想想为什么。
注意./mfork进程的父进程号为1号进程。为什么?
子进程按进程号从小到大的顺序消失,到最后,父进程才消失