一、程序与进程区别
1.程序:
存放在磁盘文件可执行文件(静态存在)
- 特点
- 静态性:程序是静态的,它只是一组指令的集合,在未被执行时,不会占用计算机的运行资源,也不会产生任何实际的操作。
- 可执行性:程序经过编译或解释后,可以被计算机系统识别和执行,从而实现特定的功能。
- 永久性:程序可以长期存储在存储设备中,只要存储介质不损坏,程序就可以一直存在,并在需要时被调用和执行。
2.进程:
运行起来的程序,占用内存空间(动态存在),每个进程都有一个唯一的数字标识符,进程id唯一,但限制时间为这一次的开机维持时间,id随机分配,
- 特点
- 动态性:进程是动态的,它在执行过程中会不断地改变自身的状态,如从就绪状态到运行状态,再到阻塞状态等。
- 并发性:多个进程可以在同一时间内并发执行,操作系统会通过调度算法来分配 CPU 时间片,使得各个进程能够交替执行,从而实现多任务处理。
- 独立性:每个进程都有自己独立的地址空间、内存空间、打开的文件等资源,不同进程之间的资源相互隔离,以保证进程的独立性和稳定性。
- 生命周期性:进程有一个从创建到结束的生命周期,在这个过程中,它会经历各种状态的转换,最终完成任务并结束运行。
二、进程结构
PCB 就像是进程的 “管理档案”,记录着进程的各种信息,而代码段、数据段和堆栈段则是进程运行时所用到的不同功能区域的内存空间。它们共同构成了 Linux 下的进程结构,彼此相互配合,使得进程能够正常运行。
提示:按ctrl+c可以退出运行程序
三、进程函数
返回值为pid_t只是在内核函数里面进行宏定义,为了便于区分,实际id返回还是int型
pid_t fork(void);
返回:子进程中为0,父进程中为子进程I D,出错为-1
#include <iostream>
#include <unistd.h>
using namespace std;
int main()
{
int num = 0;
//开辟新的进程,直接将完整整个函数拷贝(代码段),还拷贝了代码运行的状态,所以子进程从fork之后开始运行
//数据段:还可以复制里面的数据
int pid = fork();
if (pid > 0)
{
while (1)
{
cout << "主进程id=" << getpid() << " 主进程的父进程id=" << getppid()<< "number= " << num++ << endl;
sleep(1);
}
}
else if(pid==0)
{
while (1)
{
cout << "子进程id=" << getpid() << " 子进程的父进程id=" << getppid() << "number= " << num++ << endl;
sleep(1);
}
}
return 0;
}
运行结果:
if与else都运行了,并且num的值在主进程与子进程中各自执行++,互不影响
四、孤儿进程、僵尸状态
进程结束:程序结束不再是main函数走完,而是所有的进程走完,才算结束,看不见窗口也不代表程序的结束(exit()或者retuern;)
孤儿进程:当主进程比子进程先结束时,子进程的getppid()函数返回父进程id为/sbin/upstart(系统进程)
僵尸进程:子进程先于父进程结束,子进程本应该被释放,但不会将子进程释放,让子进程进入僵尸状态(Z+)
这两种情况都会让数据、逻辑出现问题,真实业务操作不会去做sleep,而是依靠cpu时间片轮转执行,解决方案:wait+exit让逻辑出现比较可控的操作
wait(&status);
//等待接收子进程结束状态码,父进程就能知道子进程是否已经结束
//阻塞式函数;逻辑中断//如果没有接收到状态码,就一直堵在这里,不执行后面代码
#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <sys/wait.h>
using namespace std;
int main()
{
int pid = fork();
if (pid > 0)
{
int status;
for (int i = 0; i < 10; i++)
{
cout << "父进程i=" << i << endl;
sleep(1);
}
//等待接收子进程结束状态码,父进程就能知道子进程是否已经结束
//阻塞式函数;逻辑中断
//如果没有接收到状态码,就一直堵在这里,不执行后面代码
wait(&status);
//waitpid(pid,&status,0);
if (WIFEXITED(status))//返回真正自己定义的退出码
{
int x = WEXITSTATUS(status);
cout << "子进程over退出码=" << x << endl;
//父类处理子进程问题
/*
switch(x)
{
case 0:
用户友好提示
break;
case 1:
父类重新执行一次业务
break;
case 2:
记录错误日志
break;
}
*/
}
}
else if(pid==0)//子进程
{
for (int i = 0; i < 25; i++)
{
cout << "子进程1号i=" << i << endl;
sleep(1);
}
exit(1);
//返回不同状态码让父进程处理不同逻辑
/*
if(业务成果)
{
exit(0);
}
else if(失败1)
{
exit(1);
}
else if(失败2)
{
exit(2);
}
*/
}
return 0;
}
但是wait无论有多少个子进程,只接收第一个最先结束子进程退出码
所以使用waitpid,等待特定最慢的进程结束(因为知道pid进程最慢)
waitpid(pid,&status,0);
#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <sys/wait.h>
using namespace std;
int main()
{
int pid = fork();
if (pid > 0)
{
int otherpid = fork();
if (otherpid == 0)
{
for (int i = 0; i < 15; i++)
{
cout << "子进程2号i=" << i << endl;
sleep(1);
}
exit(2);
}
else if(otherpid>0)
{
int status;
for (int i = 0; i < 10; i++)
{
cout << "父进程i=" << i << endl;
sleep(1);
}
waitpid(pid,&status,0);
if (WIFEXITED(status))//返回真正自己定义的退出码
{
int x = WEXITSTATUS(status);
cout << "子进程over退出码=" << x << endl;
}
}
}
else if(pid==0)//子进程
{
for (int i = 0; i < 25; i++)
{
cout << "子进程1号i=" << i << endl;
sleep(1);
}
exit(1);
}
return 0;
}