文章目录
1.Linux进程
1.1进程概念:
- 从用户角度:进程就是一个正在运行中的程序。
- 操作系统角度:操作系统运行一个程序,需要描述这个程序的运行过程,这个描述通过一个结构体task_struct{}来描述,统称为PCB,因此对操作系统来说进程就是PCB(process control block)程序控制块
- 进程的描述信息有:标识符PID,进程状态,优先级,程序计数器,上下文数据,内存指针,IO状态信息,记账信息。都需要操作系统进行调度。
1.2进程状态:
/*
* The task state array is a strange "bitmap" of
* reasons to sleep. Thus "running" is zero, and
* you can test for combinations of others with
* simple bit tests.
*/
static const char * const task_state_array[] = {
"R (running)", /* 0 */
"S (sleeping)", /* 1 */
"D (disk sleep)", /* 2 */
"T (stopped)", /* 4 */
"t (tracing stop)", /* 8 */
"X (dead)", /* 16 */
"Z (zombie)", /* 32 */
};
2.fork()函数
2.1概述:
使用 fork() 函数得到的子进程是父进程的一个复制品,它从父进程处继承了整个进程的地址空间:
包括进程上下文(进程执行活动全过程的静态描述)、进程堆栈、打开的文件描述符、信号控制设定、进程优先级、进程组号等。子进程所独有的只有它的进程号,计时器等(只有小量信息)。
pid_t fork(void);
头文件:
#include <sys/types.h>
#include <unistd.h>
功能:
- 用于从一个已存在的进程中创建一个新进程,新进程称为子进程,原进程称为父进程。
返回值:
- 成功:子进程中返回 0,父进程中返回子进程 ID。pid_t,为无符号整型。
- 失败:返回 -1。
代码示例:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main(int argc, char *argv[])
{
pid_t pid;
pid = fork();
if( pid < 0 ){ // 没有创建成功
perror("fork");
}
if(0 == pid){ // 子进程
while(1){
printf("I am son\n");
sleep(1);
}
}
else if(pid > 0){ // 父进程
while(1){
printf("I am father\n");
sleep(1);
}
}
return 0;
}
2.2 运行结果:
2.3总结:
- 通过运行结果,可以看到,父子进程各做一件事(各自打印一句话)。这里,我们只是看到只有一份代码,实际上,fork()
以后,有两个地址空间在独立运行着,有点类似于有两个独立的程序(父子进程)在运行着。需要注意的是,在子进程的地址空间里,子进程是从fork() 这个函数后才开始执行代码。 - 在 fork() 之后是父进程先执行还是子进程先执行是不确定的。这取决于内核所使用的调度算法。
3.僵尸进程
一个进程使用 fork 创建子进程,如果子进程退出而父进程并没有调用 wait() 或者 waitpid() 获取子进程信息,那么子进程的描述符仍然保存在系统中,这种进程就被称为僵尸进程。
Z 进程
3.1代码模拟僵尸进程:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
printf("输入一遍\n");
pid_t ret = fork();
if(ret > 0)
{
//parent
while(1)
{
printf("I am parent! pid is : %d\n", getpid());
sleep(1);
}
}
else if(ret == 0)
{
/child
int count = 0;
while(count<5)
{
printf("I am child! pid is : %d, ppid: %d\n", getpid(), getppid());
count++;
sleep(2);
}
exit(0);
}
else
{
printf("fork error\n");
}
sleep(1);
return 0;
}
3.2僵尸进程结果展示:
3.3僵尸进程危害:
子进程的退出状态会一直被维持下去,维护退出状态本身就是要用数据维护,也属于进程基本信息,被保存在
task_struct
中。也就是说只要Z状态不退出,那么PCB要一直维护退出信息。如果一个父进程创建了很多的子进程,都不进行回收,就会造成内存资源的极大浪费
,也会造成内存泄露
。
4.孤儿进程
父进程提前退出,子进程就称为“孤儿进程”。一旦父进程退出,子进程要退出的时候就是Z状态,因为没有进程接收它的退出信息,所以孤儿进程要被1号init进程领养回收,从而避免子进程变为僵尸进程。
4.1代码模拟孤儿进程:
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
int main()
{
pid_t ret = fork();
if(ret < 0)
{
printf("创建进程失败!\n");
}
else if(ret > 0)
{
int count = 0;
while(count++ < 5)
{
printf("我是父进程(id:%d)\n",getpid());
sleep(2);
}
exit(0);
}
else
{
while(1)
{
printf("我是子进程(id:%d)\n",getpid());
sleep(1);
}
}
sleep(1);
return 0;
}
4.2孤儿进程结果展示:
4.3总结:
os考虑了这种情况的发生,在父进程退出,子进程还在运行(子进程被称为孤儿进程)的时候重新给他找一个父亲,如图所示就是1号进程systemd。
1号进程(init)扩展:
由0进程创建,完成系统的初始化. 是系统中所有其它用户进程的祖先进程。
Linux中的所有进程都是有init进程创建并运行的。首先Linux内核启动,然后在用户空间中启动init进程,再启动其他系统进程。在系统启动完成完成后,init将变为守护进程监视系统其他进程。