Linux——进程

🌈个人主页:Yui_
🌈Linux专栏:Linux
🌈C语言笔记专栏:C语言笔记
🌈数据结构专栏:数据结构
🌈C++专栏:C++

1. 进程

1.1 基本概念

当我们编写完代码运行并让代码编译起来时,在当前路径下会出现由代码编译而成的可执行文件.exe。当我们运行这个可执行文件时,该程序便会被加载到计算机的内存当中,一些教材可能会把这个已经运行起来的程序叫做进程,其实这是不完整的,真实的进程还需要包括管理这个程序的PCB。

  • 课本概念:程序的一个执行实例,正在执行的程序。
  • 内核观念:担当分配系统资源(CPU时间,内存)的实体。

1.2 描述进程 -PCB

  • 进程信息被放在一个叫做进程控制块(process control block)的数据结构中,可以理解为进程属性的集合。
  • 在教材中一般叫PCB(process control block),Linux操作系统下的PCB是task_struct.
    PCB是一个结构体,是为了管理加载到内存的程序而产生的。

1.2.3 介绍task_struct

在Linux中每一个进程都由task_struct数据结构来定义,task_struct就是我们通常所说的PCB,它是队进程的唯一控制手段也是最有效的手段,当我们调用fork()时,系统会为我们产生一个task_struct结构。然后从父进程继承一些数据,并把新的进程插入到进程树,以待进行进程管理,因此了解task_struct的结构对于我们理解进程调度的关键。
task_struct是如何管理进程的,先描述,再组织。在task_struct结构中有以下定义:

  1. 进程状态,将记录进程在等待、运行、或者死锁。
  2. 调度信息,由哪个调度函数调度,怎样调度等。
  3. 进程的通讯情况。
  4. 因为要插入进程树,必须有联系父子兄弟的指针,当然是task_struct类型。
  5. 时间信息,比如计算好执行时间,以便于CPU分配。
  6. 标号,决定计进程归属。
  7. 可以读写打开的一些文件信息。
  8. 进程上下文和内核上下文。
  9. 处理上下文。
  10. 内存信息。
  11. 等等
    因为每个PCB都是这样的,只有这些结构才能满足另一个进程的所有要求。
    task_struct:
struct task_struct
{
	//标识符:描述本进程的唯一标识符,用来区分其他进程
	//状态:任务状态,退出代码,退出信号
	//优先级:相对于其他进程的优先级
	//程序计数器:程序中即将被执行的下一条指令的地址。
	//内存指针:包括程序代码的进程相关数据的指针,还有其他进程共享的内存块的指针。
	//上下文数据:进程执行时处理的寄存器中的数据。
	//I/O状态信息:包括显示的I/O请求,分配给进程的I/O设备和被进程使用的文件列表。
	//记账信息:可能包括处理器时间总和,使用的时钟数总和,时间限制,计账号等。
	//...
};

具体结构看:task_struct结构体结构
提问:为什么程序加载到内存中,变成进程之后,我们要给进程形成一个PCB对象呢?
回答:因为操作系统需要进行管理,操作系统是无法直接对正在运行的程序做管理的,需要通过一个PCB来间接管理,PCB上有进程的信息,对PCB对象的管理其实就是对进程的管理。
所以:进程 = 内核PCB对象 + 正在运行的程序
这就是说,所有对进程的控制和操作,都只和进程PCB有关,和进程的可执行程序无关!如果你想,你可以把PCB放到任何数据结构当中。
PCB与进程

1.3 查看进程

进程信息可以通过/proc系统文件中查看。

  • 当你需要获取PID为1的进程信息,你需要查看/proc/1这个文件夹。
    进程

  • 大多数进程信息同样可以使用top和ps这些用户级工具来获取。

ps aux | grep test | grep -v grep

过滤出于test有关的进程

1.4 通过系统调用获取进程标识符

  • 进程id(PID)
  • 父进程id(PPID)
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main()
{
    pid_t id = getpid();//获取该程序进程标识符id
    pid_t fid = getppid();//获取该程序进程父进程的标识符id
    while(1)
    {
        printf("i am process! pid = %d,ppid = %d\n",id,fid);
        sleep(1);
    }
    return 0;
}

pid_t也是一个类型,就像int一样,int用来标识整型,pid_t就用来标识进程号类型。

进程

如何判断确实是这样呢?

while :;do ps ajx|head -1 && ps ajx|grep mybin|grep -v grep;sleep 1; done

使用该条语句可以在屏幕中循环打印mybin的进程是否存在。

1.5 通过系统调用创建进程 -fork

  • 运行man fork认识fork
  • fork有两个放回值。
  • 父子进程代码共享,数据各开辟空间,私有一份(采用写时拷贝)
    功能
    fork是复制进程的函数,程序一开始就会产生一个进程,当这个进程(代码)执行到fork()时,fork就会复制一份原来的进程来产生一个新的进程,新产生的进程为子进程,而原来的进程为父进程,此时父子进程是共存的,他们会同时向下执行代码。
    关于放回值
    在父进程中,fork会返回新创建子进程的进程ID,在子进程中,fork返回0。如果出现错误,fork会返回一个负值。

也就是说,在fork函数执行完毕后,如果创建进程成功,则出现两个进程,一个子进程,一个父进程。在子进程中,fork函数返回0,在父进程中,fork返回新创建的子进程的进程ID。我们可以通过dork返回的值来判断当前进程是子进程还是父进程。

下面写一段代码,执行逻辑将会和过去不同。

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main()
{
	pid_t id = fork();
	if(id<0)
	{
		printf("出错!!!\n");
		return 0;
	}
	if(id == 0)//子进程
	{
		printf("i am a precoss PID:%d PPID:%d\n",getpid(),getppid());
	}
	else
	{
		printf("i am a precoss PID:%d PPID:%d\n",getpid(),getppid());
	}
	}
	return 0;
}
//打印结果
/*
i am a precoss PID:29088 PPID:19144
i am a precoss PID:29089 PPID:29088
*/

父子进程

运行结果:
运行结果

简直匪夷所思!if里的内容和else里面的内容居然同时执行了。这是怎么回事呢?
就像前面所说,在fork函数执行完毕后,如果创建进程成功,则出现两个进程,一个子进程,一个父进程。子进程和父进程是同时运行的,可以看出两个程序,不过他们的代码数据是相同的。由由于fork在父子进程中的返回值不同,也就造成了这种看上去if和else同时执行的情况。

2. 进程状态

  • 为了弄明白正在运行的进程是什么意思,我们需要知道进程的不同状态。一个进程可以有多个状态,在Linux内核中,进程也可以叫做任务。
  • 8
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Yui_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值