进程概念复习
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)
六条执行的结果顺序可以参照下图,反正我是没看懂原因,带着问题以后慢慢再研究把。凭男人的第六感,莫非是红黑树?