一、实验目的
- 掌握Linux 操作系统的操作和使用;
- 掌握Linux 下C 语言的编辑、编译、运行的全过程;
- 掌握进程创建系统调用的使用。
二、实验内容
- 熟悉Linux 运行环境。
- 学习UNIX/LINUX 系统的pwd, ls, cd, ps, cp, kill 等命令,查看运行结果。
- 掌握C 语言的编辑、编译、运行的全过程,掌握进程创建系统调用的使用。
a) 使用vi(全屏幕编辑器)或者文本编辑器编写C 语言文件。
b) 使用gcc(C 语言编译器)编译C 语言文件。
c) 运行编译生成的文件。 - 设计一个程序,在掌握进程创建fork()系统调用的使用,在进程中创建若干子进程。在
主进程、子进程中制定它们的运行先后关系,分析各个进程中运行情况。
三、实验步骤(附程序原码)
- C 语言编辑
(1)使用附件中的文本编辑器编辑程序
(2)编辑最简单的hello world 程序
(3)保存为hello.c 源文件。 - C 语言的编译和运行
(1)进入终端
5
(1)使用pwd 查看当前路径
pwd
(2)使用ls 查看文件hello.c。
ls -l hello.c
(3)使用gcc 对源文件进行编译。
gcc hello.c
(4)使用ls 查看生成的可执行文件a.out
ls -l a.out
(5)执行a.out,查看执行结果。
./a.out
注意:
(1)编译时没有使用-o 选项指定可执行文件的文件名,确实生成的可执行文件是a.out
(2)可以使用-o 选项指定可执行文件的文件名
gcc hello.c -o hello.out
指定的可执行文件的文件名是hello.out
(3)执行文件时必须显示的指明是当前文件夹下的可执行文件,也就是在a.out 前必须加
上./。这里./表示是当前文件夹下的。 - 进程创建1
/*fork1.c*/
#include <stdio.h>
int main()
{
int p1, p2, i;
while ((p1 = fork()) == -1)
; /*创建进程p1*/
if(p1 == 0)
{
for (i = 0; i < 8; i++)
{
printf("daughter %d \n", i);
}
}
else
{
while ((p2 = fork()) == -1); /*创建进程p2*/
if (p2 == 0)
{
for (i = 0; i < 8; i++)
{
printf("son %d \n", i);
}
}
else
{
for (i = 0; i < 8; i++)
{
printf("parent %d \n", i);
}
}
}
}
运行结果
执行结果中打印出来的语句没有交叉,好像三个进程是顺序执行,而不是并发执行。
5. 进程创建2,增加sleep 语句使得打印结果有交叉。
/*fork2.c*/
#include <stdio.h>
int main()
{
int p1, p2, i;
while ((p1 = fork()) == -1); /*创建进程p1*/
if(p1 == 0)
{
for (i = 0; i < 8; i++)
{
printf("daughter %d \n", i);
sleep(4);
}
}
else
{
while ((p2 = fork()) == -1); /*创建进程p2*/
if (p2 == 0)
{
for (i = 0; i < 8; i++)
{
printf("son %d \n", i);
sleep(4);
}
}
else
{
for (i = 0; i < 8; i++)
{
printf("parent %d \n", i);
sleep(4);
}
}
}
return 0;
}
程序运行结果
6. 增加代码,判断以下程序的执行产生多少个进程
#include <stdio.h>
int main()
{
fork();
fork();
fork();
return 0;
}
试着使用两个系统调用
(1)获得当前的进程号getpid();
(2)获得父进程的进程号getppid();
(3)等待子进程结束wait();
(4)在创建多个进程后,执行的过程中可能会产生孤儿进程,即父进程先于子进程之前结
束。在这种情况下,系统将子进程的父进程设定为init 进程(进程号为1)。
(5)要解决孤儿进程问题,可以在父进程中增加wait 系统调用。
参考程序:
/*count2.c*/
#include <stdlib.h>
#include <stdio.h>
int main()
{
fork();
fork();
fork();
printf("Hello, world! from process id %d, it's parent id %d\n", getpid(), getppid());
wait(0);
wait(0);
wait(0);
return 0;
}
执行结果如下图所示。
可以画出这8 个进程的进程关系树。
10
root@duanwh-virtual-machine:~# ./count2.out
Hello, world! from process id 17361, it's parent id 16421
Hello, world! from process id 17362, it's parent id 17361
Hello, world! from process id 17363, it's parent id 17361
Hello, world! from process id 17364, it's parent id 17361
root@duanwh-virtual-machine:~# Hello, world! from process id 17365, it's parent id
17362
Hello, world! from process id 17366, it's parent id 17362
Hello, world! from process id 17367, it's parent id 17363
Hello, world! from process id 17368, it's parent id 17365
进程号父进程号
17361 16421
17362 17361
17363 17361
17364 17361
17365 17362
17366 17362
17367 17363
17368 17365
16421 是bash 的进程号,16421 创建了第一个进程17361;17361 创建了17362,17363,
17364 三个进程;17362 进程创建了17365 和17366 两个进程;17363 创建了17367 一个
进程;17365 创建了17368 一个进程。
- 在父进程中使用fork 创建一个子进程,子进程拷贝了父进程的代码段和数据段。系统还
提供一个vfork 也是创建一个子进程,子进程共享父进程的代码段和数据段。fork 后父
子进程的执行次序不确定。vfork 保证子进程先运行,在调用exec 或exit 之前与父进程
数据是共享的,在它调用exec 或exit 之后父进程才可能被调度运行。 - 实验总结
a) 常用的命令行
b) 熟悉Linux 下的C 语言编辑、编译和运行。
c) 特殊进程
i. 父进程
ii. 子进程
11
iii. init 进程(1 号进程)
iv. 守护进程(幽灵进程,daemon)
v. 孤儿进程
vi. 僵尸进程(zombie) - 以下的程序执行会产生孤儿进程。
#include <stdio.h>
int main()
{
int pid;
pid = fork();
if (pid == 0)
{
printf("child pid = %d, ppid = %d\n", getpid(), getppid());
sleep(2);
printf("child pid = %d, ppid = %d\n", getpid(), getppid());
}
else
{
printf("parent pid = %d, ppid = %d\n", getpid(), getppid());
}
return 0;
}
执行结果
执行结果分析:
(1)执行了两次,第一次是parent 先打印,第二次是child 进程先打印。两次的执行结果
不同,这个就体现了操作系统基本特性中的异步性。
(2)分析第一次执行结果。(父进程的进程号是23331,子进程的进程号是23332)
1)父进程先打印,打印结束后,父进程结束。
2)子进程打印第一次的时候已经是孤儿进程了。子进程的两次打印结果是一样的。
(3)分析第二次执行结果。(父进程的进程号是23334,子进程的进程号是23335)
1)子进程先打印第一次,此时父进程没有结束,子进程不是孤儿进程,打印出了子进
12
程的进程号和父进程的进程号。子进程执行sleep 语句进入阻塞状态。
2)父进程执行,打印自身的进程号和自己的父进程的进程号。父进程结束。
3)子进程等待时间到了后继续执行,打印第二句话,此时父进程已经结束,子进程变
成了孤儿进程。
10. 以下的程序执行会出现僵尸进程。
/*daemon.c*/
#include <stdio.h>
int main()
{
int pid;
pid = fork();
if (pid > 0)
{
while(1)
{
//nothing
}
}
else
{
printf("child pid = %d, ppid = %d\n", getpid(), getppid());
}
return 0;
}
另打开一个终端,使用ps -al|grep daemo 可以看到子进程是僵尸状态。
执行结果分析:
(1)涉及到两个进程,父进程的进程号23473,子进程的进程号23474。