FireFox常用设置

About

about:config

  • ntl.charset.fallback.utf8_for_file:是否默认以 utf-8 编码打开文件
    • 避免纯文本文件打开乱码

Linux 系统启动

登陆、退出、关机、重启

login

登陆系统

logout

退出shell

exit

退出shell(常用)

rlogin

远程登陆服务器

poweroff

关闭系统,并将关闭记录写入/var/log/wtmp日志文件

ctrlaltdel

强制或安全重启服务器

shutdown

关闭系统

halt

关闭系统

reboot

重启系统

init 0/6

关机/重启

Shell 环境变量

环境

export

显示、设置环境变量,使其可在shell子系统中使用

  • 设置环境变量直接$ ENV=value即可,但是此环境变量 不能在子shell中使用,只有$ export ENV导出后才可
  • -f:变量名称为函数名称
  • -n:取消导出变量,原shell仍可用
  • -p:列出所有shell赋予的环境变量

系统环境变量

NAME

  • PATH:用户命令查找目录
  • HOME:用户主工作目录
  • SHELL:用户使用shell
  • LOGNAME:用户登录名
  • LANG/LANGUAGE:语言设置
  • MAIL:用户邮件存储目录
  • PS1:命令基本提示符
  • PS2:命令附属提示符
  • HISTSIZE:保存历史命令记录条数
  • HOSTNAME:主机名称
  • /etc/passwd/etc/hostname等文件中设置各用户部分 默认值,缺省随系统改变

PATH

C PATH

  • LIBRARY_PATH:程序编译时,动态链接库查找路径
  • LD_LIBRARAY_PATH:程序加载/运行时,动态链接库查找路径
  • 动态链接库寻找由/lib/ld.so实现,缺省包含/usr/lib/usr/lib64
    • 建议使用/etc/ld.so.conf配置替代LD_LIBRARY_PATH, 或在编译时使用-R<path>指定
    • 手动添加动态链接库至/lib/usr/lib等中时,可能 需要调用ldconfig生成cache,否则无法找到

Shell 常用工具

获取命令系统帮助

help

重看内部shell命令帮助信息(常用)

man

显示在线帮助手册(常用)

info

info格式的帮助文档

打印、日期、时间

echo

输出至标准输出

env

打印当前所有环境变量

cal

显示日历信息

date

date:显示、设置系统日期时间

1
date -d <time> "+<format>"
  • -d:指定时间,缺省今天
  • +:指定输出格式
    • %Y-%m-%d %h-%M-%S:年月日时(24时)分秒
    • %a/%A:星期缩写、完整
    • %b/%B:月份缩写、完整
    • %DMM/DD/YY
    • %FYYYY-MM-DD

hwclock

查看、设置硬件时钟

clockdiff

主机直接测量时钟差

rdate

通过网络获取时间

sleep

暂停指定时间

数值计算

bc

任意精度计算器

expr

将表达式值打印到标准输出,注意转义

Shell 本地化

本地化

字体

fc-

  • fc-list:列出系统已安装字体

    1
    2
    # 仅展示中文字体
    $ fc-list :lang=zh
  • fc-cache:创建字体信息缓存文件

  • mkfontdir/mkfontscale:创建字体文件索引

  • 字体安装

    • 将字体文件复制至字体文件夹
      • $HOME/.fonts
      • /usr/share/fonts
    • fc-cache 更新缓存信息

文件、目录

显示文本文件

cat

  • cat:显示文本文件内容
  • zcat:查看压缩文件内容

more

  • more:单向分页显示文本文件
  • zmore:单向分页显式压缩文本文件

less

  • less:双向分页显示文本文件内容
  • zless:双向分页显式压缩文件内容

head:显示文件指定前若干行

tail

tail:实现文件指定后若干行

nl

nl:显示文件行号、内容

文件处理

sort

对文件中数据排序

uniq

删除文件中重复行

cut

从文件的每行中输出之一的字节、字符、字段

diff

逐行比较两个文本文件

diff3

逐行比较三个文件

cmp

按字节比较两个文件

tr

从标准输入中替换、缩减、删除字符

split

将输入文件分割成固定大小的块

tee

将标准输入复制到指定温婉

expand

将文件中tab转换为空格输出到标准输出

1
$ expand -n 4 file_name

nano

awk

一门模式匹配的编程语言

  • 主要功能是匹配文本并处理
  • 同时还有一些编程语言才有的语法:函数、分支循环语句、变量 等等
  • 使用awk可以
    • 将文本文件视为字段、记录组成的文本数据库
    • 操作文本数据库时能够使用变量
    • 能够使用数学运算和字符串操作
    • 能够使用常见地编程结构,如:条件、分支循环
    • 能够格式化输出
    • 能够自定以函数
    • 能够在awk脚本中执行linux命令
    • 能够处理linux命令的输出结果

命令行语法

1
2
$ awk [-F ERE] [-v assignment] ... program [argument...]
$ awk [-F ERE] -f progfile ... [-v assignment] ... [argument ...]

sed

sed:非交互式、面向字符流的编辑器

  • sed也是默认从stdin读取输入、输出至stdout,除非 参数filename被指定,会从指定文件获取输入,但是注意 sed是面向字符流的编辑器,所以输入、输出文件不能是同一个

  • sed按行处理文本数据,每次处理一行在行尾添加换行符

1
$ sed [-hnV] [-e<script>][-f<script-file>][infile]

参数

  • -e<script>/--expression=<script>:以指定script 处理infile(默认参数)

    • 默认不带参数即为-e
  • -f<script-file>/--file=<script-file>:以指定的script 文件处理输入文本文件

    • 文件内容为sed的动作
  • -i:直接修改原文件

  • -n/--quiet:仅显示script处理后结果

  • -h/--help:帮助

  • -V/--version:版本信息

动作

  • [n]a\string:行添加,在n行后添加新行string
  • [n]i\string:行插入
  • [n]c\string:行替换
  • [n,m]d:删除,删除n-m
  • [start[,end]]p:打印数据
  • [start[,end]]s/expr/ctt[/g]:正则替换

高级语法

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ sed '2anewline' ka.file
$ sed '2a newline' ka.file
$ sed 2anewline ka.file
$ sed 2a newline ka.file
# 在第2行添加新行`newline`

$ sed 2,$d ka.file
# 删除2至最后行

$ sed 2s/old/new ka.file
# 替换第2行的`old`为`new`

$ nl ka.file | sed 7,9p
# 打印7-9行

$ sed ":a;N;s/\n//g;ta" a.txt
# 替换换行符

查找字符串、文件

grep

查找符合条件的字符串

egrep

在每个文件或标准输入中查找模式

find

列出文件系统内符合条件的文件

whereis

插卡指定文件、命令、手册页位置

whatis

在whatis数据库中搜索特定命令

which

显示可执行命令路径

type

输出命令信息

  • 可以用于判断命令是否为内置命令

CentOS7 常用配置

网络配置

编辑/etc/sysconfig/network-scripts/ifcfg-ens33

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
TYPE = Ethernet				# 网卡类型:以太网
PROXY_METHOD=none # 代理方式:无
BROWSER_ONLY=no # 仅浏览器:否
BOOTPROTO=dhcp # 网卡引导协议
DEFROUTE=yes # 默认路由:是
IPV4_FAILURE_FATAL=no # 开启IPV4致命错误检测:否
IPV6INIT=yes # IPV6自动初始化:是
IPV6_AUTOCONF=yes # IPV6自动配置:是
IPV6_DEFROUTE=yes # IPV6是否可为默认路由:是
IPV6_FAILURE_FATAL=no # 开启IPV6致命错误检测:否
IPV6_ADDR_GEN_MODE=stable-privacy
# IPV6地地址生成模型:stable-privacy
NAME=ens33 # 网卡物理设备名称
UUID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
# 通用唯一识别码
DEVICE=ens33 # 网卡设备名称
ONBOOT=yes # 开启启动:是
DNS1=xxx.xxx.xxx.xxx # DNS地址
IPADDR=xxx.xxx.xxx.xxx # IP地址
PREFIX=24 # 子网掩码
GATEWAY=xxx.xxx.xxx.xxx # 网关
  • UUID不能相同相同
  • ifcfg-ens33这个文件感觉像是个模板,但是不知道真正应用 配置文件在哪

常用应用源

EPEL

Extra Packages for Enterprise Linux 由Fedora社区创建、维护的RPM仓库,通常不会与官方源发生冲突 或相互替换文件,包括应用有:chromium

直接使用yum安装:$ sudo yum install epel-release

RPMFusion

提供Fedora和RedHat由于开源协议或者是禁止商业用途而无法提供 RPM安装包,包括两个仓库 和NuxDextop源有冲突,如:gstreamer,感觉上比NuxDextop更加权威 包含应用:mplayer、gstreamer-pluginsXXXX、

  • free:开源软件但是由于其他原因无法提供,安装方式 $>sudo yum localinstall --nogpgcheck https://download1.rpmfusion.org/free/el/rpmfusion-free-release-7.noarch.rpm

  • nonfree:闭源软件,包括不能用于商业用途,安装方式 $>sudo rpm -ivh https://download1.rpmfusion.org/nonfree/el/rpmfusion-nonfree-release-7.noarch.rpm

ELRepo

包含和硬件相关的驱动程序,通过以下命令安装

$>rpm --import https://www.elrepo.org/RPM-GPG-KEY-elrepo.org
$>rpm -Uvh http://www.elrepo.org/elrepo.org/elrepo-release-7.0-3.el7.elrepo.noarch.rpm

NuxDextop

包含与多媒体相关应用的RPM仓库,好像是个人作者维护,有的依赖 可能在EPEL源中,因此可能需要先安装EPEL,可能和其他源 (RPMFusion)有冲突,可以设置默认情况下不启用,即修改 /etc/yum.repos.d/nux.dextop.repo文件,设置enable=0, 开启时手动启用 $>yum --enablerepo=nux-dextop install PACKAGENAME

$>rpm -Uvh http://li.nux.ro/download/nux/dextop/el7/x86_64/nux-dextop-release-0-5.el7.nux.noarch.rpm

装机必备

rhytmbox-mp3

centos7的gnome默认安装rhythmbox,但无法解码mp3,需要安装 rpmfusion-free源中的

  • gstreamer-plugins-ugly.x86_64
  • gstreamer1-plugins-ugly.x86_64

chromium

  • 安装EPEL源之后直接安装

  • flash插件

    ppapi好像就是pepperapi的简称,但是两个flash插件不一样, 安装的是pkgs上下载的,fedora社区维护的

  • html5视频播放支持:ffmpeg-libs google准备不再支持h.264格式(绝大部分)的视频,所以装 了这个还需要其他设置,但firefox可播放大部分html5视频

wqy中文字体

yum源里的字体文件都是*.ttc文件,需要*ttf字体文件,有 在线解压网站可以解压

安装包常识

  • app和app-devel/app-dev:后者包括头文件、链接库,在编译 使用了app的源代码才需要

系统配置

文件目录常识

  • /usr/share/applications里*.desktop是“桌面图标”文件, centos会 菜单中的会展示的“应用”就是这些

Linux 安装后常用配置

用户设置

设置 root 密码

  • Linux 安装之初,在设置 root 密码之前无法使用 $ su 切换到 root 用户,需要先设置root用户密码

    1
    $ sudo passwd root

应用设置

Debian

配置文件

  • Debian 源配置文件:/etc/apt/source.list
  • 修改完成后运行 $ sudo apt update 更新索引
  • 163 源:https://mirrors.163.com/.help/debian.html

    1
    2
    3
    4
    5
    6
    7
    8
    deb http://mirrors.163.com/debian/ <VERSION> main non-free contrib
    deb http://mirrors.163.com/debian/ <VERSION>-updates main non-free contrib
    deb http://mirrors.163.com/debian/ <VERSION>-backports main non-free contrib
    deb-src http://mirrors.163.com/debian/ <VERSION> main non-free contrib
    deb-src http://mirrors.163.com/debian/ <VERSION>-updates main non-free contrib
    deb-src http://mirrors.163.com/debian/ <VERSION>-backports main non-free contrib
    deb http://mirrors.163.com/debian-security/ <VERSION>/updates main non-free contrib
    deb-src http://mirrors.163.com/debian-security/ <VERSION>/updates main non-free contrib
  • USTC 源:https://mirrors.ustc.edu.cn/help/debian.html

    1
    2
    3
    4
    5
    6
    7
    8
    deb http://mirrors.ustc.edu.cn/debian/ <VERSION> main contrib non-free
    deb-src http://mirrors.ustc.edu.cn/debian/ <VERSION> main contrib non-free
    deb http://mirrors.ustc.edu.cn/debian/ <VERSION>-updates main contrib non-free
    deb-src http://mirrors.ustc.edu.cn/debian/ <VERSION>-updates main contrib non-free
    deb http://mirrors.ustc.edu.cn/debian/ <VERSION>-backports main contrib non-free
    deb-src http://mirrors.ustc.edu.cn/debian/ <VERSION>-backports main contrib non-free
    deb http://mirrors.ustc.edu.cn/debian-security/ <VERSION>/updates main contrib non-free
    deb-src http://mirrors.ustc.edu.cn/debian-security/ <VERSION>/updates main contrib non-free
  • debian 的版本名,根据版本改变
  • 一般的,直接将默认配置文件中 http://deb.debian.org 修改为相应源地址即可:$ sudo sed -i 's/deb.debian.org/<mirror_addr>/g' /etc/apt/sources.list

openSUSE

  • openSUSE 使用 MirrorBrain 技术,中央服务器会按照 IP 中转下载请求到附近的镜像,所以更改软件源通常只会加快刷新软件元的速度,对下载速度影响不大

命令行

  • USTC 源:https://mirrors.ustc.edu.cn/help/opensuse.html

    1
    2
    3
    4
    5
    6
    7
    8
    # 禁用原有软件源
    $ sudo zypper mr -da
    $ sudo zypper ar -fcg https://mirrors.ustc.edu.cn/opensuse/distribution/leap/\$releasever/repo/oss USTC:OSS
    $ sudo zypper ar -fcg https://mirrors.ustc.edu.cn/opensuse/distribution/leap/\$releasever/repo/non-oss USTC:NON-OSS
    $ sudo zypper ar -fcg https://mirrors.ustc.edu.cn/opensuse/update/leap/\$releasever/oss USTC:UPDATE-OSS
    $ sudo zypper ar -fcg https://mirrors.ustc.edu.cn/opensuse/update/leap/\$releasever/non-oss USTC:UPDATE-NON-OSS
    # 15.3 或更高版本需要
    $ sudo zypper ar -fgc https://mirrors.ustc.edu.cn/opensuse/update/leap/\$releasever/sle USTC:UPDATE-SLE
  • $releaseverOpenSuSe leap 版本,若知晓可以自行替换

配置文件

  • openSUSE 源配置文件夹:/etc/zypp/repo.d
  • 配置文件格式

    1
    2
    3
    4
    5
    [<ALIAS>]			# 源别名
    enabled=1 # 默认是否启用
    autorefresh=0
    baseurl=url # 源地址
    type=rpm-md

CentOS

  • 发行版中 yum 一般自带 fast-mirrors 插件,一般无需更新官方源

三方源配置

  • Extra Packages for Enterprise Linux:由 Fedora 社区创建、维护的 RPM 仓库,通常不会与官方源发生冲突或相互替换文件

    • 安装 EPEL$ sudo yum install epel-release
    • 包括应用有
      • Chromium
  • RPMFusion:提供 FedoraRedHat 由于开源协议或者是禁止商业用途而无法提供 RPM 安装包

    • 包括两个仓库freenofree
      • free:开源软件但是由于其他原因无法提供
      • non-free:闭源软件,包括不能用于商业用途
    • 包含应用有
      • mplayer
      • gstreamer-pluginsXXXX
  • ELRepo:包含和硬件相关的驱动程序

    • 安装
      1
      2
      $ rpm --import https://www.elrepo.org/RPM-GPG-KEY-elrepo.org
      $ rpm -Uvh http://www.elrepo.org/elrepo.org/elrepo-release-7.0-3.el7.elrepo.noarch.rpm
  • NuxDextop:包含与多媒体相关应用的 RPM 仓库

    • 安装
      1
      $ rpm -Uvh http://li.nux.ro/download/nux/dextop/el7/x86_64/nux-dextop-release-0-5.el7.nux.noarch.rpm
    • 说明
      • 有的依赖在 EPEL 中,因此可能需要先安装 EPEL
      • 和其他源有(RPMFusion)冲突时
        • 可以设置默认情况下不启用,即修改 /etc/yum.repos.d/nux.dextop.repo 文件,设置 enable=0
        • 需要时手动启用:$ yum --enablerepo=nux-dextop install <PACKAGENAME>

应用安装方式

  • 包管理器

    • 安装应用比较方便
    • 但某些发行版中应用源更新缓慢
  • 自行下载二进制版本安装

    • Linux 大部分应用是 noarch,即与架构无关,无需考虑兼容问题
  • 下载源码编译安装

    • 安装流程
      • 查询文档安装编译依赖
      • ./configure配置编译选择,如:安装路径等
      • make & make install
  • 注意事项

    • 自行安装应用可以若设置安装路径不是推荐路径,记得检查环境变量 XXXX_HOME
    • 应用文件夹通常带有版本号,建议
      • 保留文件夹版本号
      • 另行创建无版本号符号链接指向所需版本文件夹

本地化

字体

  • 终端中字体需要为 monospace
    • 在多语言环境下,非 monospace 字体字符宽度不同,导致字符重叠
    • 字体名称不是字体文件名,其定义在字体文件内部定义
      • 指定未安装字体只能通过文件名
      • 指定已安装字体可直接使用字体名称

Locale

Locale:特定于某个国家、地区的编码设定

  • 代码页
  • 数字、货币、时间与日期格式

Linux文件系统设计

文件系统

磁盘存储

  • Sector 扇区:扇区概念来源于机械硬盘,指磁盘上各磁道上的扇环

    • 物理扇区:磁盘读写的最小单位
      • 早期硬盘多为 512B 大小,新式硬盘多为 4096B 或更高以提高数据记录密度
    • 逻辑扇区:硬盘可以接受读写指令的最小操作单元
      • 为兼容性而设计,硬盘内部将物理扇区逻辑上划分多个 512B 扇区片段提供给文件系统
      • 实际读写时由硬盘固件负责逻辑扇区、物理扇区间转换,对文件系统透明
  • Logical Block Address (逻辑)块/簇:多个扇区组成的操作系统最小读写单位

    • 常见的块大小为 4KB,一般为扇区 $2^N$ 倍
    • 特点
      • 提高了读写效率
      • 减少文件碎片
      • 造成一定空间浪费
  • 分区:从磁盘上划分出了的连续的扇区

    • 分区格式化:对分区范围内扇区使用进行规划
      • 引导分区设置
      • 扇区分组为块、编号
    • 分区对齐:将逻辑块对齐到磁盘物理扇区

      • 分区格式化是按照逻辑扇区划分,若分区中存储起始位置没有对齐至物理扇区边缘,则分区中块也无法对齐到物理扇区边缘
        • 分区未对齐更多是因为引导区占用扇区数不是物理扇区整数倍,导致之后存储区域无法对齐
        • 则分区一般是将分区起始位置对齐到物理扇区整数倍处即可
      • 分区未对齐时,操作系统每次读、写会多读、写一个物理扇区,降低磁盘性能
      • 4K 对齐:多数磁盘物理扇区大小为 4K,实际对齐需检查物理扇区大小

      disk_partition_alignment_unaligned disk_partition_alignment_aligned

  • 固态存储同样有扇区概念,但还有页等概念,更加复杂
  • 磁盘存储区域
    • 超级块:存储文件系统的详细信息,文件系统挂载时载入内存
      • 块数量
      • 块大小
      • 空闲块
    • 索引节点区:存储索引节点,文件被访问时载入内存
    • 数据块区:存储文件、目录数据

文件

  • Linux 系统中:一切皆文件
    • 普通的文件
    • 目录(不是目录项)
      • 同样用 Inode 唯一标识
      • 其中存储的内容是子目录、文件
    • 块设备
    • 管道
    • Socket

linux_file_system_structure

文件读写

linux_file_usage

  • 操作系统会跟踪进程打开的所有文件,即为每个进程维护一个打开文件表

    • 文件表中每项代表文件描述符(open 函数返回)
      • 即文件描述符是打开文件的标识
    • 文件表中维护打开文件状态、信息
      • 文件指针:系统跟踪上次读写位置作为当前文件位置指针,对某进程唯一
      • 文件打开计数器:跟踪文件打开、关闭数量,仅计数为 0 时关闭文件,删除条目
      • 文件磁盘位置:避免磁盘交互
      • 访问权限(访问模式):方便操作系统允许、拒绝 I/O 请求
  • 文件系统屏蔽了用户字节操作和磁盘数据块操作之间差异

    • 用户读取 1byte 数据时,文件系统获取所在块,返回所需部分
    • 用户写入 1byte 数据时,文件系统获取应写入的块,修改后写回

文件在磁盘中的存储

  • 连续空间存储方式:文件存储在磁盘连续的物理空间中

    • 需预先知道待存储的文件大小:Inode 中需包含文件起始块位置、长度
    • 特点
      • 读写效率高
      • 磁盘空间碎片化
      • 文件长度不易扩展
  • 非连续空间存放方式:链表方式

    • 隐式链表:文件头中包含首块、尾块位置,各数据块中包含指向下个数据块的指针
      • 无法直接访问数据块:只能通过指针顺序访问
      • 稳定性差:任何指针丢失、损坏都会导致文件数据损坏
    • 显式链表:将链接各数据块的指针显式存储在链接表(File Allocation Table)中(改进隐式链表问题)

      linux_file_system_storage_explicit_link

      • 链接表为每个磁盘设置一张,其中每项表示一个数据块,内容为下个数据块指针
      • 链接表可存放在内存中以提高检索速度,但会占用内存空间,也因此不适合大磁盘
      • 长期使用会使文件资料逐渐分散
    • 特点
      • 可消除磁盘碎片
      • 文件长度可动态扩展
      • 存储指针的有额外空间开销
  • 索引存储方式:为每个文件创建索引数据块,存储指向文件数据块的指针列表

    linux_file_system_storage_index

    • 文件头中需包含指向索引数据块的指针
    • 特点

      • 文件创建、增大、缩小方便
      • 无磁盘碎片问题
      • 支持顺序读写、随机读写
      • 存储索引有额外空间开销
      • 文件系统效率与查找策略高度相关
    • 大文件支持扩展

      • 链式索引块:链表链接索引

        linux_file_system_storage_link_index

      • 多级索引块:索引索引索引

        linux_file_system_storage_index_index

文件系统

文件系统:操作系统用于明确存储设备或分区上文件的方法和数据结构

  • 文件系统即在存储设备上组织文件(持久数据)的子系统

    • 基本数据单位是文件,对文件的组织方式的不同即形成不同的文件系统
    • 存储设备:磁盘、光盘、网络存储、虚拟数据
      • 存储设备可以包含多个文件系统
  • Virtual File System 虚拟文件系统:包含一组所有文件系统都支持的数据结构和标准接口

    • 作为提供给用户的统一接口

    linux_virtual_file_system_structure

  • 操作系统中负责管理、存储文件信息的软件称为文件管理系统,简称文件系统

文件系统分类

  • 磁盘文件系统:数据存储在磁盘中

    • EXT2
      • Linux 的正宗文件系统,早期常用
      • 支持 undelete,误删文件可恢复,但是操作比较麻烦
    • EXT3
      • EXT2 发展而来
      • 支持大文件
      • 不支持反删除,RedhatFedora 推荐
    • ReiserFS
      • 支持大文件
      • 支持反删除,操作简单
  • 内存文件系统:数据存储在内存中

    • 如:/proc/sys 等文件系统
    • 读写此类文件实际上是读写内核中相关数据
  • 网络文件系统:访问其他计算机主机数据的文件系统

    • NFS
    • SMB
FileSystem File Size Limit Filesystem Size Limit
ext2/ext3 with 1KB blocksize 16448MB 2048GB
ext2/ext3 with 2KB blocksize 256GB 8192GB
ext2/ext3 with 4KB blocksize 2048GB 8192GB
ext2/ext3 with 8KB blocksize 65568GB 32TB
ReiserFS3.5 2GB 16TB
ReiserFS3.6 1EB 16TB
XFS 8EB 8EB
JFS with 512B blocksize 8EB 512TB
JFS with 4KB blocksize 8EB 4PB
NFSv2(client side) 2GB 8EB
NFSv3(client side) 8EB 8EB
  • 文件系统要先挂在在某个目录上才能正常使用,Linux 会把文件系统挂载到根目录

File Allocation Table

  • FAT 文件分配表:采用 FAT 显式链表组织文件的文件系统

    • 简单,几乎所有的个人操作系统均支持,适合移动介质的文件系统
    • FAT12:12bits 寻址长度
    • FAT16:16bits 寻址长度
    • FAT32:目前只使用了其中 28bits 寻址长度
      • 按 4KB 块计算,FAT32 最大容量为 1TB
      • FAT32 起始扇区中记录有扇区总数,此限制 FAT32 最大容量 2TB
      • FAT32 最大支持 4GB 大小文件
  • Extended File Allocation Table

    • 优点
      • 允许更大分区容量、更大文件、更大逻辑块大小
      • 采用空余空间寻址,空间分配、删除性能得以改进
    • 缺点
      • 兼容性不如 FAT32:旧设备、UEFI 不支持

权限设计

  • r:读文件
  • w:修改、删除文件
  • x:可以执行文件
  • s:强制位权限(固化用户/组权限)
    • set-user-id:user执行权限位出现
    • set-group-id:group执行权限位出现
  • t:粘滞位权限(在swap中停留)

权限判断规则

  • linux中权限是根据user-idgroup-id判断用户和资源 关系,然后选择相应的类型用户(user、group、other)权限组 判断是否有相应权限

  • 需要注意的是,访问资源实际上不是用户,而是用户开启的 进程,所以这里涉及了4中不同的用户标识

    • real-user-id:UID,用户id

    • real-group-id:GID,用户默认组id

    • effective-user-id:是针对进程(可执行文件)而言, 指内核真正用于判断进程权限的user-id

    • effective-group-id:同effective-user-id,内核 判断真正判断进程权限的group-id

  • 一般情况下effective-user-id就是read-user-id,即启动 进程用户的UID,所以一般来说用户创建的进程的对资源访问 权限就是就是自身权限

可执行文件权限

  • r:读文件
  • w:写文件
  • x:执行文件

s权限

当可执行文件具有set-user-id权限时

  • 其他用户执行该文件启动的进程的effective-user-id不再是 real-user-id,即和执行用户的UID不再一致,而是用户属主 的UID

  • 内核根据进程effective-user-id判断进程权限,进程的权限 实际上同属主的权限,而不是执行用户权限

  • 这样用户就在执行这种可执行文件,暂时拥有该可执行文件属主 执行该可执行文件权限,否则可能由于进程访问其他资源原因 无法正常执行

  • 可看作是将属主的部分权限(在该文件上涉及到的权限) 固化在文件上

set-group-id类似的可以看作是将属主默认组权限固化在文件上

t权限

  • 文件被执行时,文本段会被加载到swap中,程序结束后仍然 保留在swap中

  • 下次执行文件时,文本段直接从swap中加载,因为swap为连续 block,加载速度会提高

目录权限说明

linux中目录是一种特殊的文件,其包含目录下所有文件(包括 子目录)的文件名、i-node号

  • r:列出目录下所有文件

  • w:增加、删除、重命名目录下的文件

  • x:可以是(搜索)路径的一部分

    • 即必须对要访问的文件中路径中所有目录都有执行权限
    • 可以将目录执行权限看作是过境证明
  • s:好像没啥用

  • t:用户只能增加、删除、重命名目录下属于自己文件

    • w权限补充,否则用户拥有目录w权限则可以操作目录 下所有文件
    • /home目录权限就是1777,设置有粘滞位权限

权限掩码

文件/目录默认权限 = 现有权限(0777减去权限掩码

  • 权限掩码设置参见linux/shell/cmd_fslinux/shell/config_file

进程、线程、作业

Linux进程、线程

进程发展

  • Linux2.2内核

    • 进程通过系统调用fork()创建,新进程是原进程子进程
    • 不存在真正意义上的线程
    • 只默认允许4096个进程/线程同时运行
  • Linux2.4内核

    • 运行系统运行中动态调整进程数上限,进程数仅受制于物理 内存大小,最大16000
  • Linux2.6内核

    • 进程调度重新编写,引入slab分配器动态生成 task_struct
    • 最大进程数量提升至10亿
    • 线程框架重写
      • 引入tgid、线程组、线程各自的本地存储区
      • 得以支持NPTL线程库

线程/轻量级进程

  • Linux未真正实现、区分线程,在内核层面是特殊进程,是 “真正的轻量级进程”

    • “线程”和“进程”共享
      • 相同调度策略,处于同一调度层次
      • 相同数据结构进程标识符,位于相同进程标识符空间
    • “线程”与“进程”的区别在于
      • 线程没有独立的存储空间
  • 多线程即创建多个进程并分配相应的进程描述符 task_struct、指定其共享某些资源

    • 创建线程不会复制某些内存空间,较进程创建快
    • 在专门线程支持系统多线程中,系统会创建包含指向所有 线程的进程描述符,各线程再描述独占资源
  • 尽管Linux支持轻量级进程,但不能说其支持核心级线程

    • 则不可能在Linux上实现完全意义上的POSIX线程机制
    • 所以Linux线程库只能尽可能实现POSIX绝大部分语义,尽量 逼近功能
  • 线程在进程内共享某些资源

    • 打开的文件
    • 文件系统信息
    • 地址空间
    • 信号处理函数
  • 这里讨论的线程都是内核线程,即内核可感知、调度线程,不 包括程序自建线程

内核守护线程

kthreads pthreads
资源 无用户空间 共享完整虚拟寻址空间
状态 只工作在内核态 可在内核态、用户态之间切换
目的 维护内核正常工作 用户分配任务

内核守护线程:内核为维护正常运行创建、仅工作在内核态线程

  • 按作用可以分类

    • 周期性间隔运行,检测特定资源的使用,在用量超出或低于 阈值时采取行动
    • 启动后一直等待,直到系统调用请求执行某特定操作
  • 执行以下任务

    • 周期性将dirty内存页与页来源块设备同步:bpflush线程
    • 将不频繁使用的内存写入交换区:kswapd线程
    • 管理延时动作:kthreadd线程接手内核守护线程创建
    • 实现文件系统的事务日志
  • 内核守护线程只能工作在内核态

    • 没有用户空间,和内核共用一张内核页表
    • 只能使用大于PAGE_OFFSET部分的虚拟寻址空间,即进程 描述符中current->mm始终为空
    • 对4G主存的X86_32机器,只能使用最后1G,而普通pthreads 可以使用完整虚拟寻址空间
  • 内核守护线程名往往为k开头、d结尾

特殊内核守护线程

  • Linux内核启动的最后阶段,系统会创建两个内核线程
  • init:运行文件系统上一系列init脚本,并启动shell 进程

    • 是所有用户进程的祖先,pid为1
  • kthreadd:内核启动完成之后接手内核守护线程的创建

    • 内核正常工作时永不退出,是死循环,pid为2
    • 载入内核模块时即需要调用其创建新内核守护线程

进程状态

1
2
3
4
5
6
7
8
// <kernel/include/linux/sched.h>
#define TASK_RUNNING 0
#define TASK_INTERRUPTIBLE 1
#define TASK_UNINTERRUPTIBLE 2
#define __TASK_STOPPED 4
#define __TASK_TRACED 8
#define EXIT_ZOMBIE 16
#define TASK_DEAD 64

process_status

  • 状态虽然有很多种,但总是TASK_RUNNING -> 非, 即使进程在TASK_INTERRUPTIBLE状态被kill,也需要先唤醒 进入TASK_RUNNING状态再响应kill信号进入TASK_DEAD
  • TASK_RUNNING:可执行,正在执行或在运行队列中等待执行

    • 同一时刻可能有多个进程处于可执行态,位于运行队列中 等待进程调度器调度
  • TASK_INTERRUPTIBLE:正在阻塞,等待某些条件达成

    • 条件达成后内核会把进程状态设置为运行
    • 此状态进程也会因为接收到信号而提前唤醒准备运行
    • 系统中大部分进程都此状态
  • TASK_UNINTERRUPTILBE:不响应异步信号,即使接收到信号 也不会被唤醒或准备投入运行

    • 不可中断是指进程不响应异步信号,而不是指CPU不响应 中断
    • 内核某些处理流程是不可被打断的,如:内核和硬件设备 交互被打断会导致设备进入不可控状态,因此需要此状态
  • __TASK_TRACED:被其他进程跟踪

    • 开发中进程停留在断点状态就是此状态,如:通过ptrace 对调试程序进行跟踪
    • 此状态进程只能等待调试进程通过ptrace系统调用执行 PTRACE_CONTPTRACE_DETACH等操作才能恢复到 TASK_RUNNING状态
  • __TASK_STOPPED:停止执行,没有也不能投入运行

    • 通常发生在接收到SIGSTOPSIGSTPSIGTTINSIGTTOU等信号
    • 向此状态进程发送SIGCONT信号可以让其恢复到 TASK_RUNNING状态
  • TASK_DEAD:退出状态,即将被销毁

  • EXIT_ZOMBIE/TASK_ZOMBIE:进程已结束但task_struct未 注销

    • 进程退出过程中处于TASK_DEAD状态,进程占有的资源将 被回收,但父进程可能会关心进程的信息,所以 task_struct未被销毁

内核态、用户态

  • 系统设计角度:为不同的操作赋予不同的执行等级,与系统相关 的特别关键的操作必须有最高特权程序来完成

    • 运行于用户态:进程可执行操作、可访问资源受到限制
    • 运行于内核态:进程可执行任何操作、使用资源无限制
  • 内存使用角度(虚拟寻址空间,X86_32位系统,最大4GB主存)

    • 内核空间:最高的1G,所有进程共享
      • 包含系统堆栈:2页面,即8K内存,低地址中存放 task_struct
      • 进程运行于内核空间时使用系统堆栈、处于内核态
    • 用户空间:剩余3G
      • 包含用户堆栈
      • 进程运行于用户空间时使用用户堆栈、处于用户态

    virtual_address_space

  • 内核态的逻辑

    • 进程功能和内核密切相关,进程需要进入内核态才能实现 功能
    • 应用程序在内核空间运行、内核运行于进程上下文、陷入 内核空间,这种交互方式是程序基本行为方式
  • 用户态进入内核态的方式

    • 系统调用,如:printf函数中就是调用write函数
    • 软中断,如:系统发生异常
    • 硬件中断,通常是外部设备的中断

    process_calling_structure

  • 进程或者CPU在任何指定时间点上活动必然为

    • 运行于用户空间,执行用户进程
    • 运行于内核空间,处于进程上下文,代表某特定进程执行
    • 运行于内核空间,处于中断上下文,与任何进程无关,处理 特点中断

Linux进程数据结构

task_struct

1
2
3
4
5
6
7
8
9
10
11
12
13
// <kernel/include/linux/sched.h>
struct task_struct{
volatile long state; // -1:不可运行,0:可运行,>0:已中断
int lock_depth; // 锁深度
unsigned int policy; // 调度策略:FIFO,RR,CFS
pid_t pid; // 线程ID
pid_t tgid; // 线程组ID,2.6内核中引入
struct task_struct *parent; // 父进程
struct list_head children; // 子进程
struct list_head sibling; // 兄弟进程
struct task_struct *group_leader;
struct list_head thread_group;
}

task_struct

  • 内核使用任务队列(双向循环链表)维护进程(描述符)

  • task_struct:进程描述符,包含进程的所有信息,包括

    • 进程状态
    • 打开的文件
    • 挂起信号
    • 父子进程

ID

  • pid:字面意思为process id,但逻辑上为线程ID
  • tgid:字面意思为thread group id,但逻辑上为 进程ID
1
2
3
4
5
6
7
// <kernel/timer.c>
asmlinkage long sys_getpid(void){
return current->tgid;
}
asmlinakge long sys_gettid(void){
return current->pid;
}

线程关系

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// <kernel/fork.c>
copy_process{
// some code
p->tgid = p->pid;

// 创建线程时
if (clone_flags & CLONE_THREAD)
// 从父进程获取`tgid`,归属同一线程组
p->tgid = current->tgid;

// some code
// 初始化`group_leader`、`thread_group`
p->group_leader = p;
INIT_LIST_HEAD(&p->thread_group);

// some code

// 创建线程时
if (clone_flags & CLONE_THREAD){
// `group_leader`设置为父进程`group_leader`
// 即保证`group_leader`指向首个线程`task_struct`
p->group_leader = current->group_leader;
// 通过`thread_group`字段挂到首个线程的`thread_group`队列中
list_add_tail_rcu(&p->thread_group, &p->group_leader->thread_group);

// some code
}

if(likely(p->pid)){
// some code
// 仅首个线程才会通过`tasks`字段挂入`init_task`队列中
if(thread_group_leader(p)){
//...
list_add_tail_rcu(&p->tasks, &init_task, tasks);
}
}
}

线程组退出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
// <kernel/exit.c>
NORET_TYPE void do_group_exit(int exit_code){
BUG_ON(exit_code & 0x80);

// `current->signal`由线程组中所有线程共享
// 若调用此方法线程`SIGNAL_GROUP_EXIT`标志已被设置,说明
// 其他线程已经执行过此方法,已通知线程组中所有线程
// 退出,则可以直接执行`do_exit`
if(current->signal->flags & SIGNAL_GROUP_EXIT)
exit_code = current->signal->group_exit_code;

// 否则通知线程组中所有线程退出
else if(!thread_gropu_empty(current)){
struct signal_struct * const sig = current->signal;
struct sighand_struct * const sighand = current->sighand;
spin_lock_irq(&sighand->siglock);

// another thread got here before we took the lock
if(sig->flags & SIGNAL_GROUP_EXIT)
exit_code = sig->group_exit_code;
else{
sig->group_exit_code = exit_code;
zap_other_threads(current);
}
spin_unlock_irq(&sighand->sigloc);
}

do_exit(exit_code);
}

// <kernel/signal.c>
void zap_other_threads(struct task_struct *p){
struct task_struct *t;

// 设置`SIGNAL_GROUP_EXTI`标志
p->signal->flags = SIGNAL_GROUP_EXIT;
p->signal->group_stop_count = 0;

if(thread_group_empty(p))
return;

for (t=next_thread(p); t != p; t=next_thread(t)){
// don't bohter with already dead threads
if (t->exit_state)
continue;

// 为每个线程设置`SIGKILL`信号
sigaddset(&t->pending.signal, SIGKILL);
signal_wake_up(t, 1);
}
}

// <include/linux/sched.h>
static inline struct task_struct *next_thread(const struct task_struct *p){
return list_entry(rcu_dereference(p->thread_group.next),
struct task_struct, thread_group);
}

Slab分配器

process_slab

  • slab分配器把不同对象类型划分为不同高速缓存组,如: task_structinode分别存放

    • 高速缓存又会被划分为slab
    • slab由一个或多个物理上连续的页组成
  • 申请数据结构时

    • 先从半满的slabs_partial中申请
    • 若没有半满,就从空的slabs_empty中申请,直至填满 所有
    • 最后申请新的空slab
  • slab分配器策略优点

    • 减少频繁的内存申请和内存释放的内存碎片
    • 由于缓存,分配和释放迅速

thread_info

1
2
3
4
5
6
7
8
9
10
// <asm/thread_info.h>
struct thread_info{
struct task_struct *task;
struct exec_domain *exec_domain;
usigned long flags;
__u32 cpu;
int preempt_count;
mm_segment_t addr_limit;
struct restart_block restart_block;
}
  • 内核中对进程操作都需要获得进程描述符task_struct指针, 所以获取速度非常重要
    • 寄存器富余的体系会拿出专门的寄存器存放当前 task_struct的指针
    • 寄存器不富余的体系只能在栈尾创建thread_info结构, 通过计算间接查找

进程创建

  • 继承于Unix,Linux进程创建使用两个函数分别完成,其他如Win 可能都是通过一个方法完成
  • fork函数:拷贝当前进程创建子进程

    • 子进程、父进程区别仅在于PID、PPID和少量资源
  • exec函数(族):载入可执行文件至地址空间开始运行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
SYSCALL_DEFINE0(fork){
return do_fork(SIGCHLD, 0, 0, NULL, NULL);
}
SYSCALL_DEFINE0(vfork){
return _do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD,
0, 0, NULL, NULL, 0);
}
long do_fork(unsigned long clone_flags,
unsigned long stack_start,
unsigned long stack_size,
int __user *parent_tidptr,
int __user *child_tidptr){
return _do_fork(clone_flags, stack_start, stack_size,
parent_tidptr, child_tidptr, 0);
}
long _do_fork(unsigned long clone_flags,
unsigned long stack_start,
unsigned long stack_size,
int __user *parent_tidptr,
int __user *child_tidptr,
unsigned long tls){
// some code
p = copy_process(clone_flags, stack_start, stack_size,
child_tidptr, NULL, trace, tls);
// some code
}
  • forkvfork最终都是通过调用_do_fork实现,仅传参 不一致

    • 首个参数为clone_flags,最终被copy_process用于 真正的拷贝执行
  • 通过系统调用clone()创建线程

    • 同创建进程系统调用fork()vfork()一样,最终调用 do_fork方法,但传递和进程创建时不同的flag,指明 需要共享的资源

      1
      CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGNAND

fork

fork():子进程是父进程的完整副本,复制了父进程的资源, 包括内存内容、task_struct

  • 子进程拷贝父进程的数据段、代码段

    • 同一变量的虚拟地址相同(物理地址不同)
  • 利用copy-on-write优化效率

    • 内核创建子进程时不复制父进程的地址空间,而是只读共享 父进程空间数据
    • 只有子进程需要写数据时才会拷贝到子进程
  • 页表:存放给从逻辑页号到物理页帧/块号地址的映射

Unix傻瓜式进程创建

  • 内核原样复制父进程整个地址空间,并分配给子进程,效率低

    • 为子进程页表分配页帧
    • 为子进程页分配页帧
    • 初始化子进程页表
    • 把父进程页复制到子进程相应页中
  • 大部分情况下复制父进程页无意义

    • 子进程会载入新的程序开始运行
    • 丢弃所继承的地址空间

Copy-on-Write

copy-on-write思想简单:父进程、子进程共享页帧

  • 共享页帧不能被修改,父进程、子进程试图写共享页帧时产生 page_fault异常中断

  • CPU执行异常处理函数do_wp_page()解决此异常

    • 对导致异常中断的页帧取消共享操作
    • 为写进程复制新的物理页帧,使父、子进程各自拥有内容 相同的物理页帧
    • 原页帧仍然为写保护:其他进程试图写入时,内核检查进程 是否是页帧的唯一属主,如果是则将页帧标记为对此进程 可写
  • 异常处理函数返回时,CPU重新执行导致异常的写入操作指令

  • copy-on-write:多个呼叫者同时要求相同资源时,会共同 取得相同指针指向相同资源,直到某个呼叫者尝试修改资源时, 系统才给出private copy,避免被修改资源被直接察觉,此 过程对其他呼叫者transparent

vfork

vfork():子进程直接共享父进程的虚拟地址空间、物理空间

  • vfork被设计用以启动新程序

    • 内核不创建子进程的虚拟寻址空间结构
    • 进程创建后应立即执行exec族系统调用加载新程序,替换 当前进程
    • exec不创建新进程,仅用新程序替换当前进程正文、 数据、堆、栈
  • 在子进程调用exec函数族、_exit()exit()前,子进程 在父进程的地址空间中运行

    • 二者共享数据段,子进程可能破坏父进程数据结构、栈
    • 父进程地址空间被占用,因此内核会保证父进程被阻塞, 即vfork会保证子进程先运行
  • 应确保一旦调用vfork

    • 子进程不应使用return返回调用处,否则父进程又会 vfork子进程
    • 子进程不应依赖父进程进一步动作,否则会导致死锁
    • 子进程需避免改变全局数据
    • 若子进程改变了父进程数据结构就不能调用exit函数

clone

clone:可有选择地继承父进程资源

1
int clone(int (fn)(void), void * child_stack, int flags, void * args);
  • clone通过众多参数有选择地创建进程

    • 创建LWP/线程
    • 创建兄弟进程
    • 类似vfork创建和父进程共享虚拟寻址空间
  • 参数说明

    • fn:函数指针
    • child_stack:为子进程分配的系统堆栈空间
    • flags:描述需要从父进程继承的资源,如下
    • args:传给子进程的参数

Flags

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#define CSIGNAL		0x000000ff		// signal mask to be setn at exit
#define CLONE_VM 0x00000100 // set if VM shared between process
#define CLONE_FS 0x00000200 // set if fs info shared between processes
#define CLONE_FILES 0x00000400 // set if open files shared between processes
#define CLONE_SIGHAND 0x00000800 // set if signal handlers and blocked signals shared
#define CLONE_PTRACE 0x00002000 // set if we want to let tracing continue on the child too
#define CLONE_VFORK 0x00004000 // set if the parent wants the child to wake it up on mm_release
#define CLONE_PARENT 0x00008000 // set if we want to have the same parent as the cloner
#define CLONE_THREAD 0x00010000 // same thread group?
#define CLONE_NEWS 0x00020000 // new namespace group?
#define CLONE_SYSVSEM 0x00040000 // share system V SEM_UNDO semantics
#define CLONE_SETTLS 0x00080000 // create a new TLS for the child
#define CLONE_PARENT_SETTID 0x00100000 // set the TID in the parent
#define CLONE_CHILD_CLEARTID 0x00200000 // clear TID in the child
#define CLONE_DETEACHED 0x00400000 // unused
#define CLONE_UNTRACED 0x00800000 // set if the tracing process can't force `CLONE_PTRACE` on this clone
#define CLONE_CHILD_SETTID 0x01000000 // set the TID in the child
#define CLONE_STOPPED 0x02000000 // start in stopped state
#define CLONE_NEWUTS 0x04000000 // new utsname group
#define CLONE_NEWIPC 0x08000000 // new ipcs
#define CLONE_NEWUSER 0x10000000 // new user namespace
#define CLONE_NEWPID 0x20000000 // new pid namespace
#define CLONE_NEWNET 0x40000000 // new network namespace
#define CLONE_IO 0x80000000 // clone into context

线程库

  • POSIX标准要求:线程不仅仅是共享资源即可,其需要被视为 整体
    • 查看进程列表时,一组task_struct需要被展示为列表中 一个节点
    • 发送给进程信号,将被一组task_struct共享,并被其中 任意一个线程处理
    • 发送给线程信号,将只被对应task_struct接收、处理
    • 进程被停止、继续时,一组task_struct状态发生改变
    • 进程收到致命信号SIGSEGV,一组task_struct全部退出

LinuxThread线程库

LinuxThread线程库:Linux2.6内核前,pthread线程库对应实现

  • 特点
    • 采用1对1线程模型
    • 通过轻量级进程模拟线程
    • 线程调用由内核完成,其他线程操作同步、取消等由核外 线程库完成
    • 仅通过管理线程实现POSIX以上5点要求中最后一点

管理线程

管理线程:为每个进程构造、负责处理线程相关管理工作

  • 管理线程是主线程的首个子线程

    • 进程首次调用pthread_create创建线程时会创建、启动 管理线程
  • 管理线程负责创建、销毁除主线程外线程,成为LinuxThread 的性能瓶颈

    • 从pipe接收命令创建线程
    • 子线程退出时将收到SIGUSER1信号(clone时指定), 若不是正常退出,则杀死所有子线程并自杀
    • 主循环中不断检查父进程ID,若为1说明原父线程退出并 被托管给init线程,则杀死所有子进程并自杀
  • 通过LWP模拟线程存在的问题

    • LWP不共享进程ID
    • 某些缺省信号难以做到对所有线程有效,如:SIGSTOPSIGCONT无法将整个进程挂起
    • 线程最大数量收到系统总进程数限制
    • 管理线程是性能瓶颈,一旦死亡需要用户手动清理线程、 无人处理线程创建请求
    • 同步效率低,通过复杂的信号处理机制进行同步
    • 与POSIX兼容性差

Naive POSIX Thread Library

NPTL:Linux2.6内核重写线程框架的基础上引入的pthread线程库

  • 本质上还是利用LWP实现线程的1对1线程模型,但结合新的线程 框架实现了POSIX的全部5点要求

    • 线程组tgid引入体现task_struct代表进程还是线程
    • task_struct维护两套signal_pending
      • 线程组共享signal_pending:存放kill发送信号, 任意线程可以处理其中信号
      • 线程独有signal_pending:存放pthread_kill发送 信号,只能由线程自身处理
    • 收到致命信号时,内核会将处理动作施加到线程组/进程中
  • 但也存在一些问题

    • kill未展示的LWP会杀死整个进程
  • RedHat开发,性能远高于LinuxThreads,需要内核支持

Next Generation Posix Threads for Linux

NGPT:基于GNU Portable Threads项目的实现多对多线程模型

  • IBM开发,性能介于LinuxThread、NPTL间,2003年终止开发