Linux进程状态总结
进程生命周期在Linux内核里,无论是进程还是线程,统一使用 task_struct{} 结构体来表示,也就是统一抽象为任务(task)。task_struct{} 定义在 include/linux/sched.h 文件中,十分复杂,这里简单了解下。
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061// include/linux/sched.h// ... 省略struct task_struct {#ifdef CONFIG_THREAD_INFO_IN_TASK /* * For reasons of header soup (see current_thread_info()), this * must be the first element of task_struct. */ struct thread_info thread_info;#endif /* ...
一文带你,彻底了解,零拷贝Zero-Copy技术
1、数据拷贝基础过程在Linux系统内部缓存和内存容量都是有限的,更多的数据都是存储在磁盘中。对于Web服务器来说,经常需要从磁盘中读取数据到内存,然后再通过网卡传输给用户:
1.1 仅CPU方式
当应用程序需要读取磁盘数据时,调用read()从用户态陷入内核态,read()这个系统调用最终由CPU来完成;
CPU向磁盘发起I/O请求,磁盘收到之后开始准备数据;
磁盘将数据放到磁盘缓冲区之后,向CPU发起I/O中断,报告CPU数据已经Ready了;
CPU收到磁盘控制器的I/O中断之后,开始拷贝数据,完成之后read()返回,再从内核态切换到用户态;
1.2 CPU&DMA方式CPU的时间宝贵,让它做杂活就是浪费资源。
直接内存访问(Direct Memory Access),是一种硬件设备绕开CPU独立直接访问内存的机制。所以DMA在一定程度上解放了CPU,把之前CPU的杂活让硬件直接自己做了,提高了CPU效率。
目前支持DMA的硬件包括:网卡、声卡、显卡、磁盘控制器等。
有了DMA的参与之后的流程发生了一些变化:
最主要的变化是,CP ...
任务调度
一. 前言 在前文中,我们分析了内核中进程和线程的统一结构体task_struct,并分析进程、线程的创建和派生的过程。在本文中,我们会对任务间调度进行详细剖析,了解其原理和整个执行过程。由此,进程、线程部分的大体框架就算是介绍完了。本节主要分为三个部分:Linux内核中常见的调度策略,调度的基本结构体以及调度发生的整个流程。下面将详细展开说明。
二. 调度策略 Linux 作为一个多任务操作系统,将每个 CPU 的时间划分为很短的时间片,再通过调度器轮流分配给各个任务使用,因此造成多任务同时运行的错觉。为了维护 CPU 时间,Linux 通过事先定义的节拍率(内核中表示为 HZ),触发时间中断,并使用全局变量 Jiffies 记录了开机以来的节拍数。每发生一次时间中断,Jiffies 的值就加 1。节拍率 HZ 是内核的可配选项,可以设置为 100、250、1000 等。不同的系统可能设置不同数值,以通过查询 /boot/config 内核选项来查看它的配置值。
Linux的调度策略主要分为实时任务和普通任务。实时任务需求尽快返回结果,而普通任务则没有较高 ...
进程、线程的创建和派生
一. 前言 在前文中,我们分析了内核中进程和线程的统一结构体task_struct,本文将继续分析进程、线程的创建和派生的过程。首先介绍如何将一个程序编辑为执行文件最后成为进程执行,然后会介绍线程的执行,最后会分析如何通过已有的进程、线程实现多进程、多线程。因为进程和线程有诸多相似之处,也有一些不同之处,因此本文会对比进程和线程来加深理解和记忆。
二. 进程的创建以C语言为例,我们在Linux下编写C语言代码,然后通过gcc编译和链接生成可执行文件后直接执行即可完成一个进程的创建和工作。下面将详细展开介绍这个创建进程的过程。在 Linux 下面,二进制的程序也要有严格的格式,这个格式我们称为 ELF(Executable and Linkable Format,可执行与可链接格式)。这个格式可以根据编译的结果不同,分为不同的格式。主要包括
1、可重定位的对象文件(Relocatable file)
由汇编器汇编生成的 .o 文件
2、可执行的对象文件(Executable file)
可执行应用程序
3、可被共享的对象文件(Shared object file)
动态库文件,也即 . ...
进程的核心——task_truct
一. 前言在前文中,我们分析了内核启动的整个过程以及系统调用的过程,从本文开始我们会介绍Linux系统各个重要的组成部分。这一切就从进程和线程开始,在 Linux 里面,无论是进程,还是线程,到了内核里面,我们统一都叫任务(Task),由一个统一的结构 task_struct 进行管理。这个结构非常复杂,本文将细细分析task_struct结构。主要分析顺序会按照该架构体中的成员变量和函数的作用进行分类,主要包括:
任务ID
亲缘关系
任务状态
任务权限
运行统计
进程调度
信号处理
内存管理
文件与文件系统
内核栈
二. 详细介绍2.1 任务ID任务ID是任务的唯一标识,在tast_struct中,主要涉及以下几个ID
123pid_t pid;pid_t tgid;struct task_struct *group_leader;
之所以有pid(process id),tgid(thread group ID)以及group_leader,是因为线程和进程在内核中是统一管理,视为相同的任务(task)。
任何一个进程,如果只有主线程,那 pid 和tgid相同,group_ ...
进程间通信之信号
一. 前言 众所周知,System V IPC进程间通信机制体系中有着多种多样的进程间通信方式,如管道和有名管道,消息队列,信号,共享内存和信号量,套接字。从本文开始我们就逐个剖析进程间通信的机制和底层原理,就从信号开始讲起吧。
二. 信号基本知识 信号是进程处理紧急情况所用的一种方式,它没有特别复杂的数据结构,就是用一个代号一样的数字。Linux 提供了几十种信号,分别代表不同的意义。我们可以通过kill -l命令查看信号。
1234567891011121314# kill -l 1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP 6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR111) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM16) SIGSTKFLT 17) SIGCHLD ...
进程间通信之共享内存和信号量
一. 前言本文为进程间通信的最后一篇,介绍共享内存和信号量。之所以将二者一起叙述,是因为二者有着密不可分的关系。共享内存会利用虚拟内存和物理内存的映射关系,让不同进程开辟一块虚拟空间映射到相同的物理内存上,从而实现了两个进程对相同区域的读写,即进程间通信。而信号量则实现了互斥锁,可以为共享内存提供数据一致性的保证,因此二者常结合使用。
二. 基础知识共享内存的使用包括
调用shmget()创建共享内存
调用shmat()映射共享内存至进程虚拟空间
调用shmdt()接触映射关系
信号量有着类似的操作
调用semget()创建信号量集合。
调用semctl(),信号量往往代表某种资源的数量,如果用信号量做互斥,那往往将信号量设置为 1。
调用semop()修改信号量数目,即加锁和解锁之用
整体通信过程可用如下生产者消费者的模式图来理解。
三. 统一封装的接口消息队列、共享内存和信号量有着统一的封装和管理机制,为此我们提供了对应的名字空间和ipc_ids结构体。根据代码中的定义,第 0 项用于信号量,第 1 项用于消息队列,第 2 项用于共享内存,分别可以通过 sem_ids、m ...
进程间通信之管道
一. 前言上文中我们介绍了进程间通信的方法之一:信号,本文将继续介绍另一种进程间通信的方法,即管道。管道是Linux中使用shell经常用到的一个技术,本文将深入剖析管道的实现和运行逻辑。
二. 管道简介在Linux的日常使用中,我们常常会用到管道,如下所示
ps -ef | grep 关键字 | awk '{print $2}' | xargs kill -9
这里面的竖线|就是一个管道。它会将前一个命令的输出,作为后一个命令的输入。从管道的这个名称可以看出来,管道是一种单向传输数据的机制,它其实是一段缓存,里面的数据只能从一端写入,从另一端读出。如果想互相通信,我们需要创建两个管道才行。
管道分为两种类型,| 表示的管道称为匿名管道,意思就是这个类型的管道没有名字,用完了就销毁了。就像上面那个命令里面的一样,竖线代表的管道随着命令的执行自动创建、自动销毁。用户甚至都不知道自己在用管道这种技术,就已经解决了问题。另外一种类型是命名管道。这个类型的管道需要通过 mkfifo 命令显式地创建。
mkfifo hello
我们可以往管道里面写入东西。 ...
进程间通信,管道,socket,XSI(System V)
进程通信(IPC)分为PIPE(管道)、Socket(套接字)和XSI(System_V)。XSI又分为msg(消息队列)、sem(信号量数组)和shm(共享内存)。这些手段都是用于进程间通信的,只有进程间通讯才需要借助第三方机制,线程之间通讯是不需要借助第三方机制,因为线程之间的地址空间是共享的。线程之间可以通过互斥量,死锁,唤醒,信号等来进行通讯。
管道(PIPE->FIFO) 内核帮你创建和维护管道的特点:
管道是半双工的,也就是同一时间数据只能从一端流向另一段。就像水一样,两端水同时流入管道,那么数据就会乱
管道的两端一端作为读端,一端是写端
管道具有自适应的特点, 默认会适应速度比较慢的一方,管道被写满或读空时速度快的一方会自动阻塞
1234pipe - create pipe#include <unistd.h>int pipe(int pipefd[2]);// 也就只有两端,一端读,一端写
pipe用于创建管道,pipefd是一个数组,表示管道的两端文件描述符,pipefd[0]端作为读端,pipefd[1]作为写端。
pipe产生的是匿名管道,在 ...
Linux 常用查看系统信息命令
常用命令12345678910111213141516171819202122232425262728293031323334353637uname -a # 查看内核/操作系统/CPU信息head -n 1 /etc/issue # 查看操作系统版本 cat /proc/cpuinfo # 查看CPU信息 hostname # 查看计算机名 lspci -tv # 列出所有PCI设备 lsusb -tv # 列出所有USB设备 lsmod # 列出加载的内核模块 env # 查看环境变量资源 free -m # 查看内存使用量和交换区使用量 df -h # 查看各分区使用情况 du -sh <目录名> # 查看指定目录的大小 grep MemTotal /proc/meminfo # 查看内存总量 grep MemFree /proc/meminfo # 查看空闲内存量 uptime # 查看系统运行时间、用户数、负载 cat /proc/loadavg # 查看系统负载磁盘和分区 mount | column -t # 查看挂接的分区状态 fdisk -l # 查看所有 ...