Linux不权威总结
欢迎阅读
本文主要是从Linux入手,对常用的操作系统知识进行梳理。长篇大论令人生厌,所以本文的一个原则就是聚焦关键点,浅尝辄止,以免误人子弟,同时也会尽量附上各种详尽的资料以备感兴趣的同学深入了解
Linux简史
计算机的发明
- 各大厂家各自制造机器,市场充斥着各种大型机(IBM GE HP)和小型机(Intex).
- 各个厂家从硬件到软件(主要是操作系统)维护自己的产品.
Unix诞生
- 最早的大型机只能服务数十人,于是Bell实验室跟GE MIT合作启动
MULTICS计划
,旨在开发一个能够服务上百人的多用户,多任务,多层级操作系统,卒,但该项目培养了大批优秀的计算机人才 - Ken Thompson为了玩一款<星际旅行>的游戏,反MULTICS其道而行,力求简洁实用,诞生了
Unix
- 后续Thompson被汇编语言折腾受不了,发明了
c语言
,重写了Unix内核(1970) - 由于全新的Unix易于开发移植(得益于c语言),同时功能强大,所以迅速发展,各大厂商也开始
兼容Unix
,并诞生了POSIX
,规范OS提供的编程接口 - AT&T禁止Unix传播,Unix开始内战,互不兼容,各自为战,一发不可收拾的出现各种
变种
- 此时Intel为各个中小计算机公司生产通用CPU,然后微软为该体系架构生产通用的操作系统,自此,
win-tel
体系建立,在Unix内乱之际疯狂生长 - Berkeley发布FreeBSD
GNU (GNU's not Unix)
- Unix的封闭之路使多数人深恶痛绝,Stallman发起
自由软件计划
-
GPL授权
诞生,确定了开源软件服务化的商业空间 - GNU计划开发兼容Unix的操作系统,但是为了先易后难,从应用软件下手
- 从免费的编译器,编辑器,公共库,脚本工具着手,于是gcc emacs glibc bash...等一大批优秀的开源软件诞生
Linux诞生
-
Linus Torvalds
(芬兰大学生)在一个教授开发的Unix简易教学版minix上学习,但由于教授不愿意继续开发,他便酝酿自己的操作系统 - Linus发布了自己写的"小玩意儿",吸引了大量
黑客
的兴趣 - 为了效率Linus主要开发内核,而将源码免费开放,所有人
协同开发
,统一整合 - 开源,分布式开发,虚拟团队,快速迭代...这些令人兴奋的事件都拉开了序幕
- Linux正式确定,同时支持移植386小型家用机
- 厂商开始支持Linux,由于Linux使用gcc,bash等很多GNU计划的工具,GNU也顺理成章的采用Linux,填补了开源内核这一空白,Stallman认为Linux其实应该是
GNU/Linux
内核版本 & 发布版本
linux的内核版本号形如2.6.18-92.e15
,依次为主版本.次版本.发布版本-修改版本。主、次版本号为奇数的话为内核开发版本,一般是用来测试新功能,内核工程师开发使用的;主、次版本号为偶数则为稳定版本
发布版本distributions,是指各个linux开发商发布的版本,比如CentOS、RedHat。这些开发商都是基于相同的linux内核,不同的是他们会基于此提供不同的服务、工具,比如包安装、系统管理等
换行和回车
- 在早期输出设备是一种叫做电传打字机的设备,该设备能够0.1s打一个字
- 但当需要换行的时候,该设备需要0.2秒,两个动作,换行,同时返回行首
- 正好是两个字符的时间,所有当时为了省事,直接在行尾设置了两个特殊字符,给设备换行的时间CR 回车 LF 换行
- 后来使用电子屏后,一些系统为了节省字符(unix linux)就只有一个LF(\n),mac则只有一个CR(\r),win则两个都做了保留
- 所以如果把win的文件在linux下打开,则会在每行多看见一个^M字符(win的回车),可以使用
unix2dos或者dos2unix
进行转换
计算机硬件组成
硬件基本架构
现代计算机基本都是冯·诺依曼结构,即由核心运算器、程序控制器、内存、输入设备、输出设备组成。
CPU包括核心运算器(ALU)和程序控制器(PC),通过其提供的一系列cpu指令
来提供包括数学运算、数据读取、数据写入,以及应该运行哪条指令等服务,是计算机的‘心脏’
主存储器,也就是内存,服务CPU的数据读取和写入,与I/O设备数据加载
I/O设备,则各种各种,常见如鼠标、键盘、显示器、USB、磁盘、网卡等,负责从真实世界读取数据和以各种形式数据数据
这些设备通过总线(PCI)
通信,总线同一时间只能广播一条指令
CPU
中央处理单元,可以说是人类智慧的集中体现。它的模型被抽象的很简单,读取一条cpu指令,翻译指令,然后执行(可能为数学运算、读取数据、写入数据或者指定下一条指令),循环往复,直到关机
CPU指令
cpu指令很简单就是一系列的0、1序列,cpu从寄存器中读取到这些0、1序列后,执行相应的操作。我们编写的程序最终都要翻译成cpu指令。不同的机器cpu实现不同,其指令集也会有所不同,所以编译好的可执行程序一般不能直接在不同的机器上运行
cpu的指令历史上有两条路线,精简指令集(CISC)
和复杂指令集(RISC)
,他们的主要区别是CISC认为cpu常用的指令并不多,不需要因为少数不常用的操作来增加cpu的复杂度,只需要一个少量的简单指令集合即可,既可以降低cpu的复杂度,也方便对指令进行优化;RISC则是提供功能强大,但是更耗cpu周期的指令来方便cpu的使用。目前两者已经逐渐融合
CPU型号
不同厂家生产的CPU具有不同构造和指令集,最初计算机的CPU是需要各个公司自己设计生产的。后来Intel公司设计生产了通用cpu,逐渐成为市场标准
由于Intel生产的cpu最初型号是8086、80286、i386、i486等,人们习惯称Intel系列cpu为x86,根据cpu支持的位数不同,又分为x86-32和x86-64。此外还有IBM处理器和arm处理器
CPU数量
服务器的物理CPU是可以多核的,所以 逻辑cpu数 = 物理cpu * 核数
。可以通过uptime top 等工具查看系统的平均负载,了解服务器的运行情况
如果有4个逻辑cpu,top显示负载在4及以下,则说明系统相对正常,如果长期在4以上则说明服务器压力过大(负载值同一时间段内运行的进程数,所以是会比cpu数大的)
Linux文件系统
一切皆文件
linux一个非常优雅的设计便是将所有I/O对象抽象为文件,本质上文件只是一个字节序列,这个字节序列可能存储在磁盘上,也可能来自网卡,也可能来自键盘输入,也可能来自其他程序输出,等等等等。这样做的好处是linux只需要提供少数简单一致的api接口来实现输入和输出,比如read write
主要目录
-
/
根目录,存放着系统核心程序 -
/usr
unix software resource的缩写,存放unix厂商(可能是改版的发行者)开发的软件资源 -
/usr/local
存放用户安装的软件资源 -
/opt
option(选装),存放第三方厂商开发的软件资源 -
/lost+found
标准ext2/ext3文件系统才会有该目录,主要是在文件系统异常时暂存碎片,日后恢复 -
/boot
系统启动所需程序 -
/home
系统用户主文件夹 -
/mnt
临时设备挂载点 -
/log
非日志目录
,而是与用户登录相关 -
/var
主要存放一些经常变化的文件,和系统运行过程中产生的文件-
/var/run
一般是各个进程用于存放运行时的信息,如nginx.id -
/var/lock
进程控制资源的锁 -
/var/cache
进程运行中的一些缓存
-
-
/proc
这个目录比较特殊,是一个虚拟文件系统,保存着系统内核,进程,外设,网络等的状态,数据全在内存中,本身不占用任何硬盘空间,比较重要的目录有/proc/cpuinfo, /proc/ioports, /proc/net/*
此外还有一些目录经常成套出现在以上个别目录中,意义是差不多的
-
/bin
二进制可执行文件,在 / 中为与系统运行相关 -
/sbin
二进制可执行文件,主要是系统维护相关 -
/include
c/c++常用头文件,我们在以源码包方式编译安装软件时,会使用 -
/lib
应用软件的函数库,目标文件 -
/etc
配置文件 -
/src
源码 -
/tmp
临时文件夹
注意linux中文件的目录结构只是逻辑结构, 真正的物理结构还要看实际挂载位置。比如/home挂载在硬盘1,/home/liufuxin可能挂载在硬盘2
inode
inode可以理解为文件的元信息,linux中每个文件都会对应一个inode,inode中包含文件的权限、字节、存储位置、链接数、最近访问信息等,理解inode有助于理解文件的权限和链接
- 普通文件,包含一个文件名和inode号,读取文件其实是三步:读取文件的inode号、获取inode信息、读取数据
- 目录文件,内容为目录包含的文件和文件的inode号,有目录的r权限,则可以看文件名,但如果没有x权限,则因为无法解析inode则不能获取大小、权限、最近访问等信息,更不能读取文件内容和写文件
相关命令
-
ls -i
查看文件inode号 -
stat file
查看文件inode信息
文件权限
linux中每个文件(目录本质也是文件),都会有一组以下权限串rwxrwxrwx
- r 代表读权限
- w 代表写权限
- x 代表执行权限
而三组rwx则依次代表了文件创建者,文件用户组,其他人的权限。通常也会用三个八进制数来表示权限,比如rwxrwxrwx
会用777
表示,r-x-wx---
用530
表示
注意rwx对于文件和目录的意义有着一些差别
对于file
- r 是否可以读取文件内容
- w 是否可以更改文件内容
- x 是否可以执行文件
对于dir
- r 是否可以读取到目录中文件
- w 是否可以在目录中新建,删除,重命名
- x
- 是否可以cd进入目录;
- 是否可以解析包含文件的inode信息,这就导致
x其实是rw的前提
(即便有r权限,可以知道文件名但因为无法读取inode,不能获取文件类型大小等); -
x权限是会影响子目录的,rw不会
,即无父目录的rw,也不影响子的rw权限;
权限修改
文件权限修改有两种办法
chmod 777 file
-
chmod [a|u|g|o]+r-w file
u为拥有者,g为用户组,o为其他,a为全部,+为添加,-为去掉,比迁一种要容易记一些
相关命令
-
chgrp
修改文件用户组, chgrp [-R] users file, 改变file文件的用户组为users,-R为递归修改 -
chown
修改文件拥有者, chown [-R] user[:group] file, 除了改变文件拥有者,还可以修改文件用户组
默认权限
umask控制,类似子网掩码. 创建文件时,系统会去掉umask标记的权限,比如umask为111则创建的文件权限为666
但要注意,无论umask如何设置新建的文件都不会具有x权限
,这也是经常出错的地方,为了安全起见, 一般umask为022
-
umask
查看umask值 -
umask 022
设置umask
SetUID (SUID)
想想这样的场景, /etc/shadow文件中保存着所有用户的密码信息,只有管理员能查看。/usr/bin/passwd为修改密码程序,所有用户都有执行该程序的权限, 但是我们进程的权限与启动用户一致的, 那非管理员启动的passwd进程是如何修改密码呢?
特殊权限SetUID就是处理该场景, 当文件拥有者
的权限中执行位为s
时, 其他用户执行该文件期间可以具有拥有者的权限,仅对二进制文件有效,所以只需要为passwd二进制文件添加该权限即可让普通用户执行该程序时拥有管理员权限,同时只能按照程序提供的操作执行。类似的还有SetGID(SGID), 表明执行期间拥有文件所在用户组的权限
Sticky Bit (SBIT)
想想这样的场景, 团队公共目录 ~/work, 所有人都拥有该目录的x权限, 若再具有w权限则能够进行删减和重命名, 如何避免误操作?
只需要为目录设置SBIT权限即可, SBIT只对目录有效, 设置后仅有root或拥有者可以删减对应文件
SUID SGID SBIT权限修改。第一种是在原来三位权限前再添加一位
- 4 SUID
- 2 SGID
- 1 SBIT
比如chmod 4666 file
,或者使用符号, chmod u+s|g+s|o+t file
,注意当看到S或者T时, 说明无效, 比如不具备目录w权限, t就无效
软/硬链接
linux中有软硬链接文件, 可以方便用户管理和使用文件系统
硬链接
并未创建新的inode节点, 只是新建了一个文件, 并指向源inode, 通过ls -i
可以看到文件的链接数+1。当删除源文件时, 不会影响链接文件,删除硬链接文件则会导致源文件inode链接数-1,为0时inode也会被删除。创建硬链接ln source target
因为是公用inode所以就决定, 硬链接不能跨文件系统, 不能远程。而且由于硬连接指向了inode,所以当源文件被删除时,真正的inode不会被删除,硬连接依旧正常使用。软连接则不行;同时目前硬链接不支持目标文件为目录
软链接
新建了一个链接文件, 然后这个文件的内容是链接文件的路径,注意这是两个文件、两个inode, 类似windows的快捷方式, 创建方式ln -s source target
。删除软链接文件不会影响源文件,删除源文件则会导致软链接无法正常使用
编码
计算机中的所有数据本质上都是01序列,只特定的上下文中,赋予了01序列实际意义
数据表示
- 一个0、1为1个bit,即一位
- 8个bit为一个byte,即一个字节
- 2个byte为一个word,即一个字
- 一般使用4个byte表示int,1个byte表示char
ASCII UTF-8 Unicode
- unicode是对全世界所有字符的一种编码,用
4个byte
来标记一个字符,称为统一字符集
- ascii则是计算机早期的一种字符编码,用
1个byte
来表示一个字符,主要用来表示英文字母、常用符号 - utf-8,unicode trans format,unicode传输格式,是一种为了节约空间对Unicode
字符集
的一种编码方式
,将常用的字符如英文字符使用1个byte表示,用3个byte来表示中文字符 - gb2312是汉字最早的编码格式,gbk是其扩展,收录了更多汉字(不含繁体)和少数民族文字
大端、小端
比如整数255的字节表示为01 00,01被称为最高有效字节,00称为最低有效字节,所以
- 大端模式是指计算机在存储数据时,最高有效字节在前,最低在后,即 01 00
- 小端模式是指计算机在存储数据时,最低有效字节在前,即00 01
需要注意的是为了数据在网络中交换,规定网络数据一律使用大端模式
延伸
网页编码那点事
重定向
系统会为每个程序都默认打开3个标准文件标示符,0标准输1,1标准输出,2标准错误输出,程序可以通过与文件标识符交互(read、write)完成读取输入和输出结果
重定向程序输出时,> 覆盖式写,>> 追加式写,&的作用是获取其后文件标示符表示的设备
/bin/sh script 1> out 2> err,注意>前无空格,否则1、2就变成‘参数’了
/bin/sh script 1>out 2>&1,标准输出到out,然后标准错误输出重定向到标准输出,注意顺序和无空格
/bin/sh script &> out,将标准输出和错误都输出到out
注意/bin/sh script 1>list 2>list
是错误写法,这样写会导致两个输出混在一起
虚拟内存
一台计算机的存储设备一般包括寄存器、主存(内存)、硬盘,现在考虑这些问题:
- cpu只能直接与寄存器进行交互,而且只能直接读写内存中的数据,那么如何高效的将数据从硬盘中载入寄存器供cpu使用?
- 系统中有成百上千的进程在同时运行,每个进程的执行命令、处理数据、输出结果都依赖内存,如何管理内存?
虚拟内存便是操作系统用来维护计算机存储设备的机制。其基本模型是将内存、硬盘都划分为以页为基本元素的大的数组。
基于这个模型,使用“文件”这个概念来维护硬盘中的数据,文件记录了其所代表的数据在磁盘的所有页的位置
进程申请内存便是为其在内存中分配页槽位,释放内存便是释放这些槽位,read文件数据便是将文件的页从磁盘读入到内存的页槽中。操作系统维护着内存中每个页槽属于哪个进程,来保证进程内存之间的隔离,同时给每个进程造成一种在“独占”内存的假象(在进程看来,自己的内存空间是连续的)
就是这么一个简单的模型,保证了数据有序的在硬盘、内存、寄存器之间有序维护
缓存
寄存器的读取速度为1个单位的话,那么内存的读取速度就是10个单位,而硬盘的读取速度则是100000个单位,如果每次从文件读取数据都要由硬盘重新读取的话,那么cpu资源将极大浪费
操作系统使用缓存来解决不同存储设备读取速度巨大差距的问题。当进程申请将一页从硬盘读入进程的页槽中时,操作系统会先把页加载到内核进程的页槽中,再copy到进程的页槽中,之后的一定时间内当其他进程再申请同一个页时,便可以直接在内存中copy
而且由于当前硬盘的构造,读取时间基本消耗在寻址阶段,当找到页后,顺序读取时,其实只需要大约10个单位的时间,所以操作系统在加载页时,也会顺便把该页前后的页也顺带加载,根据程序的局部性原理
这些页大概率会在接下来的时间内被申请
程序的局部性原理,是指时间的维度看同一份数据大概率会在短时间内被程序反复使用,或者从空间的维度看一份数据一旦被程序使用,那么它相邻的数据也大概率会被程序使用
正是局部性原理,保证了可以进行缓存。试想如果一个程序天上一脚、地下一脚的读取数据,那么操作系统就只能反复从硬盘中加载数据,这也是一些程序性能差的原因之一
延迟写
另一项操作系统提供性能的策略是延迟写,当进程修改页中的数据时,操作系统不会立刻刷会硬盘中,因为进程可能会短时间内频繁操作,而且刷回硬盘需要大量时间。被修改的页(称为脏页)会在内核空间中停留一段时间后,根据不同的策略再刷回硬盘
进程
进程简单来说,就是操作系统一个可执行程序的实例,是对计算机计算能力(cpu)+ 存储能力(主存)+ 交互能力(I/O设备)的抽象
通过进程,你可以对三者完全无感知的情况下来控制计算机。比如我们写程序从文件读入一个字符串,并不需要知道文件数据如何存储在硬盘,处理整个字符串也并不需要知道执行哪些cpu指令,把他打印出来也不需要知道外设如何使用
类似地,文件是操作系统对I/O设备的抽象。虚拟内存是对I/O设备+主存的抽象
我们来利用计算机工作,就必须执行具体的程序,生成进程来完成
进程基本知识点
- 进程必有pid,与线程的共用一个计数,所以有可能不连续
- 进程必有ppid,父进程,1号进程除外
- 进程必有名称
- 进程必有退出码,0~255
进程的内存结构
进程会给程序一种假象,他是在独占地使用计算机的计算、存储资源。这样不仅可以最大程度发挥计算机硬件性能,而且降低程序的开发难度,进程互相之间不需要操心彼此影响(高级编程除外)。而要实现这一点,进程的内存就必须互相隔离,操作系统利用虚拟内存机制来实现这一点。下面是进程的典型内存结构
依旧把进程的内存空间想象为一个很长的页槽数组
- 所有进程的0x004000000位开始是进程的指令序列,即
text段
,是在进程启动前完成加载的(这就是为什么更改已经运行的程序源码不会影响进程) - text段之上是
static段
包含由编译器所分配的变量,包括全局变量,和使用static声明的局部变量; - 再往上是
heap段
进程运行过程中间的内存扩展部分; - 再之上是共享库的内存空间;
- 最顶部对用户进程隐藏的是内核的虚拟内存;往下是进程运行过程中的栈空间
stack段
(用来执行函数、线程等);
所以,一个程序(也就是进程)运行时,是先把运行代码加载到text段,编译期间的静态变量、全局变量等都加载在static段。运行过程中需要读取的文件内容等存放在heap段,申请内存也加塞在这。需要执行公共库的代码、数据则在公共空间;系统调用所需的代码、数据则在最顶部。本地的函数创建调用则在用户栈里完成,调用时入栈、完成时出栈。
而且得益于虚拟内存,那些公共的部分看似进程独有,其实是进程间共享的,并没有浪费内存空间,还保证了进程内存空间的简单性
用户模式 & 内核模式
进程运行模式机制主要是为了防止恶意进程非法访问其他进程的数据,或者进行破坏,进程在正常运行是在用户模式下,当需要进行一些‘高难度’操作比如读取文件、使用显示器显示数据等,则需要通过操作系统提供的系统调用
来进行,此时进程的运行模式为内核模式,进程执行的指令跳转到内存的顶层内核指令空间,等待系统再次指定进入进程自己的指令空间
程序编译
进程是可执行程序的实例,那么可执行程序又是如何来的?
以c语言为例。我们在写好程序之后,编译器(如gcc)会把这些程序文本编译为汇编代码,然后再通过汇编器生成目标文件(不同的机器因为cpu指令集合可能不同,需要不同的汇编器),最终目标文件链接起来就是0、1组成的可执行文件了
链接
为什么不直接汇编生成可执行代码,还要经过一次链接?
最初的程序确实是这样,经过汇编之后产生的就是可执行文件,但实践证明这种模式并不高效,比如打印字符到显示器这样的功能代码段,大部分程序都需要,我们就必须在所有程序里重复实现,很傻是不是?所以就有了链接,把这些常用的程序提前汇编为目标代码,执行前链接一下就好。这样的的另一个好处就是减少编译时间,比如我就修改了一个目标文件的源码,只需要重新编译该目标文件即可,难道我要把整个项目都编译一遍吗?
动态库 & 静态库
跟链接的情况类似,还是打印字符到显示器这样的指令集合,难道要在每个进程的内存空间都加载一模一样的代码吗?
进程内存空间中共享内存便是这个作用,将常用的库在进程之间共享,这就是静态库。而动态库是指有些指令集合也是标准的,但使用频率很低,而且可以在操作系统执行期间动态加入,既不能也没必要提前加载,进程会在实际需要运行的时候,动态将指令空间加载进内存空间
为了避免从硬盘中加载代码进主存,操作系统会提前把动态库的代码加载进主存,所以当更新了动态库后,需要执行ldconfig来重新加载。lld可以查看一个可执行程序所依赖的动态库
进程状态
当进程被创建时,操作系统会为进程分配包含进程信息的数据结构,称为“进程控制块”(PCB),PCB跟踪进程的状态,主要包括:
- 运行(Running),如果进程正在运行于某个核心上
- 就绪(Ready),如果进程可以但没有运行,通常由于就绪进程数量大于内核的数量
- 阻塞(Blocked),如果进程由于正在等待未来的事件,例如网络通信或磁盘读取,而不能运行
- 终止(Done):如果进程运行完毕,但是带有没有读取的退出状态信息
文件描述符
进程有一个描述符表
用正整数来表示进程已经打开的文件。操作系统会在进程创建时预先为其打开文件,并提前打开3个标准文件,0标准输出,1标准输入,2标准错误输出
同时操作系统会全局维护一个文件表
记录所有被进程打开的文件,以及他们被引用的次数,文件被删除时,只有当引用数为0时才会真正从系统删除,所以进程要及时关闭不使用的文件
进程的并发
最初的计算机都是单任务的,同一时间只能做一件事,这样如果一个任务包含长时间的IO等操作,所有任务都得阻塞。为了解决这个问题,操作系统加入了进程概念,可以同时运行多个进程,从而实现多任务
并发 & 并行
并发
是指同一个时间段
内有多个进程在运行。并行
是并发的真子集,意思同一个时间点
有多个进程在运行,需要多cpu支持
上下文切换
操作系统实现进程并发的模型很简单,就是cpu不断在多个进程之间进行切换运行,由于cpu的周期非常短,所以多个进程看似是在同时运行,不过如果进程数不多于cpu核数的话,进程则确实是在同时运行,不过多数情况是进程数要多于cpu核数,进程需要交替运行
进程让出cpu的使用权叫做上下文切换
,内核不仅需要保存出让cpu使用权进程的各种信息,同时还要载入将运行的进程信息,这些信息被称为上下文,包括通用寄存器、浮点寄存器、程序计数器、用户栈、状态寄存器、内核栈和各种内核数据结构,比如描述地址空间的页表、包含当前有关进程信息的进程表,以及包含进程已打开文件的文件表
上下文切换的代价还是挺大的,大约是几千个周期或几毫秒
,甚至可能是操作系统cpu周期耗费的主要地方,所以多进程并不一定比单进程快
,在计算密集的场景下,频繁的进程切换反而会效率更差
异常
最初的进程切换是进程互相协调的,但很快发现这种模式容易导致恶意进程抢占运行资源,所以目前主流操作系统都使用竞争式进程抢占,由操作系统来负责进程之间的切换,这个机制便是异常
当触发异常时,比如读取文件、读取网络数据,或者其他硬件触发的中断,进程会从运行状态,转变为挂起、或者终止(由异常是否可以恢复决定),进程会在等待条件满足时,再次进入就绪态,等待下一次调度
fork & exec
进程可以fork一个子进程,子进程会复制父进程的内存空间,包括父进程的文件表,即子进程会默认打开父进程已打开的所有文件,这个要特别注意,而且操作系统为了提高性能,实现了写时复制
机制,即只有子进程的内存发生写入时才会触发内存复制
exec则是在当前进程的上下文环境中启动一个新进程来代替当前进程,在该进程执行结束之后再恢复之前的进程
守护进程
守护进程(daemon)就是一直运行的进程
web服务器就是一个守护进程,你登录服务器打开的对话窗口也是个守护进程,你写一个死循环的程序执行后也是守护进程(虽然是个垃圾)。所以守护进程最主要的特征是一直运行(直到被主动关闭)。排除掉进程出错导致退出的原因,导致进程退出的原因有:
- 进程出错,异常退出
- 进程收到非预期的SIGHUP信号,退出
后台任务
守护进程基本都在后台执行,这样不会占用命令行窗口。所以守护化的第一步便是让进程后台执行
-
test.sh &
,添加&
便可以让进程直接后台执行 - 对于已经前台执行的进程,需要先挂起
ctrl + z
然后再执行bg
(让最近一个挂起的任务后台继续执行,fg
恢复)
后台任务与前台任务的主要区别是,不再继承session的标准输入,所以除非后台进程标准输入被重定向,否则无法读取输入,而且会导致阻塞。但后台任务继承了seesion的标准输出和标准错误,所以进程的输出信息依旧会打印在会话窗口
守护化进程
- 关闭huponexit(默认即为关闭),则session进程退出后便不会向后台任务发送SIGHUP信号,后台任务就可以一直运行下去了
- 当huponexit为开启时,便需要disown命名,作用是既可以让后台任务忽略SIGHUP信号,也可以直接从会话组中踢出后台任务(交给1号进程),常使用的命令为
- disown,移出最近一个正在执行的后台任务
- disown -r,移出所有正在执行的后台任务
- disown -a,移出所有后台任务
- disown -h,不移出后台任务,但是让它们不会收到SIGHUP信号
- disown -h %2,根据jobId,移出指定的后台任务
需要注意的事,进程继承了session的标准I/O,一旦发生读写,会因为session已经退出而报错。所以需要事前重定向要守护化的进程的标准I/O
- 比disown更方便的是直接使用nohup,作用为
- 该进程忽略SIGHUP信号
- 关闭标准输入,该进程不再能够接受任何输入
- 重定向标准输出和标准错误到文件nohup.out
需要注意的是nohup并不会把进程变为后台任务,需要主动加上&
以上是将一个普通进程守护化,如果想在程序中直接实现以上功能,思路也一样,但需要使用编程语言来操作诸如后台执行(利用fork)、脱离进程组、重定向IO、忽略SIGHUP信号、改变工作目录、清理掩码。更为可靠则是父进程只负责在子进程退出后重新拉起,具体的程序逻辑则在子进程中执行
僵尸进程、孤儿进程
孤儿进程指父进程退出,仍在运行的进程,这些进程被移交给1号进程,比如前面的disown操作。孤儿进程基本没什么危害,等待其正常结束或者直接kill即可
僵尸进程则是该进程已经结束,但父进程未执行wait操作,导致内核依旧保留其退出信息,如果大量产生的话会导致内核资源耗尽,危害很大。解决办法是kill掉父进程,子进程便都移交1号进程回收
进程间通信
线程
线程是cpu调度的基本单位,其实每个进程至少要包含一个线程
同一个进程中的线程互相之间共享进程的内存空间
线程的切换成本主要是寄存器、计数器等,要远低于进程切换成本
线程安全
线程安全是指一个函数在多个线程同时调用的场景下,输出结果保持一致,反之则为线程不安全
线程不安全的函数可能是依赖了静态变量、全局变量,或者线程之间共享的进程变量没有加锁维护一致性
协程
简单小结一下
- 协程并非操作系统概念,内核无此概念
- 协程为用户级线程,线程自己负责协程的调度
- 适合I/O密集型场景
- windows下叫纤程
网络编程
由于一切皆文件的抽象,网络编程其实就是利用网卡这个特殊的文件进行编程,这个‘文件’字节流的交换是由多种网络协议与以太网来实现的
port端口
端口是操作系统对外提供服务的进程的抽象,每个提供服务的端口背后都对应一个进程
端口由一个整数表示,从0到65535,其中0-1023为著名端口
,这些端口会由一些基础服务占用;1024-49151为注册端口
,这些端口是建议给用户进程服务使用的;49152-65535则是动态端口
一般是临时分配的
socket套接字
网卡是用来进行网络数据交换的设备,维护着本地进程与外界大量的网络连接,socket套接字则是这些连接的抽象。socket由四个字段确定:本机ip、服务端口、连接ip、连接的端口,socket本质也是一个文件,记录了其缓冲区,排队进程队列
当本地进程是提供服务的进程时,端口号是固定的由进程申请bind。当本地进程是发起外界建立连接时,本地的端口是随机分配的
在实际编程中socket在不同语境下意思有所不同,系统调用socket返回的是“监听socket描述字”,他只有服务端ip+port;而accept函数返回的是“已连接的socket描述字”,包含服务端ip+port和客户端ip+port,定义了一个tcp连接
I/O多路复用
很多资料讲到这部分都是直接开怼socket、同步异步、阻塞非阻塞、select、poll,显得高深莫测。其实多路复用要解决的是这种场景:监听多个I/O设备,比如你需要监听多个文件的修改、实现一个网络服务器、监听多个socket文件,我们一步步开始
- 创建一个socket监听,bind了9000,轮询accept,建立连接、读取并处理数据。优点是简单,缺点是不断轮询也造成了cpu资源浪费
- 多路复用要登场了,把多个socket传给select系统调用,设置阻塞,这样当有socket活跃(新连接建立或已有连接数据进入)时select才会返回,此时再轮询即可。优点是不再需要定期轮询,缺点是
- select最大支持1024个socket
- 每次select被唤醒,都需要进行遍历才能获取活跃socket
- 因为select是系统调用,所以每次都要把所有socket参数复制进内核空间
- select的底层实现性能并不高,需要反复将进程加入/删除socket的就绪队列
- 用poll替换select,最大连接数不再受限
- 使用epoll。简单说就是把要监听的socket都丢到事件池中,然后注册事件触发时的回调函数,这样每当有socket活跃时,就会自动执行回调函数,非常高效,优点包括
- 连接数没有限制,理论上只受进程打开文件最大数限制
- 事件池可以避免socket的反复移除进入
- epoll维护了就绪列表,记录了就绪的socket,无需遍历
- epoll的底层实现避免了反复操作socket等待队列
其实epoll的出现大大降低了连接的维持成本,使得单台服务器可以很容易就解决C10K问题
至此,我们一步步的解决了大量连接I/O的复用问题,服务器可以保持大量的连接。但是,仍然有一个致命问题连接的处理是一个进程
,处理期间,其他连接只能等待,效率非常差。我们需要继续解决连接的处理性能问题
服务器模型
- 最简单的,来一个请求我就fork一个子进程进行处理。优点是简单,缺点是频繁的进程创建和销毁非常消耗系统资源。其实这就是最初的fork-execute模型
- 要避免进程频繁创建销毁,我们可设计一个master进程维护一个worker进程池,处理完任务后不退出。这就是leader-follower模型
- 服务器其实要解决的就两个问题,大量连接的维护问题和活跃连接的处理问题。使用epoll可以解决大量连接的维护问题,使用leader-follower可以解决请求处理问题,合起来就是reactor模型
真实研发中根据实际情况的不同,会使用线程、协程替代进程。可能有多个reactor或者多个master。编程语言可以自己维护服务器连接模型、请求处理模型,也可以由通用服务器如apache nginx维护
同步、异步 & 阻塞、非阻塞
在上面的总结中可以绕过了同步异步、阻塞非阻塞的概念。因为这几个概念就我查的资料来看,比较混乱。究其原因,我认为主要是大家预设的场景不同
同步、异步,比如前面说的网络模型,select我们认为是同步的,因为发起IO后,必须等待或者轮询,就绪后才能继续处理;epoll我们认为是异步的,因为我们提前注册了回调方法,发起IO就绪后系统会自动调用回调方法。但如果放在IO的场景,无论select还是epoll都是同步的,因为他们的区别只是识别活跃socket的性能不同,处理活跃socket仍然是需要同步阻塞进行recv的。除非使用AIO
阻塞、非阻塞,则比较好理解,就是发起调用后进程的状态,是否进入Blocked状态
综上,其实一直要解决的问题是如何降低活跃连接的发现成本(利用epoll减少数据移动、减少文件遍历),和如何降低请求处理的调度成本(复用进程减少创建成本、使用线程协程降低上下文切换成本),尽可能将机器资源用于请求处理,提高并发
延伸
如果这篇文章说不清 epoll 的本质,那就过来掐死我吧
高性能IO模型浅析
Linux IO模式及 select、poll、epoll详解
Vim
使用linux中必不可免的要进行文本操作,你有很多选择,但建议使用vim,因为linux默认安装,同时功能强大,这里总结一些常用操作
行内移动
- 0 移动到行首
- ^ 移动到行首第一个非空字符
- $ 移动到行尾
- g_ 移动到行尾最后一个非空字符
- w 移动到下一单词开头,e 移动到下一单词结尾,默认上来说,一个单词由字母,数字和下划线组成(陈皓注:程序变量)
- 如果你认为单词是由blank字符分隔符,那么你需要使用大写的E和W。(陈皓注:程序语句)
- % 括号匹配移动
- *、# 当前单词匹配移动,下一个/上一个
复制粘贴
- v 视图模式
- V 视图行模式
- y 复制选中部分
- yy 复制当前行
- p 粘贴至下一行
- P 粘贴至上一行
文件操作
- :e /file/path 打开文件
- :bn :bp 打开下/上一文件
- :saveas /file/save/as/path 另存为
编辑
- dd 删除当前行,并把删除的行存到剪贴板里
- 10ia [ESC] 会插入10个a
- . 重复最近一次编辑
- 3. 重复3次最近一次编辑
- ctrl+a 自增
块操作
- c-v 选择块区域,c-d向下移动,或者hjkl,或者%
- I 批量插入,按[ESC]生效
- J 将所选的行变为一行
- > < 缩进
- = 自动缩进
- gu gU 改变大小写
提示
插入模式下,c-n c-p 自动提示
分屏
- :split :vsplit 水平、垂直分屏
- c-w dir 方向,屏间切换
- c-w+/- 放大缩小屏
- c-w > < 改变宽度
- c-w|/_ 最大化垂直屏活水平屏
范围操作模式
操作 + 范围
y3j # 复制之后3行
d3j # 删除之后3行
gU3j # 之后3行转大写
常用配置
set enc=utf-8
set nowrap
set ts=4
set mouse=a
set nu
set relativenumber
set nobackup
syntax enable
:suspend 挂起
set paste 粘贴的时候不会错位
set nobomb|bomb|bomb? 删除bomb、添加、查看
范围操作
- cw 删除当前所在位置到word结束,并进入insert
- caw 删除当前所在位置的word头到尾,并insert
- ci" 删除" 里面的东西
- ca" 删除"里的东西,包括"
- yi"
- ya" ya(
常用工具与脚本
cat
# 不要忘了cat是可以同时显示多个文件的
cat file1 file2 ...
# 压缩空白行
cat -s file1
# 显示行号,针对所有行,-b则只针对非空白行
cat -n file1
find
# 未指明搜索条件,则列出dir目录及其子目录的所有文件
find dir
# 文件名称搜索,*为通配符,匹配任意多的字符,例子即为以Target开头的文件 -iname为忽略大小写
find dir -name "Target*"
# 文件类型搜索,f为文件,d为目录,l为符号链接,s为套接字,p为管道,b为块文件
find dir -type [fdlspb]
# 多个搜索条件,-o为'或' -a为'与'
find dir -name traget -[oa] -type d
# 指定搜索的最大|小深度,据不可靠消息,深度应该放在搜索条件的第一个,否则系统将搜索出来全部后再过滤层次
find dir -[maxdepth|mindepth] 2 -name 'Target'
# 使用正则表达式搜索,注意匹配的是find的输出结果,而不是如-name那样只匹配文件名
find dir -regex ".*\.jpg"
# 文件大小搜索,'+'为'大于','-'为'小于',不写为等于;
# 'b' for 512-byte blocks (this is the default if no suffix is used)
# 'c' for bytes
# 'w' for two-byte words
# 'k' for Kilobytes (units of 1024 bytes)
# 'M' for Megabytes (units of 1048576 bytes)
# 'G' for Gigabytes (units of 1073741824 bytes)
find dir -size [±]2[bcwkMG]
# 按照时间搜索,首先明白Linux文件系统每个文件都有三个时间戳
# 搜索的单位是天,-n指n天以内,+n指n天以前,无是等于,另外amin|mmin|cmin类似,但单位是分钟
# 访问时间(-atime): 用户最后一次访问文件时间
# 修改时间(-mtime): 文件内容最后一次修改时间
# 变化时间(-ctime): 文件元数据(权限或所有权,即inode)最后一次修改时间
find dir -atime|-mtime|-ctime [±]2
# 按照文件所有者搜索
find dir -user UNAME
# 按照文件权限搜索
find dir -perm 665
xargs
# 将标准输入转化为命令行参数,比如现在有这样的一个样本
cat args
1
2 2 2
3 3
4
# -I指定占位符
cat args | xargs -n 1 | xargs -I{} mv {} dir
sort
# sort是非稳定的,即除了排序的key外,每次排序的执行结果顺序可能不同
# 依次排序各个文件
sort file1 file2 ...
# 按照数字序对文件内容进行排序
sort -n file1
# 逆序
sort -r file2
# 假如file2中数据存在多列,-k指定使用第二列进行排序
sort -k 2 file1
# 以第二列中的第一个字符到末尾进行排序
sort -k 2.1 file1
# 以第二列中的第一个字符到第二个字符进行排序
sort -k 2.1,2.2 file1
uniq
# 将标准输入中的连续多行只输出一行,“连续多行”就决定了uniq处理的标准输入一般是排过序的,否则无意义
sort unsorted | uniq
# 只显示唯一的行
sort unsorted | uniq -u
# 同时输出行的重复次数
sort unsorted | uniq -c
split
# 以大小切分文件生成诸如`xaa xab xac ...`
split -b 10[c|k|m|g] Bigfile
# 同上,但是以行数切分
split -l 100 Bigfile
# -d选项表示使用数字作为生成文件的名字,如`x001 x002 x003 ...`
split -d -l 100 Bigfile
# 最后一个参数定义切分文件的前缀,如`mypre-001 mypre-002 ...`
split -d -l 100 Bigfile mypre-
# 合并切分文件的方法
cat mypre* > merge
dd
# if为输入文件,/dev/zero为linux特殊设备,会不断产生\0
# of为输出文件
# bs为块大小
# count为块数,即文件最终大小为 bs*count
dd if=/dev/zero of=junk.data bs=1[c|k|m|g] count=1
mkdir
# 用以创建长路径
mkdir -p /this/is/a/long/long/path
ln
# 为~目录创建一个名为home的软链接,可以通过`ls -l`查看,也可以使用`readlink`查看
ln -s ~ ./home
# 硬链接
ln ~ ./home
head & tail
# 显示前5行
head -n 5 file
# 显示后5行
tail -n 5 file
# 动态跟踪file变化,此外`-F`选项甚至可以在文件被重命名后依然跟踪
tail -f file
ls
# 列出详细信息
ls -l
# 隐藏文件
ls -a
# 显示目录本身的信息,而不是目录里的内容
ls -d Dir
wc
# 计算file的字节数、单词数、行数
wc -c|w|l file
chmod
# 直接修改
chmod 751 file
# 直接修改,但更加便于理解,a=all u=user g=group o=other w=write r=read x=execute
chmod u=wr,go=x file
# 在原基础上对权限进行修改
chmod u+w,g-r file
chown chgrp
# 修改文件的用户组为teachers
chgrp teachers file
# 修改文件的所有者为me,同时用户组修改为teachers
chown me:teachers file
grep
# 基本用法
grep 'php' file
grep "^a-z" file
# 使用拓展正则模式,此时无需转义
grep -E "^[0-9]{1,3}" file
# 一般用法,高亮显示
STDIN | grep MATCHPATTERN --color=auto
# 反向匹配
STDIN | grep -v MATCHPATTERN --color=auto
# 忽略大小写
STDIN | grep -i MATCHPATTERN
# 静默,常用于验证
STDIN | grep -q MATCHPATTERN
# 找出'test'在哪个文件中,`-L`则恰好相反
grep -l 'test' file1 file2 file3...
# 输出匹配行的上下文,A为After,B为Before,C为前后各
STDIN | grep test -A|B|C 3
# 以文件的每一行为模式进行匹配
grep -f patternfile file
cut
# 显示file的第一列,关于列的指定
cut -f 1 file
# 1,3,4 显示1、3、4列
# 1-4 显示1到4列
# 2- 显示第2列,直到最后
# -d选项指定分隔符
cut -f 1 -d ',' file
# -s选项用以去掉不含分隔符的列,比如文件中的注释、说明性文字
cut -f 1 -s file
# 显示file的前10个字符,类似的还有-b,显示的字节
cut -c 10 file
wget
# 最基本的用法,从网络中下载文件
wget url1 url2 url3
# -O选项指定输出结果的保存文件,-o选项指定wget执行过程中日志存储文件
wget url1 -O savefile -o logfile
# --limit-rate 限制下载速度,--quota(-Q)控制下载文件的大小
wget --limit-rate 20[k|m|g] url1 --quota 100m
# 需要http或ftp认证
wget --user username --password pass URL
curl
# curl是比wget更高级的http工具
# curl的基本用法,下载url,--slient表示静默,--progress显示进度条
curl [--slient|--progress] URL
# curl默认将下载的内容输出标准输出,-O则控制curl输出到文件,文件名则自动识别,比如 DOMAIN/index.html则下载至index.html;-o选项则指定文件名,此处也可以看出linux各个命令直接选项的混乱
curl URL [-O|-o newfile]
# 指定(多个)header
curl -H "Host: www.baidu.com" -H "Accept-language: en"
# 只显示响应报文中的头文件
curl -l|head URL
# 设置http中refer
curl --refer http://www.baidu.com URL
# 设置cookie,当使用cookie文件时`curl URL --cookie-jar cookiefile`
curl URL --cookie "user=liufuxin; pass=hack"
# 指定用户代理
curl URL --user-agent "Mozilla/5.0"
# 安全验证
curl -u user:pass URL
tar
# 打包文件,-f参数必须为参数组的最后一项,其后必须紧跟打包文件名
tar -cf demo.tar file1 dir1 dir2
# 提取打包文件,也可以指定提取的文件
tar -xvf demo.tar [file1|dir1|dir2]
# 排除通配符文件
tar --exclude '*.svn' -cvf demo.tar *
# 打包的同时压缩
tar -cv[j|z]f demo.[gz|bz2].tar *
gzip、bzip
# 只能对一个文件进行压缩,所以经常和打包命令配合,两者使用方法类似,bzip效率更好一些,解压缩命令为gunzip bunzip
# 生成text.gz同时保留原文件
gzip -c test > test.gz
# 列出压缩信息,包括压缩前后文件大小、压缩比率
gzip -l test.gz
# 压缩比,1最快(--fast) 9最好(--best)
gzip -[1-9|--fast|--best] test
# 列出压缩文件
gzcat test.gz
base64
# 编码test,输出到标准输出
base64 test
# 解码
base64 -d test
ping
# ping 10次
ping www.baidu.com -c 10
scp
# 远程递归复制文件夹至本地,反过来即复制到远程
scp -r user@host:/home/path/dir ./
lsof
# list open files,列出被打开的文件,因为Linux中‘一切皆文件’,所以该命令也可以查看打开的网络连接和端口
# 查看file文件被哪些进程占用
lsof file
# -r选项为一直执行losf
lsof -r file
# 显示abc进程打开的文件
lsof -c abc
# 显示PID 123打开的文件
lsof -p 123
# 显示文件描述符(fd)为4的文件被哪些进程打开
lsof -d 4
# 查看用户username的进程打开的文件
lsof -u username
# 46 IPv4 or IPv6
# protocol TCP or UDP
# hostname Internet host name
# hostaddr IPv4地址
# service /etc/service中的 service name (可以不止一个)
# port 端口号 (可以不止一个)
lsof -i[4|6] [protocol][@hostname|hostaddr][:service|port]
lsof -i tcp@ohaha.ks.edu.cn:ftp
lsof -i :3306
netstat
# 用于显示各种网络相关信息
# 列出所有端口
netstat -a
# 列出所有tcp端口,-u为udp
netstat -at
# 只显示监听端口
netstat -l
# 在输出中显示PID和进程名
netstat -p
# 不显示主机、端口和用户名,而用数字代替,可以加速输出
netstat -n
# 每隔一秒输出一次
netstat -c
du
# disk used,磁盘使用情况
# 基本用法,列出文件、目录的大小,单位字节,目录默认只显示其子目录,且显示一层
du file dir1 file2 dir2
# 递归显示所有
du -a dir
# 以可读单位显示,`-b -k -m`字节、kb、mb
du -h dir
# 只显示目录的总计,而不是子目录
du -s dir
df
# 列出磁盘空闲情况
df -h
time
# 列出命令执行时间
# real 开始执行到结束的时间
# user 进程在用户模式的时间,唯一真正用于执行进程所花费的时间(不包括阻塞、挂起)
# sys 花费在内核模式的时间,唯一真正使用CPU的时间
time COMMOND
watch
# 固定时间间隔监控命令输出
# 默认2秒执行一次,-n指定为5秒
watch -n 5 COMMOND
# 高亮显示区别
watch -d COMMOND
ps
# 列出所有进程信息,默认值显示当前终端进程,`-ax`效果类似
ps -e
# 显示更多信息
ps -e[f|l]
# 指定要显示哪些列,常用的有
# pid
# ppid
# pcpu cpu占用率
# pmem 内存使用率
# comm 可执行文件名
# user 启动用户
# stat 进程状态
# time 累计cpu时间
# 排序,-降序,+升序
ps -ef -o [TargetCol]
ps -ef --sort -pcpu,+time
# -L选项列出线程信息
ps -efL
kill
# 列出所有可用信号
kill -l
# 终止一个进程,默认发送的是SIGTERM信号(15),常用
# SIGHUP 1 挂起
# SIGINT 2 中断,ctrl+c
# SIGKILL 9 强制杀死
# SIGTERM 15 默认的终止进程
# SIGTSTP 20 ctrl+z
kill 13113
# 杀死进程名的所有实例,类似有`pkill`默认使用进程名
killall processname
which、whereis
查询工具,区别是which是按照PATH变量中的目录逐个查找
而whereis则是在Linux自己维护的文件数据库中查找,所以范围更大。缺点是非实时,数据库是每天自动更新,你也可以手动更新,updatedb
uptime
# 系统使用时间统计,包括最近1分钟、5分钟、15分钟的系统负载
uptime
uname
# 主机名,`hostname`
uname -n
# 打印内核、硬件架构等详细信息
uname -a
# 内核版本
uname -r
# 主机类型
uname -m
crontab
# 指定用户,root使用
crontab -u liufuxin -e
# */5 1,2 5-9 7 * COMMOND
# 每5分 每天的1,2点 每5-9日 每7月 执行任务
# 注意,周跟日月不能同时出现,同时出现不是'与'关系,而是'或'关系
# 注意,Commond的输出未指定的话会发送邮件
tee
# tee会将标准输入传送给某个设备的同时将其输出到标准输出
ls | tee lsresult | cat
umask
# 权限掩码,用于计算文件的默认权限
# 查看
umask
# 编辑
umask 022
# 作用
从777中拿走umask指定的位
# 注意
文件默认不允许添加执行权限,即即使设定umask为000,新建的文件权限也是666,执行权限必须chmod添加;文件没此限制
查看cpu
# 总核数 = 物理CPU个数 X 每颗物理CPU的核数
# 总逻辑CPU数 = 物理CPU个数 X 每颗物理CPU的核数 X 超线程数
# cpu基本信息
cat /proc/cpuinfo
# 查看物理CPU个数
cat /proc/cpuinfo| grep "physical id"| sort| uniq| wc -l
# 查看每个物理CPU中core的个数(即核数)
cat /proc/cpuinfo| grep "cpu cores"| uniq
# 查看逻辑CPU的个数
cat /proc/cpuinfo| grep "processor"| wc -l
top
# 实时监控系统cpu、进程、内存状态
# top命令之后
P 为cpu使用占比排序
M 为内存使用占比排序
i 使top不显示任何闲置或者僵死进程
# 进程状态
S 可打断阻塞,比如等待IO就绪
D 不可打断阻塞,比如正在读取文件
Z 僵尸进程
R 正在运行
T 处在被跟踪状态,如gbd调试
# top第三行显示当前系统的,其中有两个值很关键:
%id:空闲CPU时间百分比,如果这个值过低,表明系统CPU存在瓶颈;
%wa:等待I/O的CPU时间百分比,如果这个值过高,表明IO存在瓶颈;
环境变量
bashrc与profile都用于保存用户的环境信息,bashrc用于交互式non-loginshell,而profile用于交互式login shell。
/etc/profile,/etc/bashrc 是系统全局环境变量设定~/.profile,~/.bashrc用户目录下的私有环境变量设定
当登入系统获得一个shell进程时,其读取环境设置脚本分为三步:
- 首先读入的是全局环境变量设置文件/etc/profile,然后根据其内容读取额外的文档,如/etc/profile.d和/etc/inputrc
- 读取当前登录用户Home目录下的文件~/.bash_profile,其次读取~/.bash_login,最后读取~/.profile,这三个文档设定基本上是一样的,读取有优先关系
- 读取~/.bashrc
/.profile与~/.bashrc的区别:
- 这两者都具有个性化定制功能
- ~/.profile可以设定本用户专有的路径,环境变量,等,它只能登入的时候执行一次
- ~/.bashrc也是某用户专有设定文档,可以设定路径,命令别名,每次shell script的执行都会使用它一次
用户管理
# 添加用户,同时添加home目录
useradd -m liufuxin
# 设置密码
paswd liufuxin
# 删除用户 -r同时删除home目录
userdel -r liufuxin
awk
# 基本格式,BEGIN、END必须大写,可以省略
awk ’BEGIN {awk-commands} /pattern/ {awk-commands} END {awk-commands}‘ targt.file
# 打印所有行
awk '{print $0}' file
# 统计匹配的行数
awk '/a/{++cnt} END {print "Count = ", cnt}' marks.txt
Count = 4
延伸
三十分钟入门AWK
sed
# 首处替换,替换每一行的第一处匹配的text
sed 's/text/replace_text/' file
# 全局替换
sed 's/text/replace_text/g' file
# 移除空白行
sed '/^$/d' file
#变量转换,已匹配的字符串通过标记&来引用.
echo this is en example | sed 's/\w+/[&]/g'
$>[this] [is] [en] [example]
# 子串匹配标记,第一个匹配的括号内容使用标记 1 来引用
sed 's/hello\([0-9]\)/\1/'
延伸
Linux 系统性能分析的 60,000 毫秒
记一次PHP并发性能调优实战