DAY16-进程知识点复习

本文介绍了进程的概念,包括进程描述符task_struct、PID、进程状态以及进程上下文切换。重点讨论了fork函数的使用,解释了其一次调用两次返回的特性,并通过示例代码展示了父子进程的交互。还提到了僵尸进程的问题以及循环fork的场景分析。
摘要由CSDN通过智能技术生成

进程概念复习

1. 进程就是把代码running起来的状态

2. 进程描述符(Process Descriptor)包含的是单个进程具体的信息,包含进程状态state,进程优先级priorty,父进程,pid等等信息。它通过task_struct结构体存放在内核的任务队列内(task list),是一个双向循环列表。单个记录就代表一个task_struct,也就是一个进程的信息。

3. task_struct结构体通过slab进行分配

4. 进程标识号:Process IDentification, 简称就是PID,也是我们在Linux中最熟悉的,一个PID就代表一个进程号。由于short int短整型变量最大值为32768,所以在32位的系统内默认上限是32768,可通过/proc/sys/kernel/pid_max进行在线调整,即时生效。

5. 进程的5种状态:

  • task_running: 进程在用户空间唯一存在的一种状态
  • task_interruptible:处理slepp状态,等待达到条件后被唤醒,内核将状态调整为task_running
  • task_uninterruptible: 无法被中断,此状态的任务对信号不做响应。
  • __task_traced:被追踪,如ptrace
  • __task_stopped:进程停止

6. 进程上下文切换:用户空间的进程在执行代码时,如需要写入数据,就会进行系统调用,通过write()调用syscall函数,陷入内核空间调用写入相关函数,完成后返回,回到用户空间,进程继续执行后续操作。

## fork函数实践

浅析1次调用2次返回

貌似网上对fork函数讨论的最多的就是调用返回,对此的面试题层出不穷。

我从网上找了一段简单的代码,做上注释,今天唯一满意的一点就是我能看得懂简单的C代码。。。然后依葫芦画瓢修改了一些输出

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
int main()
{ 
   char *s = NULL;  //定义变量s为空值 
   int n = 0;       //定义n为数值0
pid_t pid = fork();  //对进程进行fork
assert( pid != -1 ); //判断pid返回值不为-1
// 1)在父进程中,fork返回新创建子进程的进程ID;
// 2)在子进程中,fork返回0;
// 3) 如果出现错误,fork返回一个负值;
 if ( pid == 0 )
{
   s = "child";  //当返回值为0,确认为子进程,赋值n=4
   n = 4;
}
else
{
  s = "parent";  //返回值不为0时则赋值s=paraent,n=8
  n = 10;
}
int i = 0;
for(; i < n; i++ )
{
  printf("pid=%d,parent_pid=%d,s=%s\n",getpid(),getppid(),s); //获取进程的pid和父进程pid以及对应s的变量输出
  sleep(1);
}
exit(0);
}

从输出来看,首先返回的值是父进程返回的,其次才是子进程返回。

父进程返回的pid是7328, 子进程返回的pid是7329,其父进程pid是7328

从子进程退出的机制反向分析,与之前学习的进程的退出逻辑也是一样的。当子进程通过exit()退出以后,父进程会获得子进程的PCB信息。

此时需要通过父进程的机制对子进程进行释放,直接通过kill -9命令是无效的,因为说白了,通过ps -ef能看到的只是子进程的PCB残留信息,真实的进程代码已经被回收了。不过残留的信息也会占用一定的系统资源,尤其是文件描述符fd。

僵尸进程

子进程PID18441, kill -9 18441,从下图可以明确看到,无效。只有kiill主进程,再能释放子进程的残留信息。

修改一下代码,把parent和child互换一下,模拟一下主进程在子进程之前退出的情况,可以发现,子进程的父进程指向了进程1。

循环fork的重点

#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>

int main(void)
{
    int i;
    for(i=0; i<2; i++){
        fork();
        printf("i:%d,pid:%d,ppid:%d\n",i,getpid(),getppid());
    }
    wait(NULL);
   wait(NULL);
  return 0;
}

输出如下

i:0,pid:57742,ppid:18378
i:1,pid:57742,ppid:18378
i:0,pid:57743,ppid:57742
i:1,pid:57743,ppid:57742
i:1,pid:57744,ppid:57742
i:1,pid:57745,ppid:57743

以下是绞尽脑汁以后对这个程序的解释,根据fork的位置,会输出6行内容:

首先当i=0,自身进程id为57742,接着执行fork命令,fork子进程57743

接着父进程57742 print一行 (id:0, pid:57742,ppid:18378),

对应子进程54473 print一行 (i:0, pid57743, ppid:57742),

父进程57742执行i++, i变成了1,随后fork(), fork出子进程57744

随后父进程57742 print一行(i:1, pid57742,ppid18378),随后i++,不小于2, 退出exit

父进程在i=1时fork出的子进程57744执行print (i:1, pid:57744, ppid: 57742') ,随后i++ .不小于2, 退出exit

子进程57743在i=1时,fork子子进程57745,同时print (i=1, pid=57743,ppid:57742)

子子进程57745执行print(i:1, pid:57745,ppid:57743)

六条执行的结果顺序可以参照下图,反正我是没看懂原因,带着问题以后慢慢再研究把。凭男人的第六感,莫非是红黑树?

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值