fork函数、进程退出、进程等待(2)

简介:  return是一种更常见的退出进程方法。执行return n等同于执行exit(n),因为调用main的运行时函数会将main的返回值当做 exit的参数。

return退出


return是一种更常见的退出进程方法。执行return n等同于执行exit(n),因为调用main的运行时函数会将main的返

回值当做 exit的参数。


进程退出返回值的意义:


return以及exit给与的数据其实就是进程的退出码

 作用: 一个程序运行起来肯定是为了完成一个任务,但是这个任务完成的怎么样外界怎么知道呢?因此就必须有这个进程的退出码,来表示当前进程任务处理的结果。不管是return还是exit,都需要我们给出一个进程的返回值exit(0), return 0;给出的返回值,是进程的返回值因为一个进程就是为了调度运行一个程序, 完成一个任务的(但是任务完成有好有坏),就必须得有一种方式能够告诉我们这个任务完成的怎么样? (返回值的作用)


内存管理方式


分段式内存管理:将一个整体的地址空间划分为多个段(代码段,全局数据段,堆区,共享区,栈区,环境变量,运行参数…)


优势/作用:非更加利于编译器对于地址的管理。

重要的两个要素:段表,地址组成 虚拟地址组成:段号,段内的偏移量

段表:是一种数据结构,其中描述的信息,段号:物理内存的一个起始地址 映射:虚拟地址组成+段表

通过段号找到段表项,得到一块物理内存的起始地址

物理内存起始地址+偏移量就是实际数据存储在物理内存中的位置

image.png分页式内存管理:将一个整体的地址空间划分 为大量的小的分页page (当前一般默认都是4096字节为一页)

作用:实现数据的离散存储,提高内存利用率

段页式内存管理:先将地址空间进行分段,然后在每个分段内使用分页进行管理(集合了分段式和分页式的优势) 映射:虚拟地址组成(页号+页内偏移)

页表(页号+物理块地址+访问权限+缺页中断…)

段页式:先对虚拟地址空间进行分段,在每个段内进行分页管理,集合分段与分页各自的优势进行内存管理

好处:

  1. .每个进程都有一个完整独立的虚拟地址空间,则地址可以随便使用,不用担心冲突(地址管理更加方便)
  2. 进过页表映射可以将数据存储在物理内存的任意位置,实现数据的离散式存储,提高内存利用率
  3. 在进行页表映射的之后可以进行访问权限的控制


进程等待


进程等待必要性


wait方法


#include<sys/types.h>

#include<sys/wait.h>

pid_t wait(int*status);

返回值: 成功返回被等待进程pid,失败返回-1。

参数: 输出型参数,获取子进程退出状态,不关心则可以设置成为NULL

(子进程没有退出会一直等待,子进程退出会直接退出)

waitpid方法

pid_ t waitpid(pid_t pid, int status, int options);

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

Pid=-1,等待任一个子进程。与wait等效。

Pid>0.等待其进程ID与pid相等的子进程。

status:

WIFEXITED(status): 若为正常终止子进程返回的状态,则为真。(查看进程是否是正常退出)

WEXITSTATUS(status): 若WIFEXITED非零,提取子进程退出码。(查看进程的退出码)


options:

WNOHANG: 若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。若正常结束,则返回该子进程的ID。0是默认阻塞


 故:waipid(-1,&status,0)与wait等价,都是没获取到就堵塞,获取任意一个子进程的返回值

注意:

 如果子进程已经退出,调用wait/waitpid时,wait/waitpid会立即返回,并且释放资源,获得子进程退出信息。

 如果在任意时刻调用wait/waitpid,子进程存在且正常运行,则进程可能阻塞。

 如果不存在该子进程,则立即出错返回。

image.png

获取子进程status


wait和waitpid,都有一个status参数,该参数是一个输出型参数,由操作系统填充。如果传递NULL,表示不关心子进程的退出状态信息。否则,操作系统会根据该参数,将子进程的退出信息反馈给父进程。status不能简单的当作整形来看待,可以当作位图来看待,具体细节如下图(只研究status低16比特位,高16位暂时不关心):

低16位中的高8位保存进程退出码,之后有一个coredump标志位占据一个比特位,coredump文件很大,默认情况下是关闭的。

image.png

注意:通过wait获取的返回值,有多个信息,其中进程的退出码保存到高8位中,并且只用一个字节来保存,即(0~255),多了采取截断、


image.png

因此要获取一一个进程的退出码,首先得确定这个进程是否是正常退出的,如果是,才有意义。


代码中关心的问题:

如何判断进程是否是正常退出:取出status中的低7位; status & 0x7f(就是0111 1111)== 0正常退出,否则异常退出

如何从status中取出退出码:取出status中的低1 6位中的高8位 (status >> 8) & 0xff(1111 1111) 向右移动8位


问:exit()和status是怎么链接上的?

image.png

阻塞等待代码演示


int main()
{
 pid_t pid;
 pid = fork();
 if(pid < 0){
   printf("%s fork error\n",__FUNCTION__);
   return 1;
 } else if( pid == 0 ){ //child
   printf("child is run, pid is : %d\n",getpid());
   sleep(5);
   exit(257);
 } else{
   int status = 0;
   pid_t ret = waitpid(-1, &status, 0);//阻塞式等待,等待5S
   printf("this is test for wait\n");
   if( WIFEXITED(status) && ret == pid ){
 printf("wait child 5s success, child return code is 
:%d.\n",WEXITSTATUS(status));
 }else{
 printf("wait child failed, return.\n");
 return 1;
 }
 }
 return 0;
}
运行结果:
[root@localhost linux]# ./a.out
child is run, pid is : 45110
this is test for wait
wait child 5s success, child return code is :1.

进程的非阻塞等待方式:

#include <stdio.h> 
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
int main()
{
   pid_t pid;
   pid = fork();
   if(pid < 0){
       printf("%s fork error\n",__FUNCTION__);
       return 1;
   }else if( pid == 0 ){ //child
       printf("child is run, pid is : %d\n",getpid());
       sleep(5);
       exit(1);
   } else{
       int status = 0;
       pid_t ret = 0;
       do
       {
           ret = waitpid(-1, &status, WNOHANG);//非阻塞式等待
           if( ret == 0 ){
               printf("child is running\n");
           }
           sleep(1);
       }while(ret == 0);
       if( WIFEXITED(status) && ret == pid ){
           printf("wait child 5s success, child return code is 
:%d.\n",WEXITSTATUS(status));
       }else{
           printf("wait child failed, return.\n");
           return 1;
       }
   }
   return 0;
}


目录
相关文章
|
2月前
|
Linux C语言
C语言 多进程编程(三)信号处理方式和自定义处理函数
本文详细介绍了Linux系统中进程间通信的关键机制——信号。首先解释了信号作为一种异步通知机制的特点及其主要来源,接着列举了常见的信号类型及其定义。文章进一步探讨了信号的处理流程和Linux中处理信号的方式,包括忽略信号、捕捉信号以及执行默认操作。此外,通过具体示例演示了如何创建子进程并通过信号进行控制。最后,讲解了如何通过`signal`函数自定义信号处理函数,并提供了完整的示例代码,展示了父子进程之间通过信号进行通信的过程。
|
2月前
|
编译器
【收藏】内核级利用通用Hook函数方法检测进程
【收藏】内核级利用通用Hook函数方法检测进程
|
3月前
|
Linux API
Linux源码阅读笔记07-进程管理4大常用API函数
Linux源码阅读笔记07-进程管理4大常用API函数
|
4月前
|
小程序 Linux
【编程小实验】利用Linux fork()与文件I/O:父进程与子进程协同实现高效cp命令(前半文件与后半文件并行复制)
这个小程序是在文件IO的基础上去结合父子进程的一个使用,利用父子进程相互独立的特点实现对数据不同的操作
100 2
|
4月前
|
NoSQL Linux Redis
c++开发redis module问题之避免在fork后子进程中发生死锁,如何解决
c++开发redis module问题之避免在fork后子进程中发生死锁,如何解决
|
5月前
|
Linux 调度
Linux进程控制——Linux进程等待
Linux进程控制——Linux进程等待
64 2
|
5月前
|
算法 Linux 调度
Linux进程——进程的创建(fork的原理)
Linux进程——进程的创建(fork的原理)
183 2
|
4月前
|
缓存 Linux 调度
【linux】进程控制——进程创建,进程退出,进程等待
【linux】进程控制——进程创建,进程退出,进程等待
|
5月前
|
监控 Linux 应用服务中间件
探索Linux中的`ps`命令:进程监控与分析的利器
探索Linux中的`ps`命令:进程监控与分析的利器
119 13
|
4月前
|
运维 关系型数据库 MySQL
掌握taskset:优化你的Linux进程,提升系统性能
在多核处理器成为现代计算标准的今天,运维人员和性能调优人员面临着如何有效利用这些处理能力的挑战。优化进程运行的位置不仅可以提高性能,还能更好地管理和分配系统资源。 其中,taskset命令是一个强大的工具,它允许管理员将进程绑定到特定的CPU核心,减少上下文切换的开销,从而提升整体效率。
掌握taskset:优化你的Linux进程,提升系统性能