🦄 个人主页——🎐开着拖拉机回家_Linux,大数据运维-CSDN博客 🎐?🍁
🪁🍁🪁🍁🪁🍁🪁🍁 🪁🍁🪁🍁🪁🍁🪁 🪁🍁🪁🍁🪁🍁🪁🍁🪁🍁🪁🍁
感谢点赞和关注 ,每天进步一点点!加油!
目录
进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配的基本单位,是操作系统结构的基础。在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体(百度百科)。
多个不同的进程可以包含相同的程序:一个程序在不同的数据集里就构成不同的进程,能得到不同的结果;但是执行过程中,程序不能发生改变。
引入一个新概念: 进程控制块PCB(process control block)。PCB就是进程属性的集合(数据结构),里面存储的是进程信息。
进程信息被放在一个叫做进程控制块的数据结构中,可以理解为进程属性的集合,称之为PCB(process control block),Linux操作系统下的PCB是: task_struct。
即task_struct是Linux内核的一种数据结构,它会被装载到RAM(内存)里并且包含进程的信息。
根目录下的proc目录,/proc下存储着进程信息。
目录名为数字的即为进程信息的目录,每个目录内存储着他们对应的进程信息。而这些数字对应着该进程的标识符PID。如下查看标识符PID=18964的进程信息:
我们进入进程 18964进程目录
实例:ps ajx/ps aux
该命令可以查看所有系统进程。如下查看PID=18964的进程信息
创建如下测试脚本并执行
#!/bin/bash
proc_test()
{
while(true)
do
echo "I am a process!"
sleep 1
done
}
proc_test
## 执行
sh proc_test_18964.sh
另外通过指令对进程进行检测,检测它是否运行:
ps ajx | head -1 && ps ajx | grep proc_test | grep -v grep
这里grep实际也是进程,且该进程内包含有proc_test的信息,所以也显示出来了。-v选项是反向搜索的意思,即过滤掉包含有grep内容的信息。
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
int main()
{
pid_t id = getpid();
while (1)
{
printf("I am a process!pid:%d\n", id);
sleep(1);
}
return 0;
}
执行上面可执行程序并运行如下命令进行监控
while :; do ps ajx | head -1 && ps ajx | grep proc_test | grep -v grep ; sleep 1;done
函数getppid(获取父进程的进程标识符),一般在Linux中普通进程都有他的父进程。
每一个子进程都是由父进程创建出来的。
子进程只能有一个父进程,父进程可以有多个子进程。每次执行可执行程序,进程标识符会改变,因为每次都会创建新的进程。
编写如下测试代码
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
int main()
{
pid_t id = getpid();
pid_t fid = getppid();
while (1)
{
printf("I am a process!pid:%d ppid:%d\n", id, fid);
sleep(1);
}
return 0;
}
?执行结果如下图:
?多次执行我们发现程序多次执行子进程每次都会创建新的新的进程标识符,父进程标识符没有改变。如下父进程是bash命令行解释器。
pid_t fork(void);
?一个进程包括代码、数据和分配给进程的资源。fork 函数会新生成一个进程,调用 fork 函数的进程为父进程,新生成的进程为子进程。在父进程中返回子进程的 pid,在子进程中返回 0,失败返回-1。
创建测试代码:?
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
int main()
{
pid_t pid;
printf("before fork : pid is : %d, ppid is : %d\n", getpid(), getppid());
pid=fork();
printf("after fork : pid is : %d, ppid is : %d, fork return %d\n", getpid(), getppid(),pid);
sleep(2);
return 0;
}
编译后执行
fork函数执行后,后面的"after fork"执行了两次,8897作为父进程创建了子进程 8898。也就是说fork之后,代码共享,从一个进程分为两个分支,一为父,一为子。
进程调用fork,当控制转移到内核中的fork代码后,内核会:
所以,fork之前父进程独立执行,fork之后父子两个执行流分别执行。注意:fork之后谁先执行完全由调度器决定。
fork它仅仅被调用一次,却能够返回两次,它可能有三种不同的返回值:
(1) 在父进程中,fork返回新创建子进程的进程ID;
(2)在子进程中,fork返回0;
(3)如果出现错误,fork返回一个负值;
一个进程调用fork()函数后,系统先给新的进程分配资源,例如存储数据和代码的空间。然后把原来的进程的所有值都复制到新的新进程中,只有少数值与原来的进程的值不同。相当于克隆了一个自己。
进程状态:
TASK_RUNNING:就绪/可运行状态
TASK_INTERRUPTABLE:进程被挂起(睡眠),直到等待条件为真被唤醒
TASK_UNINTERRUPTABLE:深度睡眠,睡眠期间不响应信号
TASK_STOPPED:进程的执行被暂停
TASK_TRACED:被其它进程跟踪,常用于调试
EXIT_ZOMBIE:僵死状态,进程的执行被终止
EXIT_DEAD:僵死撤销状态,防止wait类系统调用的竞争状态发送
可以使用man ps 查看进程状态 Ss、Sl、S+、Z、I 释义
具体解释如下:
1.僵死状态(Zombies)是一个比较特殊的状态。当子进程退出并且父进程(使用wait()系统调用)没有读取到子进程退出的返回代码时就会产生僵死(尸)进程
2.僵死进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码。
3.所以,只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,子进程进入Z状态
测试代码 :
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
pid_t id = fork();
if (id < 0) {
perror("fork");
return 1;
}
else if (id > 0) { //parent
while(1)
{
printf("parent[%d] is sleeping...,ppid: %d\n", getpid(),getppid());
sleep(1);
}
}
else {
printf("child[%d] is begin Z...,ppid: %d\n", getpid(),getppid());
sleep(5);
exit(1);
}
return 0;
}
fork函数执行后子进程PID:15156 在5 s后exit(1)异常退出 ,父进程15155没有读取到正常的退出代码导致产生僵尸进程。
进程PID:15156的名称加上了【】且后面跟着< defunct > (失效的)
修改代码,让子进程和父进程同时不间断运行
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
pid_t id = fork();
if (id < 0) {
perror("fork");
return 1;
}
else if (id > 0) { //parent
while(1)
{
printf("parent[%d] is sleeping...,ppid: %d\n", getpid(),getppid());
sleep(1);
}
}
else {
while(1)
{
printf("child[%d] is begin Z...,ppid: %d\n", getpid(),getppid());
sleep(1);
}
}
return 0;
}
运行
while :; do ps ajx | head -1 && ps ajx | grep pro_test | grep -v grep ; sleep 1;done
监控进程状态
杀掉父进程(10552),子进程被编号为1的进程接管,该1号进程就是bash,而bash就是父进程的父进程,父进程被杀死后,bash进程就接管了子进程,这种失去“爸爸”后被接管的进程就被称为孤儿进程。且子进程从前台进程变成了后台进程。
原文链接:fork函数详解-CSDN博客
原文链接:Linux进程状态_task_interrupt-CSDN博客
?