进程(2)

程序地址空间

虚拟地址空间:虚拟地址空间是一个结构体 struct mm_struct给操作系统描述了一个完整连续的地址空间,每个进程都拥有一个该结构体,所有的进程都认为自己拥有全部的内存。

为什么要使用虚拟地址空间?
进程在通过虚拟地址从而获取变量数据,最终还是要去访问物理内存你的,因为数据存放在物理内存中.虚拟地址和物理地址通过页表完成映射,虚拟地址连续,物理地址不一定连续,通过映射转换实现数据的离散存储,内存使用率更高,而且有内存访问控制。

页表
由操作系统维护,将物理地址映射到虚拟地址的表叫页表。
MMU:内存管理单元,结合页表快速将物理地址转为虚拟地址的硬件。

页表不但记录了虚拟地址和物理地址的映射关系,还记录了这块地址的属性(rw属性)

pcb里的指针指向mm_struct,mm_struct通过页表映射到物理内存中的位置
在这里插入图片描述
多级页表
在这里插入图片描述

进程创建

fork()函数

它从已存在进程中创建一个新进程。新进程为子进程,而原进程为父进程,
进程调用fork(),当控制转移到内核中的fork()代码后,内核将:

  1. 分配新的内存块和内核数据给子进程

  2. 将父进程部分数据结构拷贝一份分配给子进程

  3. 添加子进程到系统进程列表中

  4. fork()返回,开始调度
    在这里插入图片描述
    在这里插入图片描述
    所以,fork之前父进程独立执行,fork之后,父子两个执行流分别执行。注意,fork之后,谁先执行完全由调度器决定。
    fork函数返回值

  5. 子进程返回0

  6. 父进程返回子进程的id
    fork()之后,父子进程代码共享,数据各自私有一份。

通常,父子代码共享,父子再不写入时,数据也是共享的,当任意一方试图写入,便以写时拷贝的方式各自一份副本
在这里插入图片描述

进程终止

进程退出一般有三种结果:

  1. 代码跑完,结果正确
  2. 代码跑完,结果不正确
  3. 代码没跑完,程序异常退出,在运行期间被提前终止
exit函数和_exit函数

void _exit(int status);
void exit(int status);
exit函数和_exit函数都是用来终止进程的函数,

int main()
{
	printf("hellow");
	sleep(1);
	//exit (150);
	_exit(150);
}

上面这段代码中我们可以看到的现象是,exit函数打印结果为“hellow ",而_exit则直接终止了进程,并没有打印hellow,这里我们可以得出他们的区别是:_exit调用时会将进程直接终止,而exit最后也会调用exit, 但在调用exit之前,还做了其他工作:
1. 执行用户通过 atexit或on_exit定义的清理函数。
2. 关闭所有打开的流,所有的缓存数据均被写入
3. 调用_exit

在这里插入图片描述

进程等待

进程退出后,所占的资源会立刻释放掉吗?
答案是不会。

进程等待必要性

子进程退出后,父进程如果不管不顾,就会造成僵尸进程的问题,进而造成内存泄漏,
此时,父进程就需要通过进程等待的方式来读取子进程的状态,我们需要知道,子进程是否完成,结果对还是不对,
父进程就通过进程等待的方式回收子进程资源,获取子进程退出信息

wait函数
pid_t wait(int*status);
返回值:
成功返回被等待进程pid,失败返回-1。
参数:
输出型参数,获子进程退出状态,不关心可以设成NULL。

接下来我们来谈一谈status,wait和waitpid,都有一个status参数,该参数是一个输出型参数,由操作系统填充,有32位,它的结构是这样的:
在这里插入图片描述
低八位用来存放终止信号,用来判断进程退出是否出现异常,若为0则是正常退出,若不为0则是异常退出;次低八位则存放的是进程的退出状态,也就是退出码,

waitpid函数
*pid_ t waitpid(pid_t pid, int status, int options)
pid:这里的pid指的是父进程所等待的id,所等待的id不同意义也不同
1. pid>0,只等待进程id等于子进程ID的进程,不管有其他多少子进程退出了,只要指定等待的子进程没有退出,就一直等下去
2. pid=-1,等待任何一个子进程退出,没有限制,这里和wait方法是一样的没有区别
3. pid=0,等待同一个进程组的子进程,如果子进程已经进入了其他的进程组,waitpid则不予理睬
4. pid<-1时,等待一个指定进程组中的任何子进程,这个进程组的ID等于pid的绝对值。
options:
options提供了一些额外的选项来控制waitpid,
目前在Linux中只支持WNOHANG和WUNTRACED两个选项,这是两个常数,可以用"|"运算符把它们连接起来使用.
WNOHANG:若pid指定的子进程没有结束,则waitpid()函数返回0,不予等待;若正常结束则返回该子进程的id。

  • 如果子进程已经退出,调用wait/waitpid时,wait/waitpid会立即返回,并且释放资源,获得子进程退出信息。
  • 如果在任意时刻调用wait/waitpid,子进程存在且正常运行,则进程可能阻塞。
  • 如果不存在该子进程,则立即出错返回

waitpid的返回值比wait稍微复杂一些,一共有3种情况:

当正常返回的时候,waitpid返回收集到的子进程的进程ID;
如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0;
如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在;

进程等待方式

阻塞方式

阻塞方式指的是,当某一个条件不满足,操作系统就将该进程状态设置为非R状态,并将该进程放至等待队列,当操作系统检测到该条件发生,则从等待队列将该进程唤醒,也就是将该进程pcb状态设为R状态,并把它的PCB从等待队列移除,放至运行队列,调度器会在合适的时候调度它。

非阻塞方式
非阻塞方式则是,当某一个条件不满足是,父进程立刻返回,隔一段时间来检测条件是否满足,

发布了34 篇原创文章 · 获赞 4 · 访问量 1743

猜你喜欢

转载自blog.csdn.net/qq_41181857/article/details/104677947