【Linux】进程|进程的查看与管理|创建进程

目录

1️⃣ 进程的概念(Process)

1.什么是进程?

2.多进程管理

3. 描述进程-PCB

2️⃣ 进程的查看与管理

1.使用指令查看进程

2.通过系统调用函数查看pid

3.杀掉进程

4.ppid(父进程id)

3️⃣ 创建进程

fork()的工作原理

父进程与子进程的关系

写时复制(Copy-on-Write)

fork()的使用


1️⃣ 进程的概念(Process)

1.什么是进程?

❍ 课本概念:程序的一个执行实例,正在执行的程序等

❍ 内核观点:担当分配系统资源(CPU时间,内存的实体)

简单来说进程 == PCB(进程控制块) + 进程对应的代码和数据;一个进程对应一个PCB操作系统对进程的管理,最终变成了对链表的增删查改。

注意:可执行程序加载到内存不是进程,只是进程对应的代码和数据

2.多进程管理

操作系统中可以存在大量的进程,操作系统需要管理这些进程,以确保系统资源(如CPU时间,内存等)的合理分配。管理进程的本质是对进程数据的管理

3. 描述进程-PCB

进程信息被放在一个叫做进程控制块的数据结构中,可以理解为进程属性的结合,课本上称之为 PCB(process control block),Linux操作系统下的PCB是:task_struct

task_struct 的结构

struct task_struct {
    volatile long state;
    void *stack;
    atomic_t usage;
    unsigned int flags;     
    unsigned int ptrace;
    unsigned long ptrace_message;
    siginfo_t *last_siginfo; 
    int lock_depth;         
    // ... 其他属性
};

 task_struct 包含进程的所有属性数据,如进程状态、优先级、程序计数器、内存指针、上下文数据、I/O状态信息和记账信息等。

2️⃣ 进程的查看与管理

1.使用指令查看进程

ps指令:

语法:ps[选项]

功能:显示当前终端会话中属于当前用户的进程列表。

选项:

常见格式:ps -ajx | head -1 && ps -ajx | grep 可执行文件

 

2.通过系统调用函数查看pid

操作系统对进程进行管理,但是用户不能直接访问操作系统,因此需要通过系统提供的系统调用函数来管理进程。

✸ 查看pid的函数为getpid();

✸ 可以通过man手册查询,输入指令:man getpid

 通过创建一个C语言代码来查看pid :

#include <stdio.h>    
#include <unistd.h>    
#include <sys/types.h>    
    
int main(){                                                                             pid_t id = getpid();    
  pid_t parent = getppid();    
  while(1)    
  {    
   printf("I am a process, pid: %d, ppid: %d\n", id, parent);    
   sleep(1);    
  }    
  return 0;    
}    

3.杀掉进程

方法1: crtl + c ;方法2: kill -9 pid

 

注意:kill掉任意一个进程不会影响另一个进程!!!(保证进程的独立性)

4.ppid(父进程id)

刚刚在演示的过程中,我已经把ppid的方法加入里面了,这里我就不多介绍使用了

我们直接看刚刚到代码

为什么进程每次启动pid会变,但是ppid不会变呢???

我们先查看一下父进程是什么,输入该命令: ps -axj | head -1 && ps -axj | grep ppid 通过查看可以看到该进程是bash进程(命令行解释器),因此就很好理解了。

 

❍ 当我们运行一个进程时,命令行解释器会把这个指令解释成bash的子进程。

❍ 接着再由这个bash的子进程执行对应的命令。

❍ 即:每一条命令被执行,都属于bash的子进程,只是子进程不一样。 

为了更好看到执行程序与进程信息,可以使用shell脚本,隔一秒查一次进程

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

3️⃣ 创建进程

在类Unix操作系统中,进程的创建通常是通过fork()系统调用实现的。fork()调用会创建一个与父进程几乎完全相同的新进程,这个新进程称为子进程。

 

fork()的工作原理

fork()被调用时,以下步骤大致发生:

内核数据结构:操作系统在内核中为子进程创建新的进程描述符(process descriptor),这是一个数据结构,包含了进程的所有信息,如进程ID(PID)、父进程ID(PPID)、状态、资源使用情况等。

复制地址空间:子进程会获得父进程地址空间的一个副本,这包括代码段、数据段、堆栈等。这个复制是写时复制(copy-on-write)的,意味着只有在任一进程尝试写入这些数据时,物理内存才会实际被复制。在fork()调用后,子进程和父进程的地址空间是完全相同的。

执行流程fork()调用之后,子进程和父进程都从fork()调用点之后继续执行。在父进程中,fork()返回子进程的PID;在子进程中,fork()返回0;如果fork()调用失败,则在父进程中返回-1。

父进程与子进程的关系

代码和数据继承:子进程默认情况下继承了父进程的代码和数据。这是因为在fork()调用时,并没有实际复制这些数据,而是采用了写时复制的技术。

独立性:尽管子进程继承了父进程的资源,但它是一个独立的进程,拥有自己的PID和PPID。子进程可以执行与父进程不同的代码路径,也可以通过exec()系列函数加载并执行全新的程序。

资源分配:子进程会获得父进程打开的文件描述符、信号处理设置等资源的副本,但它们之间不会共享这些资源的状态(例如,文件偏移量是独立的)。

写时复制(Copy-on-Write)

写时复制是一种有效的资源管理技术,它可以减少不必要的内存复制操作。在fork()之后,父子进程共享相同的物理内存页面,直到任一进程尝试写入这些页面。这时,操作系统会为写入者分配新的页面,从而保持两个进程的独立性。

fork()的使用

示例1️⃣

#include <stdio.h>    
#include <unistd.h>    
#include <sys/types.h>    
int main()    
{    
    fork();//创建进程    
    printf("hello world,pid: %d,ppid: %d\n",getpid(),getppid());//查看进程对应信息  
}

示例2️⃣

#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
 
int main()
{
 
	printf("process is running,only me!,pid: %d\n", getpid());
	sleep(3);
	pid_t id = fork();
	if (id == -1) return -1; //进程创建错误直接退出
	else if (id == 0)
	{
		//child 子进程代码
		while (1)
		{
			printf("id: %d,I am child process,pid: %d,ppid: %d\n", id, getpid(), getppid());
			sleep(1);
		}
	}
	else
	{
		//parent 父进程代码
		while (1)
		{
			printf("id: %d,I am parent,pid: %d,ppid: %d\n", id, getpid(), getppid());
			sleep(2);
		}
	}
	return 0;
}

 结果:

示例3️⃣ 一次创建多个进程 

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
void RunChild()
{
 
	while (1)
	{
		printf("I am a child process,pid: %d,ppid: %d\n", getpid(), getppid());
		sleep(1);
	}
}
int main()
{
	const int num = 5;
 
	for (int i = 0; i < num; i++)
	{
		pid_t id = fork();
		if (id == 0)
		{
			RunChild();//运行子进程代码
		}
		sleep(1);
	}
 
	while (1)
	{
		sleep(1);
		printf("I am parent,pid: %d,ppid: %d\n", getpid(), getppid());
	}
	return 0;
}

结果:

[wuxu@Nanyi lesson12]$ vim myprocess.c
[wuxu@Nanyi lesson12]$ make
gcc -o myprocess myprocess.c -std=c99
[wuxu@Nanyi lesson12]$ ./myprocess
I am a child process,pid: 18490,ppid: 18489
I am a child process,pid: 18490,ppid: 18489
I am a child process,pid: 18491,ppid: 18489
I am a child process,pid: 18490,ppid: 18489
I am a child process,pid: 18491,ppid: 18489
I am a child process,pid: 18492,ppid: 18489
I am a child process,pid: 18490,ppid: 18489
I am a child process,pid: 18491,ppid: 18489
I am a child process,pid: 18492,ppid: 18489
I am a child process,pid: 18493,ppid: 18489
I am a child process,pid: 18490,ppid: 18489
I am a child process,pid: 18491,ppid: 18489
I am a child process,pid: 18492,ppid: 18489
I am a child process,pid: 18493,ppid: 18489
I am a child process,pid: 18495,ppid: 18489
I am a child process,pid: 18490,ppid: 18489
I am a child process,pid: 18491,ppid: 18489
I am a child process,pid: 18492,ppid: 18489
I am a child process,pid: 18493,ppid: 18489
I am a child process,pid: 18495,ppid: 18489
I am a child process,pid: 18490,ppid: 18489
I am a child process,pid: 18491,ppid: 18489
I am a child process,pid: 18492,ppid: 18489
I am parent,pid: 18489,ppid: 17725
I am a child process,pid: 18493,ppid: 18489
I am a child process,pid: 18495,ppid: 18489
I am a child process,pid: 18490,ppid: 18489
I am a child process,pid: 18491,ppid: 18489
I am a child process,pid: 18492,ppid: 18489
I am parent,pid: 18489,ppid: 17725
I am a child process,pid: 18495,ppid: 18489
I am a child process,pid: 18493,ppid: 18489
I am a child process,pid: 18490,ppid: 18489
I am a child process,pid: 18491,ppid: 18489
I am a child process,pid: 18492,ppid: 18489
I am parent,pid: 18489,ppid: 17725
I am a child process,pid: 18493,ppid: 18489
I am a child process,pid: 18495,ppid: 18489
I am a child process,pid: 18490,ppid: 18489
I am a child process,pid: 18491,ppid: 18489
I am a child process,pid: 18492,ppid: 18489
I am parent,pid: 18489,ppid: 17725
I am a child process,pid: 18495,ppid: 18489
I am a child process,pid: 18493,ppid: 18489
I am a child process,pid: 18490,ppid: 18489
I am a child process,pid: 18491,ppid: 18489
I am a child process,pid: 18492,ppid: 18489
I am parent,pid: 18489,ppid: 17725
I am a child process,pid: 18493,ppid: 18489
I am a child process,pid: 18495,ppid: 18489
^C

补充:

为了更好看到执行程序与进程信息,可以使用shell脚本,隔一秒查一次进程,且不查看grep进程信息。

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

为什么要创建子进程

创建子进程是操作系统和多任务环境中常见的做法,它服务于多种目的和场景。以下是一些主要原因:

1.并行和并发执行

并行处理:通过创建子进程,可以在多个处理器核心上并行执行任务,从而提高程序的执行速度。

并发执行:在单个处理器上,子进程可以让多个任务看似同时进行,即并发执行。

2.资源隔离

隔离环境:子进程可以运行在独立的地址空间中,这样即使子进程崩溃,也不会影响父进程或其他子进程。

权限限制:子进程可以以不同的用户权限运行,从而限制对系统资源的访问。

3.任务分解

负载分配:一个大任务可以被分解成多个子任务,每个子任务由一个子进程执行,这样可以更有效地利用系统资源。

模块化设计:程序可以通过创建子进程来分离不同的功能模块,使得程序结构更清晰,易于维护。

4.进程控制

父子关系:子进程可以由父进程创建、监控和终止,这为进程控制提供了方便。

信号传递:父进程可以发送信号给子进程,以便于通信和同步。

5.程序设计

执行外部程序:使用fork()exec()系统调用,一个程序可以启动另一个程序作为子进程执行。

程序扩展性:通过创建子进程,程序可以动态地根据需要增加或减少并行执行的实例。

6.实用场景

服务器模型:在许多服务器应用程序中,通常会为每个客户端连接创建一个子进程,以便并行处理多个请求。

后台任务:某些任务可以放在后台执行,不干扰前台的用户交互,例如日志处理、数据备份等。

管道和过滤器:在Unix命令行中,通过管道将一个命令的输出作为另一个命令的输入,通常涉及到子进程的创建。

7.错误处理和恢复

异常隔离:如果一个子进程遇到错误或异常,可以单独终止该子进程,而不影响其他进程。

容错机制:通过监控子进程的状态,可以实现故障检测和恢复机制。

创建子进程是操作系统中实现多任务、并行处理和资源管理的关键机制之一。然而,这也带来了一些挑战,如进程同步、通信和资源竞争等,需要在设计程序时予以考虑。

4️⃣ task_struct内容分类

❍ 标示符: 描述本进程的唯一标示符,用来区别其他进程。

​ ❍ 状态: 任务状态,退出代码,退出信号等。

​ ❍ 优先级: 相对于其他进程的优先级。

​ ❍ 程序计数器: 程序中即将被执行的下一条指令的地址。

​ ❍ 内存指针: 包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针 ​

❍ 上下文数据: 进程执行时处理器的寄存器中的数据[休学例子,要加图CPU,寄存器]。

​ ❍ I/O状态信息: 包括显示的I/O请求,分配给进程的I/ O设备和被进程使用的文件列表。

​ ❍ 记账信息: 可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等。

​ ❍ 其他信息 

5️⃣ 查看进程内容

ls /proc/pid -d # 按照目录查看

ls /proc/pid -l # 查看进程内容

测试内容

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
int main()
{
	while (1)
	{
		printf("I am process,pid: %d,ppid: %d\n", getpid(), getppid());
		sleep(1);
	}
	return 0;
}

1.如果我们在此处把可执行程序给删除,进程还会不会运行呢???

此处删除程序并不会造成影响,因为程序是加载到内存的,也就是内存拷贝了一份,这里是不会造成影响的,因为所占内存小,但如果内存过大运行就会出现问题

2.当前工作路径有什么用呢?

我们在C语言中学习文件操作,fopen("log.txt","w");默认是在当前目录创建文件,但是我们不一定每次都在当前目录创建文件,那怎么才能在其他目录下创建文件呢?

修改文件创建的路径,我们需要一个命令:chdir

 

#include<stdio.h>  
#include<unistd.h>  
   
int main()                                                                       
{  
  chdir("/home/wuxu");//更改工作目录为/home/wuxu  
  FILE* pf = fopen("log.txt", "w");//创建文件  
  (void)pf;// ignore system warning   
  fclose(pf);  
   
  while (1)  
  {  
    printf("I am a process,pid: %d\n", getpid());      
    sleep(1);  
  }  
  return 0;  
}  

 

因此我们对文件的理解不应该是在C语言上,而是应该在操作系统上 

  • 19
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值