进程创建
fork()–通过复制调用进程创建一个新的子进程。
特点:复制(pcb–代码共享,数据独有)、程序计数器(运行位置一样)。
返回值:父进程返回子进程的PID,子进程返回0。
写时拷贝技术:操作系统通过复制父进程创建子进程,子进程初始时与父进程指向同一块物理内存区域,当内存数据发生改变时,,会为子进程重新开辟内存更新页表,这样也可以保证进程的独立性,独立才能更加稳定。
vfork()–创建的子进程与父进程共享数据段,而且由vfork()创建的子进程将先于父进程运行。
共用一个虚拟地址空间,为防止函数调用栈混乱,因此父进程调用vfork会阻塞,阻塞到子进程exit退出或者子进程程序替换,开辟内存创建自己的地址空间。
但是fork通过写时拷贝技术这个点将vfork淘汰了,已经很少使用vfork。
创建子进程代码实现:https://blog.csdn.net/qq_43414142/article/details/90678431
进程等待
等待子进程的状态改变(等待子进程退出),获取子进程返回值,避免僵尸进程。
进程等待原因:子进程退出时为了保存退出原因,因此操作系统未完全释放子进程全部资源,此时通知父进程获取子进程退出返回值,允许系统释放资源,由于父进程没有关注到这个请求,会导致子进程成为僵尸进程。
如果父进程获取了子进程的返回值,僵尸子进程没有存在的意义就会被释放资源。
如何等待呢?如下:
pid_t wait(int* status);
pid_t waitpid(pid_t pid, int* status,int options);
wait接口功能是一直等待一个子进程退出,子进程退出后,获取返回值,放到传入参数statu中,如果一直没有子进程退出,wait将一直阻塞(简单来说就是当前状态不具备完成条件,等待直到条件具备完成功能后返回。与之对应的非阻塞就是不具备完成条件,立即报错返回)。
status中低16位的高8位存储子进程退出返回值,低8位中的高1位存储core dump标志。其他七位若全是0,表示没有异常退出信号,表示程序正常退出;否则表示异常退出,返回值将不具有意义。
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/wait.h>
int main(){
int pid=fork();
if(pid<0){
printf("fork error:%s\n",strerror(errno));
perror("fork error");
}
else if(pid==0){
sleep(3);
exit(111);
}
int status;
while(waitpid(-1,&status,WNOHANG)==0){
//非阻塞轮询
printf("drink water\n");
sleep(1);
}
if((status&0x7f)==0){
printf("exit code:%d\n",(status>>8)&0xff);
}
while(1){
printf("haha,i want to drink\n");
sleep(2);
}
return 0;
}
结果:
进程退出
- main函数中的return。
- void exit(int status)。 库函数,退出前刷新缓冲区。
- void _exit(int status)。 系统调用接口,不会刷新缓冲区,缓冲区的数据会被丢失。
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
//简单演示这三种方法,及效果
int main(){
printf("============");
sleep(2);
return 0;
//_exit(1); //任意位置调用退出进程 且不刷新缓冲区
//exit(1); //任意位置调用退出进 刷新缓冲区
printf("game over!!\n");
}
结果:只执行了第一个打印,第二个打印并未执行。