当前位置: 首页 > java >正文

9.进程控制(上)

一、进程终止

代码运行完毕,结果正确。

代码运行完毕,结果不正确。

代码异常终止。(退出码无意义)

子进程也是进程->父进程创建->子进程执行的结果

进程一旦出现异常,一般是进程收到了信号。

return 本质就是将返回值放到寄存器。

即使不写return 0,返回值也是0的原因:无非就是少了将 ret 放到寄存器这一句指令,寄存器默认存的值为0,拿到的程序退出码(语言层面)还是0。

int main(); main函数返回值,通常表明程序的执行情况。

echo $? 打印最近一个程序退出时的退出码->进程退出码->写到进程的task_struct内部的

结果正确,退出码为0。

结果不正确,退出码非0,{1,2,3,...},为了通过错误码得知错误信息。

strerror,<string.h>,根据错误码返回对应的字符串。

[0,133],一共有134种错误码。

errno:整形变量,<errno.h>,记录上一个错误码。

测试代码:

#include <stdio.h>    
#include <string.h>    
#include <errno.h>    int main()    
{    FILE* pf = fopen("test.txt","r");    if(pf==NULL)    {                                                                                                                                                printf("strerror:%s\n",strerror(errno));    return errno;                               }                                               fclose(pf);                                     return 0;                                       
}   

进程退出的方式(重点) 

main函数结束,表示进程结束。

其他函数结束,只表示自己函数调用完成,返回。

main:(return)

exit:(status)

任何地方调用exit,表示进程结束!并返回给父进程,子进程的退出码。

exit(C)vs _exit(系统):

_exit()和exit()是上下层的关系,exit()封装了系统调用_exit()。

我们之前讨论的缓冲区。应该在哪里?

一定不是操作系统内部的缓冲区,否则_exit退出会导致内存泄漏。

库缓冲区,C语言提供的缓冲区。

二、进程等待(重点) 

进程等待的必要性 

为什么要等待?

1)防止造成“僵尸进程”的问题,进而导致内存泄漏。

2)父进程派给子进程的任务完成的情况(结果对,结果不对,异常退出),父进程要知道。

总结:父进程通过进程等待的方式,回收子进程资源(重要)获取子进程退出信息 。

进程等待的方式

wait,<sys/types.h>,<sys/wait.h>

pid_t wait(int *status); status为输出型参数,输出等待子进程的进程退出码。

等待成功,返回子进程pid,等待失败返回-1。

wait是阻塞调用,父进程会一直阻塞。

waitpid的使用(重点)

pid_t waitpid(pid_t pid, int *status, int options);

pid为指定等待子进程的pid(特别的,pid为-1,等待任意一个子进程,pid<-1,等这个pid绝对值的子进程)

status为输出型参数,输出等待子进程的进程退出码

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

options为选项,可以选择等待的方式为 阻塞等待 或 非阻塞等待。(默认为0,阻塞等待)

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

waitpid(-1,NULL,0); 等同于 wait(NULL);

其中NULL,表示父进程不关心子进程退出信息。

测试代码如下:

#include <stdio.h>    
#include <stdlib.h>    
#include <sys/types.h>    
#include <sys/wait.h>    
#include <unistd.h>    int main()    
{    pid_t pid = fork();    if(pid==0)    {    //子进程    int cnt = 5;    while(cnt--)    {    sleep(1);    printf("子进程pid:%d\n",getpid());    }    exit(1);    }    //pid_t ret = wait(NULL);    int status;    pid_t ret = waitpid(-1,&status,0);    printf("等待的子进程pid:%d,status:%d\n",ret,status);    return 0;    
}    

POSIX标准定义
status的位操作需严格遵循从0开始的编号方式。例如:

终止信号编号:存储在第0~7位(即低8位),通过 status & 0x7F 提取。

退出码:存储在第8~15位(即高8位),通过 (status >> 8) & 0xFF 提取。

core dump标志:第7位(从0开始计数,即 status & 0x80 的非零值)。

异常判断:

1)低7个比特位,0

2)一旦低7个比特位不为0,异常退出,退出码无意义!

main函数返回值要写到PCB里,PCB里有对应的exit_code退出码,exit_signal信号码,父进程通过系统调用waitpid获取子进程的状态码,getpid()和getppid()同理,PCB里有存。

为什么?

因为子进程结束后,进入僵尸状态,等待父进程回收,其中进程地址空间、代码和数据以及页表可以释放,但是子进程的PCB(task_struct)需要保留,

WNOHANG 子进程还没退出就立即返回,非阻塞调用。

返回值:

大于0:等待结束

等于0:调用结束,但子进程没有退出

小于0:等待失败

非阻塞调用:可以让等待者,做自己的事情,充分利用资源。

http://www.xdnf.cn/news/4015.html

相关文章:

  • linux 历史记录命令
  • Python爬虫(18)反爬攻防战:动态IP池构建与代理IP实战指南(突破95%反爬封禁率)
  • 全局过滤器与局部过滤器: Vue中的文本格式化工具
  • Python中的JSON库,详细介绍与代码示例
  • STC单片机与淘晶驰串口屏通讯例程之01【新建HDMI工程】
  • 计算机视觉与深度学习 | 图像匹配算法综述
  • Spring Boot 加载application.properties或application.yml配置文件的位置顺序。
  • Qwen3 性价比新王 Qwen3-30B-A3B 本地私有化部署,可灵活切换思考模式
  • 信息系统项目管理师-软考高级(软考高项)​​​​​​​​​​​2025最新(九)
  • Qml组件之AnimatedImage
  • 牛客1018逆序数-归并排序
  • 从入门到登峰-嵌入式Tracker定位算法全景之旅 Part 5 |地图匹配与轻量 SLAM:HMM/Viterbi 与简化图优化
  • 【PaaS与AI融合】MLOps平台的架构设计
  • DHCP服务器配置
  • PHP的现代复兴:从脚本语言到企业级服务端引擎的演进之路-优雅草卓伊凡
  • HTTP协议
  • 如何判断node节点是否启用cgroup?
  • 深入浅出数据库规范化的三大范式
  • 网络传输中字节序
  • 线程局部存储----TLS
  • seaborn
  • suna工具调用可视化界面实现原理分析(二)
  • 黑马点评day02(缓存)
  • 五一の自言自语 2025/5/5
  • 基于python的哈希查表搜索特定文件
  • 【C/C++】各种概念联系及辨析
  • Cadence高速系统设计流程及工具使用
  • [C++] 小游戏 决战苍穹
  • 《Java 高并发程序设计》笔记
  • NSOperation深入解析:从使用到底层原理