进程章节总结性实验

进程实验课笔记
本节需要有linux基础,懂基本的linux命令操作即可。

Ubuntu镜像下载
https://note.youdao.com/s/VxvU3eVC
ubuntu安装
https://www.bilibili.com/video/BV1j44y1S7c2/?spm_id_from=333.999.0.0
实验环境ubuntu22版本,那个linux环境都可以,mit这套课程的实验环境是xv6,这种类unix远古的环境都可以,那么正常环境都是没问题的,当然windows环境应该也是可以的,这里主要是Linux环境。
gcc基础
这里用一个helloworld演示

注意linux所有文件的后缀都是无意义的,我们这里标识.c只是单纯标识
当然先写代码

一步到位:gcc hello.c
这条命令隐含执行了
(1)预处理
(2)编译
(3)汇编
(4)链接
在这里插入图片描述
这里未指定输出文件,默认输出为a.out
gcc编译C源码有四个步骤:
预处理 ----> 编译 ----> 汇编 ----> 链接
现在我们就用gcc的命令选项来逐个剖析gcc过程。
在该阶段,编译器将C源代码中的包含的头文件如stdio.h添加进来
参数:”-E”
gcc –E hello.c –o hello.i
第一步预处理的过程,我们可以看下系统给我们做了什么
在这里插入图片描述
只加了注释,这里看不到汇编代码,那就算了
继续下一步
编译
gcc –S hello.i –o hello.s
这里报错了
在这里插入图片描述看起来不需要头文件
注释掉
在这里插入图片描述重新预处理和编译,注意这里要重新来
在这里插入图片描述

可以了
我们看下文件
转化成汇编了,不出所料
在这里插入图片描述在这里插入图片描述
不多深究了继续
链接 其实就是解析汇编代码了
gcc –c hello.s –o hello.o
在这里插入图片描述
链接其实就是做成exe文件
gcc hello.o -o hello

在这里插入图片描述
这步可以理解成加壳

注意要执行给权限,随便怎么给,我给777

在这里插入图片描述
lab1:获取进程号

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{ // getpid()函数返回当前进程的ID号
printf("Before fork Process id:%d\n", getpid());
printf("After fork Process id:%d\n", getpid());
pause();
return 0;
}

在这里插入图片描述我们运行一下
在这里插入图片描述

我们打开另外一个terminal
在这里插入图片描述

实验二:子进程与父进程的pid

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
//pid_t是数据类型,实际上是一个整型
//通过typedef重新定义了一个名字,用于存储进行ID
pid_t pid;
//parent pid
pid_t cid;
//child pid
// getpid()函数返回当前进程的ID号
printf("Before fork Process id:%d\n", getpid());

fork();
printf("After fork Process id:%d\n", getpid());
pause();
return 0;
}

在这里插入图片描述为什么执行了三遍呢?
这里我们可以看出父进程执行了两次,而子进程执行了一次
我们再看下进程状况

在这里插入图片描述
至于为什么父进程执行了两边呢?

这就和我们系统创建子进程的过程相关了,创建子进程的第一步就是将父进程的内存数据全部clone一份,然后放入一份新的属于子进程的内存区域中。这样也就代表他们的内存中的text区域数据是一样的,这样他们就会执行相同的代码。当然这里是并发执行,那么父子进程都要执行一遍fork函数之后的代码,注意这里字进程是在fork函数执行时被唤醒的,所以子进程虽然有父进程的代码,但是子进程也只会并发执行fork函数后面的代码(也就是从fork函数开始执行)。

这里我们需要注意,这里并发执行虽然是父进程唤醒子进程,但是由于并发的机制让我们用户基本感受不到谁先执行,所以理论上来说我们可以让父进程执行的时间更长一些(比如调用write输出i/o设备产生的结果时间更长),这样就可以做到父进程还没有输出,但是子进程却已经输出结果了,让我们产生子进程优先执行的错觉。

实验三:fork函数返回值
先看下fork函数的返回值

#include <unistd.h>
int main(int argc, char const *argv[])
{
//pid_t是数据类型,实际上是一个整型
//通过typedef重新定义了一个名字,用于存储进行ID
pid_t pid;
//parent pid
//为了防止误判先给cid一个值
pid_t cid=8;
//child pid
// getpid()函数返回当前进程的ID号
printf("Before fork Process id:%d\n", getpid());

cid = fork();
printf("cid=%d\n", cid);
pause();
return 0;
}

那么这个0是什么呢?

如果成功创建子进程,对于父子进程fork会返回不同的值,对于父进程它的返回值是子进程的pid号,而对于子进程他返回值是0,这里猜测这个0大概率是内核模式。如果创建失败,cid返回值为-1

实验四:并发与父子内存数据复制
并发

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
int main(int argc, char const *argv[])
{
pid_t cid;
//child pid
printf("Before fork Process id:%d\n", getpid());
cid = fork();
if (cid == 0){
// 该分支是子进程执行的代码
    printf("Child process id (my parent pid is %d):%d\n", getppid(),getpid());
        for(int i = 0; i < 3; i++){
        printf("hello\n");
        }
}else{
// 该分支是父进程执行的代码
    printf("Parent Process id: %d\n",getpid());
    for(int i = 0; i < 3; i++){
    printf("world\n");
    }
wait(NULL);
}
return 0;
}

这里可以看出结果是父进程还是比子进程先执行,但是这个并看不出并发
在这里插入图片描述

我们将for循环增加次数直接增加到3000次

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
int main(int argc, char const *argv[])
{
pid_t cid;
//child pid
printf("Before fork Process id:%d\n", getpid());
cid = fork();
if (cid == 0){
// 该分支是子进程执行的代码,for循环增加次数
    printf("Child process id (my parent pid is %d):%d\n", getppid(),getpid());
        for(int i = 0; i < 3000; i++){
        printf("hello\n");
        }
}else{
// 该分支是父进程执行的代码,for循环增加次数
    printf("Parent Process id: %d\n",getpid());
    for(int i = 0; i < 3000; i++){
    printf("world\n");
    }
wait(NULL);
}
return 0;
}

这一段输出我们可以看出父进程与子进程是你执行一下我执行一下,前面那个是因为进程执行输出数据太少看不出并发执行。
在这里插入图片描述

父子内存数据复制
我们前面说子进程是将父进程的内存空间数据copy了一份,那么我们现在设置一个值看下子进程读取是否会有问题

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
int main(int argc, char const *argv[])
{

pid_t cid;
//child pid
printf("Before fork Process id:%d\n", getpid());
int value = 100;
cid = fork();
if (cid == 0){
printf("Child process id (my parent pid is %d):%d\n", getppid(),getpid())
for(int i = 0; i < 3; i++){
printf("hello(%d)\n",value--);
}
}else{
printf("Parent Process id: %d\n",getpid());
for(int i = 0; i < 3; i++){
printf("world(%d)\n",value++);
}
wait(NULL);
}
return 0;
}

这里我们可以看出子进程和父进程的value值确实是一样的,
在这里插入图片描述

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
int main(int argc, char const *argv[])
{
pid_t cid;
//child pid
printf("Before fork Process id:%d\n", getpid());
cid = fork();
if (cid == 0){
printf("Child process id (my parent pid is %d):%d\n", getppid(),getpid());
for(int i = 0; i < 3; i++){
printf("hello\n");
}
}else{
printf("Parent Process id: %d\n",getpid());
for(int i = 0; i < 1; i++){
printf("world\n");
}
//wait(NULL);
}
return 0;
}

在这里插入图片描述
我们ps -ef看下
在这里插入图片描述

我们可以看出这子进程的父进程是systemd这个进程

其实出现这个现象的原因是因为我们没有在父进程中加wait函数,那么父进程由于执行的太快,快到父进程唤醒子进程的过程还没开始,父进程就已经被执行完了,那么父进程已经进入终止状态,cpu使用权回到了systemd这个进程上,那么只能是systemd这个进程去调用子进程,所以现在子进程的父进程号就变成了systemd的进程号了。

解决上面这个问题,我们就必须要要让父进程等待子进程,直到子进程执行完再把cpu使用权返回给systemd。

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
int main(int argc, char const *argv[])
{
pid_t cid;
//child pid
printf("Before fork Process id:%d\n", getpid());
cid = fork();
if (cid == 0){
printf("Child process id (my parent pid is %d):%d\n", getppid(),getpid());
for(int i = 0; i < 3; i++){
printf("hello\n");
}
}else{
printf("Parent Process id: %d\n",getpid());
for(int i = 0; i < 1; i++){
printf("world(%d)\n",getppid());
}
wait(NULL); //等待子进程结束,再返回,()里面参数一般是空指针
}
return 0;
}
        
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

白帽小丑

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值