Linux进程的创建
【实验目的】
(1)熟悉在c语言源程序中使用linux所提供的系统调用界面的方法。
(2)理解由系统调用创建的子进程的特点。
(3)掌握linux中子进程的创建方法以及调度执行情况,理解进程与程序的区别。
(4)掌握linux提供的c编译器gcc的使用方法。
(5)掌握vi的使用方法。
【实验原理/实验基础知识】
- 由系统调用创建的子进程特点:
① 子进程复制了父进程的数据与堆栈空间,继承父进程的用户代码、组代码、环境变量等等。
② 父子映像有各自的存储空间。
③ 对于父子进程的调度执行具有随机性。
- Linux中相关系统调用
- fork()
- 功能:创建子进程。
- 格式: int fork( )
- 返回值:为0时表示创建成功,从子进程返回;大于0时表示创建成功,从父进程返回,其值为子进程的PID号;等于-1时表示创建失败。
- getpid()
- 功能:获取当前进程ID号。
- 格式: int getpid ( )
- 返回值:当前进程ID号。
- getppid()
- 功能:获取当前进程父进程的ID号。
- 格式: int getppid ( )
- 返回值:当前进程父进程的ID号。
- wait()系统调用
- 功能:等待任意一个子进程终止
- 格式:pid_t wait( )
- 返回值:返回值≥0 表示有子进程终止,其值为终止子进程的pid号;返回值=-1 表示无子进程终止。该进程阻塞,插入等待子进程终止的队列,当有子进程终止时被唤醒。
- 例:wait(0)等待任意子进程结束。
- 注意:等待多个子进程终止需要使用多个wait(),如果该进程没有创建自己的子进程就不能使用wait()。
- 所用头文件:# include < sys / wait.h >,# include < sys / types.h >
- exit()系统调用
- 功能:终止进程,释放其所占有的资源,向父进程发终止信号。
- 格式:void exit(status)
- 参数:status发送给父进程的值,0-255之间。
- 例:exit(0) 进程正常终止。
- 返回值:无返回值
- 头文件: # include < stdlib.h >
- Vi的使用方法
1.vi的基本概念
vi可以分为两种模式,分别是命令模式、插入模式和底行模式,各模式的功能区分如下:
1) 命令行模式
这是进入vi的默认模式,主要功能是控制屏幕光标的移动,字符、字或行的删除,区段复制。
2) 插入模式
主要功能是完成数据、文字的输入,按「ESC」键从插入模式回到命令行模式。
3) 底行模式
主要功能是设置命令,例如保存文件或退出vi等,按「ESC」键从插入模式回到底行模式。
2.vi的基本操作
1)进入vi
在系统提示符号输入vi及文件名称后,就可以进入vi默认命令行模式。
2)切换至插入模式编辑文件
进入vi后,按键盘字母i键就可以进入插入模式输入文字或数据。
3)文本修改
在插入模式下只能一直输入文字,如果需要修改输入的文字,则按键盘ESC键返回命令行模式再使用上下键移动光标,再删除文字。
4)退出vi及保存文件
按键盘ESC键从插入模式回到命令行模式,输入冒号进入底行模式,继续输入命令。保存及退出相关命令:
: w filename (将文章以指定的文件名filename保存)。
: wq (存盘并退出vi) 。
: q! (不存盘强制退出vi) 。
3、命令行模式功能键
1). 删除文字
x:每按一次,删除光标所在位置的"后面"一个字符。
dd:删除光标所在行。
2). 复制
yw: 将光标所在之处到字尾的字符复制到缓冲区中。
#yw:复制#个字到缓冲区,例如单击6yw表示复制6个字。
yy: 复制光标所在行到缓冲区。
p: 将缓冲区内的字符贴到光标所在位置。所有与"y"有关的复制命令都必须与"p"配合才能完成复制与粘贴功能。
3).撤销上一次操作
撤销前一个操作按u,按多次u可以执行多次撤销。
- GCC
1.GCC简介
GCC(GNU Compiler Collection,GNU编译器套装),是一套由 GNU 开发的编程语言编译器,能够编译C、C++等语言编写的程序。
使用GCC由C语言源代码文件生成可执行文件的过程经历四个步骤:预处理(也称预编译,Preprocessing)、编译(Compilation)、汇编(Assembly)和链接(Linking)。
预编译。GCC调用预处理器将预处理指令如#include包含的文件内容插入程序代码中,并做语法检查。
编译。这个阶段,GCC调用编译器将预处理后的文件进行编译,生成一个汇编语言文件。
汇编。GCC调用汇编器as处理汇编文件,并生成一个以.O为扩展名的目标文件。
链接。GCC调用链接器ld将程序中用到的函数库连同目标文件连接,产生一个可执行文件。
- GCC的使用
1)Linux下GCC使用的格式命令
gcc [参数] 源文件 目标文件
参数:
-o file:编译产生的文件以指定文件名保存。若没有指定文件名,则默认文件名为a.out。
-i:在GCCd头文件搜索路径中添加新目录。
-c:仅把源代码编译为目标代码,而不进行函数库链接。完成后产生一个以.O为扩展名的文件。
例:gcc test.c -o t1 表示将test.c进行编译,编译后的可执行文件名为t1。
【实验环境】VMware Workstation、RedHat
【实验步骤】
(1)父进程创建子进程
用vi编写程序,实现父进程创建一个子进程,要求j在子进程中输出当前进程为子进程的提示、当前进程的PID和父进程的PID、根据用户输入确定当前进程的返回值、退出提示信息;k在父进程中分别输出当前进程为父进程的提示、当前进程的PID和子进程的PID、等待子进程退出后获得的返回值、退出提示信息。通过gcc将程序编译链接后执行,观察并分析运行结果。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main() {
pid_t pid = fork();
if (pid < 0) {
// fork 失败
perror("Fork failed");
return 1;
} else if (pid == 0) {
// 子进程
printf("Child process:\n");
printf("PID: %d\n", getpid());
printf("Parent PID: %d\n", getppid());
// 获取用户输入确定当前进程的返回值
int childReturnValue;
printf("Enter return value for child process: ");
scanf("%d", &childReturnValue);
// 退出提示信息
printf("Child process exiting with value: %d\n", childReturnValue);
exit(childReturnValue);
} else {
// 父进程
printf("Parent process:\n");
printf("PID: %d\n", getpid());
printf("Child PID: %d\n", pid);
// 等待子进程退出,并获取其返回值
int status;
waitpid(pid, &status, 0);
if (WIFEXITED(status)) {
// 子进程正常退出
printf("Child process exited with value: %d\n", WEXITSTATUS(status));
} else {
// 子进程异常退出
printf("Child process exited abnormally\n");
}
// 退出提示信息
printf("Parent process exiting\n");
}
return 0;
}
(2)进程家族树
用vi编写程序,在程序中连续使用4个fork(),不使用if进行返回值的判断,在最后一个fork()后输出字符“A”。通过gcc将程序编译链接后执行,观察并分析运行结果。
【实验报告】
填写《信息技术学院学生上机实验报告》。
【思考题】
- 该程序的执行顺序有什么特征?
父进程先创建子进程,然后输出提示信息和相关PID信息,等待子进程退出后输出子进程的返回值。子进程输出提示信息和相关PID信息,根据用户输入确定返回值,然后退出。实现父子进程时程序相互分离,父子进程双方感受不到对方的行为。
- 程序中连续使用4个fork()后,其进程家族树如何?