Linux初识进程
文章目录
- 进程的基本概念
- 描述进程-PCB
- task_ struct
- 查看进程
- 通过==系统调用==获取进程标示符
- 通过系统调用创建进程-fork初识
进程的基本概念
课本概念:程序的⼀个执⾏实例,正在执⾏的程序等
内核观点:担当分配系统资源(CPU时间,内存)的实体。
当前:进程=内核数据结构(task_struct)+⾃⼰的程序代码和数据
描述进程-PCB
写在前面:先描述再组织,是所有管理任务的核心。而操作系统管理进程的方式就是利用PCB对进程进行描述。
- 进程信息被放在⼀个叫做进程控制块的数据结构中,可以理解为进程属性的集合。
- 课本上称之为PCB(process control block),Linux 操作系统下的PCB 是: task_struct
- 在Linux 中描述进程的结构体叫做task_struct
- task_struct 是Linux 内核的⼀种数据结构类型,它会被装载到RAM(内存)⾥并且包含着进程的信息。
task_ struct
在任何操作系统中都存在PCB,而在Linux系统下,将PCB具体称为task_ struct,其包括以下内容:
- 标⽰符:描述本进程的唯⼀标⽰符,⽤来区别其他进程。
- 状态:任务状态,退出代码,退出信号等。
- 优先级:相对于其他进程的优先级。
- 程序计数器:程序中即将被执⾏的下⼀条指令的地址。
- 内存指针:包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针。
- 上下⽂数据:进程执⾏时处理器的寄存器中的数据[休学例⼦,要加图CPU,寄存器。
- I∕O状态信息:包括显⽰的I/O请求,分配给进程的I∕O设备和被进程使⽤的⽂件列表。
- 记账信息:可能包括处理器时间总和,使⽤的时钟数总和,时间限制,记账号等。
查看进程
查看进程的两种方式:
- 通过ps指令查询进程
ps ajx | grep processname
-
可以使用
分号
或者&&
连接两条语句,同时执行. -
grep 带-v选项,不搜索本身
-
通过
/proc
目录查看进程
ls /proc
- cwd表示当前当前的工作目录
- exe表示进程对应的可执行文件的目录
- 杀死进程
kill -9 xxxx
通过系统调用获取进程标示符
关于系统调用:操作系统对外会表现为⼀个整体,但是会暴露⾃⼰的部分接⼝,供上层开发使⽤,这部分由操作系统提供的接⼝,叫做系统调⽤。系统调⽤在使⽤上,功能⽐较基础,对⽤⼾的要求相对也⽐较⾼,所以,有⼼的开发者可以对部分系统调⽤进⾏适度封装,从⽽形成库,有了库,就很有利于更上层⽤⼾或者开发者进⾏⼆次开发。
简单来说:操作系统是不相信任何人的,但是为了向上层提供服务,不得不暴露一些接口供用户使用,来访问操作系统。这些接口叫做系统调用。
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{printf("pid: %d\n", getpid());printf("ppid: %d\n", getppid());return 0;
}
这里的getpid
getppid
就是系统调用,额…其实我觉得不如叫调用系统(的函数)。
通过系统调用创建进程-fork初识
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int gval = 100;int main()
{printf("父进程开始运行,pid: %d\n", getpid());pid_t id = fork();if(id < 0){perror("fork");return 1;}else if(id == 0){printf("我是一个子进程 !, 我的pid: %d, 我的父进程id: %d, gval: %d\n", getpid(), getppid(), gval);sleep(5);// childwhile(1){sleep(1);printf("子进程修改变量: %d->%d", gval, gval+10);gval+=10; // 修改printf("我是一个子进程 !, 我的pid: %d, 我的父进程id: %d\n", getpid(), getppid());}}else{//fatherwhile(1){sleep(1);printf("我是一个父进程 !, 我的pid: %d, 我的父进程id: %d, gval: %d\n", getpid(), getppid(), gval);}}printf("进程开始运行,pid: %d\n", getpid());
}
核心问题:
- 为什么
fork
给父子进程返回值不相同? - 为什么一个函数会返回两次?
- 为什么全局变量
gval
对于父子进程,值不相同?
问题1:因为父 : 子 = 1 :n,所以父进程要拿到子进程的id
。而对于子进程,返回0,让其知道自己是子进程即可。
问题2:因为fork
函数创建了新的进程,而父子进程共享代码和数据。
fork() 系统调用的作用是创建一个与当前进程(父进程)几乎完全相同的新进程(子进程)。
子进程会复制父进程的地址空间(代码、数据、堆、栈等)、文件描述符、信号处理方式等。
从 fork() 调用之后开始,父子进程会各自独立执行后续的代码(共享同一份代码,但拥有独立的执行流)。
问题3:因为采用写时拷贝机制。
fork() 创建子进程时,系统采用写时拷贝机制:初始时,子进程共享父进程的地址空间(包括 gval),但这是只读的。当子进程尝试修改 gval 时,系统会为子进程复制一份 gval 的副本,此后父子进程的 gval 各自独立,修改互不干扰。这体现了进程独立性:每个进程有自己独立的地址空间,不受其他进程影响。
完