Linux-进程简介

2023-11-03

内核代码版本:2.6.11.1

1、进程

进程是任何多道程序设计的操作系统中的基本概念。通常把进程定义为程序执行的一个实例。

在Linux源代码中,进程通常称为任务(task)或线程(thread)。Linux系统的线程实现非常特别:他对线程和进程并不特别区分。对于Linux而言,线程只不过是特殊的进程罢了。

当一个进程创建的时候,它几乎与父进程相同,它接受父进程地址空间的一个(逻辑)拷贝,并从进程创建系统调用的下一条指令开始执行与父进程相同的代码。尽管父子进程可以共享含有程序代码的页,但是它们有独立的数据拷贝(堆和栈),因此子进程对一个内存单元的修改对父进程是不可见得(反之亦然)。

2、进程描述符

为了管理进程,内核必须对每个进程所做的事情进行清楚的描述。此结构称之为进程描述符。在Linux系统中使用task_struct结构体表示。此结构定义在<linux/sched.h>中。
内核把进程的列表存放在任务队列(task list)的双向循环链表中。task_struct相对较大,在32位的机器上大约有1.7KB。

2.1、分配进程描述符

Linux通过slab分配器分配task_struct结构,这样能达到对象复用和缓存着色的目的。(预分配和重复使用task_struct可避免动态分配和释放所带来的的资源消耗)
在2.6以后的版本中使用slab分配器动态生成task_struct,所以只需要在栈底(对于向下增长的栈)或栈顶(向上增长的栈)创建一个新的结构struct thread_info。thread_info结构就保存了进程的基本信息,并有一个指针指向task_struct。

在X86上,struct thread_info在文件<include\asm-x86_64\thread_info.h>中定义如下:

struct thread_info {
	struct task_struct	*task;		/* main task structure */
	struct exec_domain	*exec_domain;	/* execution domain */
	__u32			flags;		/* low level flags */
	__u32			status;		/* thread synchronous flags */
	__u32			cpu;		/* current CPU */
	int 			preempt_count;

	mm_segment_t		addr_limit;	
	struct restart_block    restart_block;
};

下图中显示了在物理内存中存放两种数据结构的方式。线程描述符驻留与这个内存区的开始,而栈顶末端向下增长。

内核进程堆栈:
在这里插入图片描述
内核态的进程堆栈大小通常为8192个字节(两个页框),考虑到效率的因素,内核让这8K空间占据连续的两个页框并让第一个页框的起始地址是2^13的倍数。

C语言使用联合体表示一个进程的线程描述符和内核栈:

union thread_union {
	struct thread_info thread_info;
	unsigned long stack[THREAD_SIZE/sizeof(long)];
};

2.2、标识一个进程

内核通过一个唯一的进程标识值(process identification value)或PID来标识每一个进程,PID存放在进程描述符中的pid字段中。PID被顺序的编号,新创建的进程PID通常是前一个进程的PID加1。不过PID有一个上限,当内核使用的PID达到这个上限的时候就必须开始循环使用的已闲置的小PID号,在Linux中前1000个PID号是给系统用的,我们最好不要去用这些。在缺省的情况下,最大的PID号是32767。64位系统中,系统管理员可把PID的上限扩大到4194303。

路径:include\linux\threads.h

/*
 * This controls the default maximum pid allocated to a process
 */
#define PID_MAX_DEFAULT 0x8000   /*32758*/

/*
 * A maximum of 4 million PIDs should be enough for a while:
 */
#define PID_MAX_LIMIT (sizeof(long) > 4 ? 4*1024*1024 : PID_MAX_DEFAULT)  /*4194304*/

由于循环的使用PID编号,内核必须通过管理一个pidmap_array位图来表示当前已分配的PID号和闲置的PID号。因为一个页框包含32768个位,所以在32位体系结构中pidmap_array位图存放在一个单独的页中。然而64为系统体系结构中,当内核分配超过当前位图大小的PID号时,需要为PID位图增加更多的页。系统会一只保存这些页不被释放。
当然对于一般的桌面系统来说系统默认的PID号上限值是够用的,但在一些服务器中可能需要更多的进程,由于确实需要的话,可以通过修改/proc/sys/kernel/pid_max来提高 PID上限。

2.3、标识当前进程

从效率的观点来看,thread_info结构与内核态堆栈之间的紧密结合提供的主要好处是:内核很容易从esp寄存器的值获得当前在CPU上正在运行的进程的thread_info结构的地址。
事实上,如果thread_union结构长度是8K,则屏蔽掉esp的低13位有效位就可获得thread_info结构的基地址,如果thread_union结构长度是4K,内核需要屏蔽掉esp的低12位有效位就可获得thread_info结构的基地址。这项工作由current_thread_info()来完成(不同架构中有所差异),下面看下arm的current宏:

#define THREAD_SIZE		8192
#define current (get_current())

static inline struct task_struct *get_current(void)
{
	return current_thread_info()->task;
}

static inline struct thread_info *current_thread_info(void)
{
	register unsigned long sp asm ("sp");
	return (struct thread_info *)(sp & ~(THREAD_SIZE - 1));
}

进程最常用的就是进程描述符,而不是thread_info结构。为了获得当前在CPU上运行的进程描述符指针,我们从上面的代码可以看到,内核使用current宏,他的本质是current_thread_info()->task,可以看到在current_thread_info()函数中使用sp指针并屏蔽掉低13位,获取到thread_info的地址,然后通过current_thread_info()->task来获取进程描述符。
这里不同架构中会有所差异,比如说PowerPC,他们当前运行的进程的进程描述符是保存在一个寄存器中。只需要从寄存器中取出进程描述符的地址即可。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Linux-进程简介 的相关文章

  • 我们如何在使用循环时调用 ansible playbook 中的变量

    我有两个文件 其中这些文件包含server names and server IP s 我想更改 替换一些特定的server names and IP addressees根据要求在两个文件中 这与这篇文章 因为它被要求开设一个新职位 ht
  • 何时用引号将 shell 变量括起来?

    我应该或不应该在 shell 脚本中用引号括住变量吗 例如 下列说法正确的是 xdg open URL eq 2 or xdg open URL eq 2 如果是这样 为什么 一般规则 如果它可以为空或包含空格 或实际上任何空格 或特殊字符
  • 应用程序中两个不同版本的库

    考虑一个场景 其中有两个不同版本的共享库 考虑 A 1 so 链接到 B so A 2 so 链接到 C so 现在 B so 和 C so 都链接到 d exe 当 B so 想要调用 A 1 so 中的函数时 它最终会调用 A 2 so
  • 对于任何真实数据集,数据压缩比的最小可能值是多少

    我在写信ZLIB类似于嵌入式硬件压缩器的 API 它使用 deflate 算法来压缩给定的输入流 在进一步讨论之前 我想解释一下数据压缩率 数据压缩率定义为未压缩大小与压缩大小之间的比率 压缩比通常大于一 这意味着压缩数据通常比未压缩数据小
  • Vagrant 遇到问题 - “404 - 未找到”

    我正在尝试使用 Vagrant 制作一个 LAMP 盒子 有人告诉我它使用起来非常简单 我对网络和虚拟机完全陌生 对 Linux Ubuntu 的经验也很少 我目前已尝试按照官方文档页面上的教程进行操作 http docs vagrantu
  • 在 scapy 中通过物理环回发送数据包

    我最近发现了 Scapy 它看起来很棒 我正在尝试查看 NIC 上物理环回模块 存根上的简单流量 但是 Scapy sniff 没有给出任何结果 我正在做的发送数据包是 payload data 10 snf sniff filter ic
  • 使用 systemctl 获取 systemd 进程的正常运行时间或停机时间?

    喜欢使用systemctl is active
  • Bash:将字符串添加到文件末尾而不换行

    如何将字符串添加到文件末尾而不换行 例如 如果我使用 gt gt 它将添加到文件末尾并换行 cat list txt yourText1 root host 37 echo yourText2 gt gt list txt root hos
  • Ruby:在 Ubuntu 上安装 rmagick

    我正在尝试在 Ubuntu 10 04 上安装 RMagick 看起来here https stackoverflow com questions 1482823 is there an easy way to install rmagic
  • 链接错误:命令行中缺少 DSO

    我对 Linux 使用 Ubuntu 14 04 LTS 64 位 相当陌生 来自 Windows 并且正在尝试移植我现有的 CUDA 项目 当通过链接时 usr local cuda bin nvcc arch compute 30 co
  • 来自守护程序的错误响应:加入会话密钥环:创建会话密钥:超出磁盘配额

    我尝试在我的服务器上安装 docker 使用本教程 https docs docker com install linux docker ce ubuntu 我想远程运行 docker 镜像并使用 portainer Web 界面来管理一切
  • Linux 使用 boost asio 拒绝套接字绑定权限

    我在绑定套接字时遇到问题 并且以用户身份运行程序时权限被拒绝 这行代码会产生错误 acceptor new boost asio ip tcp acceptor io boost asio ip tcp endpoint boost asi
  • ssh 连接超时

    我无法在 git 中 ssh 到 github bitbucket 或 gitlab 我通常会收到以下错误消息 如何避免它 输出 ssh T email protected cdn cgi l email protection i ssh
  • ioctl 命令的用户权限检查

    我正在实现 char 驱动程序 Linux 并且我的驱动程序中有某些 IOCTL 命令仅需要由 ADMIN 执行 我的问题是如何在 ioctl 命令实现下检查用户权限并限制非特权用户访问 IOCTL 您可以使用bool capable in
  • 相当于Linux中的导入库

    在 Windows C 中 当您想要链接 DLL 时 您必须提供导入库 但是在 GNU 构建系统中 当您想要链接 so 文件 相当于 dll 时 您就不需要链接 为什么是这样 是否有等效的 Windows 导入库 注意 我不会谈论在 Win
  • FileOutputStream.close() 中的设备 ioctl 不合适

    我有一些代码可以使用以下命令将一些首选项保存到文件中FileOutputStream 这是我已经写了一千遍的标准代码 FileOutputStream out new FileOutputStream file try BufferedOu
  • ALSA:snd_pcm_writei 调用时缓冲区不足

    当运行我最近从灰烬中带回来的旧程序时 我遇到了缓冲区不足的情况 该程序将原始声音文件完全加载到内存中 2100 字节长 525 帧 并准备 ALSA 进行输出 44 1khz 2 通道 有符号 16 位 if err snd pcm set
  • linux下无法创建僵尸进程

    嗯 我有一个奇怪的问题 我无法在我的项目中创建僵尸进程 但我可以在其他文件中创建僵尸进程 有简单的说明 int main if fork 0 printf Some instructions n else sleep 10 wait 0 r
  • docker 非 root 绑定安装权限,WITH --userns-remap

    all 尝试让绑定安装权限正常工作 我的目标是在容器中绑定安装卷 以便 a 容器不以 root 用户身份运行入口点 二 docker daemon 配置了 userns remap 这样容器 主机上没有 root c 我可以绑定挂载和读 写
  • Google BQ:运行参数化查询,其中参数变量是 BQ 表目标

    我正在尝试从 Linux 命令行为 BQ 表目标运行 SQL 此 SQL 脚本将用于多个日期 客户端和 BQ 表目标 因此这需要在我的 BQ API 命令行调用中使用参数 标志 parameter 现在 我已经点击此链接来了解参数化查询 h

随机推荐