学习分享
- 1、程序和进程
- 1.1、程序
- 1.2、进程和进程ID
- 2、Linux下的进程结构
- 3、init进程
- 4、获取进程标识
- 5、fork系统调用
- 5.1、fork函数实例分析
- 6、进程的特性
- 7、在Linux下进程指令
- 7.1、终止进程指令
- 7.2、查看进程指令:
- 7.3、以树状图列出进程
- 8、多进程运行异常情况
- 8.1、孤儿进程
- 8.2、僵尸进程
- 8.3、解决方案
- 8.3.1、wait和waitpid函数
- 8.3.1.1、wait函数
- 8.3.1.2、waitpdi函数
- 8.3.1.3、wait和waitpid的区别
- 8.3.2、wait函数解决孤儿进程
- 8.3.3、wait函数解决僵尸进程
- 8.4、进程退出码传递示例
- 9、子进程结束的方式
1、程序和进程
1.1、程序
程序(program)是存放在磁盘文件中的可执行文件。
1.2、进程和进程ID
程序的执行实例被称为进程(process)。本书的每一页几乎都会使用这一术语。内核观点:分配系统资源(CPU,内存)的实体。
每个linux进程都一定有一个唯一的数字标识符,称为进程PID(process ID)。进程ID总是一非负整数。
使用指令:ps ajx
2、Linux下的进程结构
操作系统将.exe文件从磁盘搬到内存后被CPU处理,os内可能存在多个进程,此时操作系统就要管理好这些进程。操作系统将可执行程序的各种数据提取出来,在创建一个结构体把数据存进去,由于同时会存在多个进程,所以操作系统使用链表或其他数据结构将它们链接在一起。
PCB(进程控制块)是这个结构体的总称,在Linux系统下,PCB具体叫做:struct task_struct‘
3、init进程
进程ID为1通常是init进程,在自举过程结束时由内核调用。
init进程绝不会终止。
它是一个普通的用户进程(与交换进程不同,它不是内核中的系统进程),但是它以超级用户特权运行。
4、获取进程标识
#include <iostream>
#include <sys/types.h>
#include <unistd.h>
using namespace std;
int main()
{
pid_t pid =getpid();
while(1)
{
cout<<"pid = "<<pid<<endl;
sleep(1);
}
return 0;
]
5、fork系统调用
由fork创建的新进程被称为子进程( child process)。
该函数被调用一次,但返回两次。两次返回的区别是子进程的返回值是0,而父进程的返回值则是子进程的进程ID。
一般来说,在f o r k之后是父进程先执行还是子进程先执行是不确定的。这取决于内核所使用的调度算法。
#include <iostream>
#include <sys/types.h>
#include <unistd.h>
using namespace std;
int main()
{
pid_t pid;
int number =0 ;
cout <<"main 函数主进程 pid = "<<getpid()<<endl;
//创建新的子进程
pid =fork();
if(pid ==0)
{
while(1)
{
cout<<"pid = "<<pid<<"自己 getpid ="<<getpid()<<"父进程为:"<<getppid()<<end;
number++;
cout<<"number"<<number<<endl;
sleep(1);
}
}
else if(pid > 0)
{
while(1)
{
cout<<"pid = "<<pid<<"getpid ="<<getpid()<<end;
numbe++;
cout<<"number"<<number<<endl;
sleep(1);
}
}
return 0;
]
5.1、fork函数实例分析
#include <iostream>
#include <sys/types.h>
#include <unistd.h>
using namespace std;
int main()
{
pid_t pid;
for(int i =0;i<2;i++)
{
pid =fork();
cout<<"pid = "<<pid <<"当前进程 ID = "<<getpid()<<endl;
}
return 0;
]
6、进程的特性
1、操作系统分配资源的基本单位
2、一个操作系统下的多个进程是同时运行
3、多个进程的运行,是随机的,代码无法决定进程执行顺序,只能做到进程创建、结束
4、多个进程之间不会相互影响,它们各自有属于自己的PCB(代码段、数据段、堆栈段)
5、多个进程之间不能共享数据(IPC技术才能解决数据互通、共享)
7、在Linux下进程指令
7.1、终止进程指令
在我们的程序运行时,可以在运行的地方按CTRL+C来结束进程,但是还有一种方法可以结束进程:
使用指令:kill -9 要杀掉的进程ID
(注:这里的-9是信号参数,直接使用即可)
7.2、查看进程指令:
ps -aux
7.3、以树状图列出进程
pstree
8、多进程运行异常情况
8.1、孤儿进程
父进程比子进程先结束,这个子进程称为孤儿进程。
孤儿进程的父进程统一转变为1号进程,这个1号进程实际上就是操作系统本身!
#include <iostream>
#include <sys/types.h>
#include <unistd.h>
using namespace std;
int main()
{
pid_t pid;
cout <<"main 函数主进程 pid = "<<getpid()<<endl;
//创建新的子进程
pid =fork();
if(pid ==0)
{
for(int i=0;i<30;i++)
{
cout<<"自己 getpid ="<<getpid()<<" i= "<<i<<end;
sleep(1);
}
}
else if(pid > 0)
{
for(int i=0;i<15;i++)
{
cout<<"父进程 getpid ="<<getpid()<<"i = "<<i<<end;
sleep(1);
}
}
return 0;
]
8.2、僵尸进程
子进程比父进程先结束,子进程逻辑走完,但是占用资源(资源还没有释放)。
- 若一个进程长期处于z状态(僵尸状态),不及时回收,此时会有内存泄漏的风险!
当一个子进程结束运行时,它与其父进程之间的关联还会保持到父进程也正常地结束运行或者父进程调用了wait才告终止。
进程表中代表子进程的数据项是不会立刻释放的,虽然不再活跃了,可子进程还停留在系统里,因为它的退出码还需要保存起来以备父进程中后续的wait调用使用。它将称为一个“僵尸进程”。
#include <iostream>
#include <sys/types.h>
#include <unistd.h>
using namespace std;
int main()
{
pid_t pid;
cout <<"main 函数主进程 pid = "<<getpid()<<endl;
//创建新的子进程
pid =fork();
if(pid ==0)
{
for(int i=0;i<15;i++)
{
cout<<"自己 getpid ="<<getpid()<<" i= "<<i<<end;
sleep(1);
}
}
else if(pid > 0)
{
for(int i=0;i<30;i++)
{
cout<<"父进程 getpid ="<<getpid()<<"i = "<<i<<end;
sleep(1);
}
}
return 0;
]
8.3、解决方案
- 子进程exit 父进程wait
8.3.1、wait和waitpid函数
当一个进程正常或异常终止时,内核就向其父进程发送SIGCHLD信号。因为子进程终止是个异步事件(这可以在父进程运行的任何时候发生),所以这种信号也是内核向父进程发的异步通知。父进程可以忽略该信号,或者提供一个该信号发生时即被调用执行的函数(信号处理程序)。对于这种信号的系统默认动作是忽略它。
wait函数用于使父进程阻塞,直到一个子进程结束或者该进程接收到一个指定信号为止。
8.3.1.1、wait函数
函数说明
wait返回状态检测
8.3.1.2、waitpdi函数
函数说明
8.3.1.3、wait和waitpid的区别
8.3.2、wait函数解决孤儿进程
#include <iostream>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
using namespace std;
int main()
{
int status =0;
pid_t pid;
cout <<"main 函数主进程 pid = "<<getpid()<<endl;
//创建新的子进程
pid =fork();
if(pid ==0)
{
for(int i=0;i<30;i++)
{
cout<<"自己 getpid ="<<getpid()<<" i= "<<i<<end;
sleep(1);
}
}
else if(pid > 0)
{
for(int i=0;i<15;i++)
{
cout<<"父进程 getpid ="<<getpid()<<"i = "<<i<<end;
sleep(1);
}
wait(&status);
}
return 0;
]
8.3.3、wait函数解决僵尸进程
#include <iostream>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <unistd.h>
using namespace std;
int main()
{
int status =0;
pid_t pid;
cout <<"main 函数主进程 pid = "<<getpid()<<endl;
//创建新的子进程
pid =fork();
if(pid ==0)
{
for(int i=0;i<15;i++)
{
cout<<"自己 getpid ="<<getpid()<<" i= "<<i<<end;
sleep(1);
}
exit(0);
}
else if(pid > 0)
{
wait(&status);//阻塞函数 等待某一个事件发生 等待子进程exit传递退出码
for(int i=0;i<30;i++)
{
cout<<"父进程 getpid ="<<getpid()<<"i = "<<i<<end;
sleep(1);
}
}
return 0;
]
8.4、进程退出码传递示例
#include <iostream>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <unistd.h>
using namespace std;
int main()
{
int status =0;
pid_t pid;
cout <<"main 函数主进程 pid = "<<getpid()<<endl;
//创建新的子进程
pid =fork();
if(pid ==0)
{
for(int i=0;i<15;i++)
{
cout<<"自己 getpid ="<<getpid()<<" i= "<<i<<end;
sleep(1);
}
exit(0);
}
else if(pid > 0)
{
wait(&status);//阻塞函数 等待某一个事件发生 等待子进程exit传递退出码
cout<<"status="<<status<<endl;
//判断子进程是否结束
if(WIFEXITED(status)>0)
{
cout<<"退出码"<<WEXITSTATUS(status)<<endl;
}
for(int i=0;i<30;i++)
{
cout<<"父进程 getpid ="<<getpid()<<"i = "<<i<<end;
sleep(1);
}
}
return 0;
]
9、子进程结束的方式
- 1、return;
- 2、break;
- 3、exit(0);