当前位置: 首页 > news >正文

关于 Linux

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

以下是一个 Windows 用户在试图转向 Linux 时所记下的笔记...

因为 Windows 非常完美地向用户隐藏了操作系统的几乎所有细节,所以 UP 在尝试接触 Linux 的时候才发现自己对操作系统的实现几乎一无所知。

本文基本都是对《现代操作系统 第三版》(此书翻译不佳,典型的直译而且存在低级错误,建议适当对照阅读) 和 《Linux 命令行与Shell脚本大全 第二版》 的整理摘抄。关于鸟哥的书,讲的是很细致啦,但是那个语速和腔调看的我很蛋疼所以 pass 掉了,看目录的话和这两本内容差不多。

这两本书加起来一千多页并没有全看,尤其是一些底层的细节。Shell 方面虽然也是一个大块,但因为我主要使用 Python,所以这里也只求了解一下基本的系统调用,能配合着手册看懂普通脚本就可以了。 <br /> ##操作系统

不论是操作系统还是网络系统甚至物流系统,处理复杂系统的一个通用方法就是抽象和分层。因此操作系统的根本目的可以简单概括为,封装底层核心态的操作,并向运行于此系统上的用户态程序提供系统调用接口。但具体的分层实现其实很灵活,比如某些基于微内核设计的类 UNIX 系统会把内存管理、文件系统这样一般被认为应当运行于核心态的进程,作为用户进程实现;而 Windows 则正相反地把 GUI 都放到了内核之中。

Linux 具有三层不同的接口:真正的系统调用(管理进程、文件系统等)接口,库函数(open、fork 等)接口和由标准应用程序(shell 等)提供的接口。

可移植操作系统接口(POSIX,Portable Operating System Interface,最后的 X 是为了读起来像 UNIX)是在 UNIX 的各种互不兼容的版本被开发出来后,由 IEEE 提出的一个 UNIX 标准,其定义了操作系统应当为应用程序提供的一系列库函数接口,从而使得应用程序在不同的 UNIX 系统间具有可移植性。

Linux 内核的版本号由 4 个数字组成,形如 A.B.C.D 。其分别代表:内核版本:主要修订版:次要修订版(比如增加了新驱动):BUG 修复与安全补丁

设计 Linux 的一个基本方针就是每个程序应该只做一件事并且把它做好。 <br /> ##文件系统

###文件 文件是由进程使用的数据单元,每个文件都可以看成是一个地址空间。操作系统中负责管理文件的部分称为文件系统。

Linux 中的文件可分为:普通文件、目录文件和设备文件。其中普通文件一般分为 ASCII 文件和二进制文件。文件系统除了保存文件名和数据外,还会保存一些附加信息,称为文件属性或元数据(metadata)。

###目录 目录在 Linux 中也是文件的一种,可以简单将其理解为一个容器结构,其中的元素为普通文件的指针(如 inode)。假设在一个文件系统中存在目录文件 \usr 和 普通文件 xx.py,那么将此普通文件放入该目录中的系统调用就称为链接ln),连接后,就可以通过 \usr\xx.py 这样的路径访问到普通文件。这种连接也称为硬链接( hard link)。当然也相应的存在 unlink 系统调用。

在 Linux 系统中,同一个文件可以链接到多个目录,文件的 inode(index-node)计数器属性负责记录指向该文件的连接数。当这个数值变为 0 的时候,该文件就等于是被删除了。这听起来和 Python 的垃圾回收器机制很像。不过这里也隐含了一个风险就是,如果一个文件的 inode 计数器值大于 1,那么想一般意义上的删除这个文件就麻烦了。(如果使用符号链接ln -s),则不存在这个问题。)

对于多磁盘(分区)环境下文件系统的管理问题,Linux 采用的方法是维护一个虚拟文件系统(VFS),具体操作为将所有磁盘都挂载(mount)到唯一的目录树上,这与 Windows 基于设备(盘符)的独立文件系统不同。

目录的删除操作仅能对空目录执行,但空目录也并不是完全意义上空的,任何一个目录都至少包含两个路径——...,分别指向当前目录和其父目录。

对目录的访问与普通文件类似,都需要先打开再访问,最后还记得关闭。不过目录有单独的系统调用命令,如 opendirclosedirreaddir 等。

###文件锁 为了解决对单一文件共享访问的冲突问题,POSIX 定义了一种机制,允许一个进程使用一个原子操作对文件(的一部分)加锁。加锁者需要指定要加锁的文件,开始位置和要加锁的字节数。系统提供了两种锁——共享锁和互斥锁,他们的区别就在字面上。在加锁时,进程还应当指定当加锁失败的时候,系统应当阻塞还是立即返回一个错误代码。

###日志 Linux 从 Ext3 起通过维护一个日志文件来增强文件系统的健壮性。日志的工作原理与数据库类似,都使用日志记录、原子操作与事务的概念。

###权限 用户对文件的访问权限,由一个 9 位二进制数标识,也可写作字母的格式。访问操作分为:读、写、执行三种,分别由 r w x 这三个字母标识(拒绝访问标识为 -);用户分为三类,分别是文件所有者、所有者所在的用户组(不含所有者)、除前两类外的其他人。这三类用户彼此为独立关系,互不包含。比如一个允许任何人读写的文件会被标识为 rw-rw-rw-

对于目录文件,x 位不代表执行,而是标识是否允许查询。

不过以上规则对 UID 为 0 的超级用户无效。

操作系统对当前用户的 UID 辨识来自于试图访问文件的进程,但进程携带的 UID 是可以通过名为 SetUID 的保护位来篡改的。如果一个可执行文件的权限被标记为 r-s--x--x,那么任何用户在调用该程序时,进程的有效 UID(effective UID)都会变成该可执行文件的拥有者的 UID。例如用于修改密码的 /usr/bin/passwd 程序就允许任何用户以 root 权限来执行。 <br /> ##I/O

从顶层来看,所有的 I/O 功能都整合在一个虚拟文件系统层中,这也便是 “在 Linux 里,一切设备都是文件” 这句话的意义所在。在编程上,可以类比为这样的表述: 该设备提供了一个类文件的访问接口。

从底层来看,所有 I/O 操作都实际作用于某一个物理设备。以设备驱动器的运作方式来区分的话,Linux 设备基本都可以分类为字符设备块设备。其主要区别在于,块设备把数据存储在固定大小的块中,而且每个块都有自己的地址,所有传输以一个或多个完整的(连续的)块为单位;而字符设备以字符为单位发送或接收字符流,字符设备是不可寻址的。因此块级设备允许查找操作和随机访问操作,而字符设备不行。

因为 Linux 允许多种文件系统的共存,所以在下图块设备列的文件系统与 I/O 调度器之间还存在一个通用块设备层,它负责为不同的文件系统提供一个统一的抽象。

【图】

典型的块设备如硬盘,硬盘的每张磁碟(的一面)按同心圆的格式划分为多条磁道,每条磁道又等分为多个扇形,称为扇区(sector)。一个扇区就是一个磁盘的 “块”,大小通常为 512 字节或 4K,扇区的格式包含三部分:前导码(preamble)、数据 和 ECC。其中前导码用于索引,ECC 则负责校验。给磁盘划分扇区以及分配扇区结构的操作就称为低级格式化。对于多碟(多面)硬盘,全部面上相同序号(位置)的磁道的组合称为 磁柱 或柱面(cylinder)。(实际上因为一般全部磁头都装在同一个机械臂上同步运动,所以通常是磁柱被默认为扇区的基本组织结构,而不是磁道。)

在低级格式化完成后,要对磁盘进行分区。逻辑上,每个分区就像是一个独立的磁盘。在 0 扇区的 MBR 中,除了引导程序外,还有一张分区表,分区表给出了每个分区的起始扇区和大小。分区完成后的下一步是给各个分区进行高级格式化,这一操作负责设置引导块、FreeList、根目录和空的文件系统,以及给分区表项添加一个代码以指示该分区使用的是何种文件系统。然后这块硬盘就可以使用了。

典型的字符设备如键盘,显然对其 100 多个按键编写索引并进行随机访问并没有什么实际意义。

这些特殊文件(设备文件),通常被放在 /dev 目录下,并由一个二元数组设备号来唯一标识。其中主设备号标识了该设备使用的驱动程序,而次设备号负责标识此驱动程序可能支持的多个设备实体。通常一个驱动程序只负责一种设备,但也有例外,比如对应于 /dev/tty 的驱动程序就同时控制着键盘和显示器,因为这两个设备的组合通常被认为是一种设备,即终端
<br /> ##Linux 的启动过程

  1. 计算机启动时,BIOS 加电自检(POST,Power_On_Self_Test),并对硬件进行检测和初始化
  2. 读取启动磁盘的第一扇区,即主引导记录(MBR),到内存中一个固定地址并执行
  3. MBR 中包含有一个很小的程序,该程序会从启动设备中调入一个名为 boot 的独立程序
  4. boot 将自己复制到一个固定的高端内存地址,从而为操作系统腾出低端内存的空间
  5. 复制完成后,boot 程序读取启动设备的根目录,这个过程一般是由 bootloader 完成的,如 GRUB
  6. 此时 boot 开始读入操作系统内核,并转交控制权。至此 boot 任务完成,系统内核开始运行

<br /> ##Shell ------------ 在乔布斯开启操作系统图形化界面的风潮之前,人与 UNIX 交互的唯一接口是 shell 提供的**文本命令行界面**(CLI,Command Line Interface)。CLI 只允许输入文本,而且只能显示文本和低级图形输出。在这种限制条件下,终端设备只需要一个显示器和一个键盘就够了。(正如上面 I/O 节尾提到的)

在几乎所有 Linux 发行版都自带图形桌面环境的当下,进入 shell 交互环境的方法一般有两种:一是改变系统启动级别,比如在级别 3 下,图形桌面程序根本就不会启动,直接进入命令行模式,这种方法一般在远程登录时好用;而如果是简单的本地登录,更好的进入 shell 的方法是在图形桌面环境下使用终端模拟器,就像 windows 下的 cmd.exe,好处是你可以同时打开很多个 shell。

当 shell 被启动时,它初始化自己,然后在屏幕上输出一个提示符(prompt),通常是 “%” 或 “$”,并等待用户输入命令行。多条命令间由 “;” 间隔。

关于命令和参数的问题。shell 会把“$”后的第一个单词当做可执行文件去运行,后面的是参数。参数前面可以选择是否加短横线。书上解释说,不加短横线的参数会被解释成字符串,如果是数字加了的就会被解释成数字。但我自己写了个 py 脚本看了下,发现这个短横线是当做参数的一部分传进来的,即对短横线的解释工作其实由脚本来完成。

命令行的结尾加一个 “&” 符号,就可以把本条命令行作为后台程序执行。

man 命令可以用来查看某条 shell 命令的相关手册,$ man bash 直接对 bash 使用 man 命令就可以查看全部手册页面,查看过程中可以用空格翻页,方向键导航,以及 “q” 键退出。 <br /> ##环境变量

bash shell 用一个称作环境变量(environment variables)的特性来存储有关 shell 会话和工作环境的信息。环境变量分为两类:全局变量和本地变量。其中全局变量对所有 shell 创建的子进程也是可见的,而本地变量仅对 shell 会话可见。

可以通过 $ printenv 命令查看当前的所有全局变量,其中系统变量为了与用户变量区分,变量名使用全部大写的格式。对所有全局变量 + 本地变量一起查看的命令为 $ set。对单个环境变量的查看可以使用 $ echo $HOME 命令,其中要查询的变量名前必须加 $ 符号。

设置环境变量的方法是使用简单的赋值语句:$ str=test$ str='hello world'$ num=32。需要注意的是:值可以是字符串或数字,但如果字符串中包含空格,就应当用单引号引用起来,否则空格会被解释为一条命令结束;出于同样的原因,等号左右也不能有空格。这种赋值方式创建的环境变量均为本地变量,创建全局变量的方法为对以创建的本地变量使用 $ export var 命令,这里 export 因为是环境变量专用的命令,所以变量名前不必加 $

删除环境变量的命令为 $ unset var,同样不加 $

###PATH 环境变量中最重要的一个是 PATH,它定义了一个用 “:” 分隔的 shell 查找命令的目录列表。通过在本地覆盖这个变量,可以方便的创建多个独立的运行环境。临时添加 PATH 的命令为:$ PATH=$PATH:. 这里将当前目录添加到了 PATH 里。

###配置文件 shell 在启动时默认添加的环境变量来自于一系列配置文件,而不同的启动方式下,shell 加载的配置文件也不尽相同。启动 shell 有三种方式:

  • 登录时当做默认登录 shell

    • /etc/profile;
    • $HOME/.bash_profile;
    • $HOME/.bash_login;
    • $HOME/.profile
  • 非登录时的交互式 shell

    • $HOME/.bashrc
  • 作为脚本运行的非交互式 shell

    • 通过 BASH_ENV 环境变量查找需要执行的启动文件(默认为空)

建议使用 cat 命令查看一下这些文件,他们添加环境变量的方式其实很简单。

一种常用的添加可执行命令的方法是在 usr/local/bin (此目录默认在 $PATH 中)添加一个软链接:

$ ln -s /Applications/Sublime\ Text.app/Contents/SharedSupport/bin/subl usr/local/bin/subl

这种方式与在 ~/.bash_profile 中添加 alias subl=/Applications/Sublime\ Text.app/Contents/SharedSupport/bin/subl 效果相同。

更近一步地,针对 Sublime Text, export EDITOR='subl -w' 可以将其设为 shell 默认编辑器。

转载于:https://my.oschina.net/lionets/blog/266244

相关文章:

  • 革新2410D开发板试用手记(三)
  • SQL Server 2005系列教学(9) SQL 存储过程
  • 华为配置SSH登陆详细步骤
  • 国内成长型中小企业已迈入ERP替换时代
  • 开始学习C基础
  • 爆强失恋诗
  • 依次从数据库中取固定大小的数据
  • 反恐精英之夺宝奇兵概要设计说明书
  • SQL 事务 [两种]
  • 一个人的战斗---走出软件作坊:三五个人十来条枪 如何成为开发正规军(十九)...
  • 广州景点助手 IOS APP 上线
  • 金融词汇Day Day Up —— 次贷危机
  • Monit - Linux系统进程、文件、目录和设备的监测软件
  • 十大安全建议 助您保护无线网络安全
  • 那些坑爹的BUG
  • (三)从jvm层面了解线程的启动和停止
  • canvas 五子棋游戏
  • iOS | NSProxy
  • java小心机(3)| 浅析finalize()
  • SQLServer之索引简介
  • storm drpc实例
  • Vue 动态创建 component
  • 复习Javascript专题(四):js中的深浅拷贝
  • 技术:超级实用的电脑小技巧
  • 开源中国专访:Chameleon原理首发,其它跨多端统一框架都是假的?
  • 我有几个粽子,和一个故事
  • 中国人寿如何基于容器搭建金融PaaS云平台
  • 【运维趟坑回忆录】vpc迁移 - 吃螃蟹之路
  • kubernetes资源对象--ingress
  • 积累各种好的链接
  • 容器镜像
  • ​Linux Ubuntu环境下使用docker构建spark运行环境(超级详细)
  • ​软考-高级-系统架构设计师教程(清华第2版)【第15章 面向服务架构设计理论与实践(P527~554)-思维导图】​
  • #pragam once 和 #ifndef 预编译头
  • #预处理和函数的对比以及条件编译
  • (145)光线追踪距离场柔和阴影
  • (4)通过调用hadoop的java api实现本地文件上传到hadoop文件系统上
  • (C++20) consteval立即函数
  • (附源码)springboot太原学院贫困生申请管理系统 毕业设计 101517
  • (欧拉)openEuler系统添加网卡文件配置流程、(欧拉)openEuler系统手动配置ipv6地址流程、(欧拉)openEuler系统网络管理说明
  • (一)基于IDEA的JAVA基础1
  • (转)Android中使用ormlite实现持久化(一)--HelloOrmLite
  • .net core 微服务_.NET Core 3.0中用 Code-First 方式创建 gRPC 服务与客户端
  • .NET Core、DNX、DNU、DNVM、MVC6学习资料
  • .NET 中小心嵌套等待的 Task,它可能会耗尽你线程池的现有资源,出现类似死锁的情况
  • .NET/C# 避免调试器不小心提前计算本应延迟计算的值
  • [Ariticle] 厚黑之道 一 小狐狸听故事
  • [cb]UIGrid+UIStretch的自适应
  • [CDOJ 838]母仪天下 【线段树手速练习 15分钟内敲完算合格】
  • [CF543A]/[CF544C]Writing Code
  • [CVPR 2023:3D Gaussian Splatting:实时的神经场渲染]
  • [IE 技巧] 显示/隐藏IE 的菜单/工具栏
  • [ListView.View=List]的垂直滚动条
  • [MAUI]集成高德地图组件至.NET MAUI Blazor项目
  • [Python从零到壹] 五十三.图像增强及运算篇之直方图均衡化处理