Skip to content

Linux系统概念

Linux 系统概述

Linux,这个诞生于个人兴趣的开源操作系统,如今已成为服务器、嵌入式设备乃至个人电脑领域的主力军。其免费、开源、稳定、高效等特点,使其在全球范围内拥有庞大的用户群体。

什么是 Linux?

Linux 并非一个单一的软件,而是一个操作系统内核。这个内核负责管理系统硬件、进程调度、内存分配等底层任务。Linux 的开源特性使得全球开发者共同参与其开发,不断完善和创新。

Linux 的特点

  • 开源:任何人都可以自由获取、使用、修改和分发 Linux 的源代码。
  • 稳定性:Linux 在长时间运行下表现出极高的稳定性,适合用于构建可靠的服务器。
  • 安全性:相较于其他操作系统,Linux 的安全性更高,这得益于其开源的特性和社区的共同努力。
  • 可定制性:Linux 可以高度定制,以满足各种不同的需求。用户可以通过配置内核、安装不同的软件包来构建个性化的系统。
  • 社区:Linux 拥有全球最大的开源社区,提供丰富的文档、教程和支持。

Linux 的发展历史

从 Unix 到 Linux

Linux 的诞生受到了 Unix 操作系统的深远影响。Unix 的哲学、设计理念和工具集为 Linux 提供了坚实的基础。Linus Torvalds 在大学期间,出于对 Unix 的热爱和对当时商业操作系统的不满,决定开发一个属于自己的操作系统。

Linux 的主要版本和发行版

  • Linux 内核:Linux 内核是 Linux 系统的心脏,负责管理系统资源。主要版本有 2.x、3.x 和 4.x 等。
  • Linux 发行版:为了方便用户使用,Linux 社区衍生出了众多发行版,如 Ubuntu、CentOS、Fedora、Debian 等。每个发行版都有自己的特点和目标用户。

Linux 的核心概念

内核(Kernel)

  • 内核的作用:内核是系统与硬件之间的桥梁,负责管理系统的所有资源。
  • 内核态与用户态:内核运行在内核态,拥有最高的权限;用户程序运行在用户态,权限受限。
  • 系统调用:用户程序通过系统调用来请求内核执行特权操作。

Shell

Shell 的概念:Shell 是一个命令解释器,用户通过 Shell 与系统进行交互,对用户输入到终端的命令进行解析,调用对应的执行程序。

用户在终端输入命令,由shell命令解释器对命令进行解析(按照$PATH环境变量搜索命令),解析成内核能够识别的指令,然后由内核执行命令,最后由终端显示命令执行的结果给用户。

注意

shell在寻找命令的时候是按照$PATH环境变量去查找的,如果找到了就执行对应的命令,若找不到就报错,执行echo $PATH可以查看PATH环境变量的值。

常用的 Shell:Bash、Zsh、Fish 等。Bash 是最常用的 Shell,提供了强大的命令和脚本功能。

  • shell -- Bourne Shell

    shell
    /bin/sh
  • bash -- Bourne Again Shell

    shell
    /bin/bash

当前系统所使用的shell

shell
echo \$SHELL

当前系统下有哪些shell

shell
cat /etc/shells

切换shell

shell
chsh -s shell的路径
# 例如
chsh -s /bin/zsh

文件系统

文件系统的概念:文件系统是组织和管理文件的方式。 Linux 常用的文件系统:ext4、XFS、Btrfs 等。ext4 是目前最常用的文件系统,具有良好的性能和可靠性。

用户管理

用户和组:Linux 系统中的用户和组用于控制对系统资源的访问权限。 用户管理命令:useradd、userdel、passwd 等。

进程管理

进程的概念:进程是正在运行的程序。 进程管理命令:ps、top、kill 等。

Linux 的基本架构

Linux 系统的层次结构

Linux 系统可以分为内核层、系统调用层、库函数层和用户程序层。

Linux 的启动过程

Linux 系统的启动过程大致分为 BIOS 加载、Bootloader 加载内核、内核初始化、启动用户界面等几个阶段。

Linux 与其他操作系统的区别

Linux 与 Windows 的对比:Linux 开源、稳定、安全,而 Windows 商业化、易用。 Linux 与 macOS 的对比:Linux 和 macOS 都基于 Unix,但 Linux 更开放,macOS 更封闭。

环境变量

环境变量:存储在操作系统中的全局键值对(key-value),用于定义进程的运行环境,影响应用程序的行为。

作用域

  • 全局环境变量:对所有用户和进程有效(如 /etc/profile
  • 用户环境变量:仅当前用户生效(如 ~/.bashrc
  • 临时环境变量:仅当前终端会话有效

核心作用

环境变量作用场景典型值示例
PATH程序搜索路径/usr/bin:/home/user/bin
HOME用户主目录/home/user
LD_LIBRARY_PATH动态库加载路径/usr/local/lib
LANG语言环境设置en_US.UTF-8
PWD当前工作目录/home/user/project

操作命令(shell层面)

  • 查看所有环境变量
    bash
    env  # 显示所有环境变量
    printenv  # 同上
  • 查看特定变量
    bash
    echo $PATH  # 输出PATH变量值
  • 临时设置变量
    bash
    export MY_VAR="value"  # 全局环境变量
    MY_VAR="value"         # 仅当前shell有效
  • 永久设置变量
    bash
    echo "export MY_VAR=value" >> ~/.bashrc  # 用户级
    sudo echo "export MY_VAR=value" >> /etc/profile  # 系统级
    source ~/.bashrc  # 使配置生效
  • 删除变量
    bash
    unset MY_VAR

在C/C++程序中访问

cpp
#include <cstdlib>
#include <iostream>

int main() {
    char* path = std::getenv("PATH");
    if (path) {
        std::cout << "PATH: " << path << std::endl;
    }
    return 0;
}

常见应用场景

  • 配置搜索路径:将自定义程序目录添加到PATH
  • 动态库依赖:设置LD_LIBRARY_PATH解决找不到.so文件的问题
  • 语言国际化:通过LANG控制字符编码
  • 脚本开发:在shell脚本中引用环境变量(如$HOME

注意事项

  • 变量名区分大小写PathPATH
  • 优先级:用户变量 > 系统变量
  • 安全性:避免在环境变量中存储敏感信息(如密码)
  • 继承性:子进程会继承父进程的环境变量

扩展知识

  • 环境变量与shell变量的区别
    • 环境变量可被子进程继承,shell变量仅限当前shell
    • 环境变量通过export声明
  • 环境变量配置文件
    • /etc/profile(系统级全局)
    • ~/.bash_profile(用户登录时加载)
    • ~/.bashrc(交互式shell加载)
  • 特殊变量
    • $0:当前脚本名称
    • $?:上一条命令的退出状态码
    • $$:当前进程ID

"环境变量是Linux系统中配置运行环境的核心机制,通过定义全局或局部的键值对,控制程序的搜索路径、语言环境、资源依赖等。掌握环境变量的查看、设置、删除方法,以及在C++程序中通过getenv访问,是Linux开发的必备技能。在实际应用中需注意变量作用域、继承性和安全性,合理利用环境变量提升开发效率。"

总结

Linux 作为一款功能强大、灵活可定制的操作系统,在服务器、嵌入式系统、云计算等领域有着广泛的应用。学习 Linux 不仅可以提升计算机系统的理解,还可以掌握更多强大的工具,为未来的发展打下坚实的基础。

拓展阅读

Linux下常用快捷键

tab键的作用

  1. 补齐命令

    如:在终端输入his然后按tab键,会补齐history命令;

    如:输入l然后按tab键,会显示所有以l开头的命令.

  2. 补齐文件(包括目录和文件)

    例如: 如果在执行ls,然后按tab键,会显示当前目录下所有的文件

主键盘快捷键

遍历输入的历史命令

  • 从当前位置向上遍历:ctrl + p(
  • 从当前位置向下遍历:ctrl + n(

注意:使用history命令可以显示用户输入的所有命令。

光标位置移动

  • 光标左移: ctrl + b (
  • 坐标右移: ctrl + f (
  • 移动到头部: ctrl + a(home
  • 移动到尾部: ctlr + e(end

字符删除

  • 删除光标前边的字符:ctrl + h(Backspace)

  • 删除光标后边的字符:ctrl + d

    光标后边的字符即光标覆盖的字符,例如

    执行该命令,删除的是字符W

  • 删除光标前所有内容:ctrl + u

  • 删除光标后所有内容:ctrl + k

man手册

在使用Linux系统及进行Linux系统编程的时间,有任何不懂的地方都可以查阅man手册进行学习。使用命令man man可以阅读man手册的帮助文档

man手册的内容

该手册分为多卷:第一卷是用来查看 shell 命令的;第二卷是用来查看系统调用相关信息的;第三卷是用来查看 库函数信息的...

shell
1 可执行程序或 shell 命令
2 系统调用(内核提供的函数)
3 库调用(程序库中的函数)
4 特殊文件(通常位于 /dev)
5 文件格式和规范,如 /etc/passwd
6 游戏
7 杂项(包括宏包和规范,如 man(7),groff(7))
8 系统管理命令(通常只针对 root 用户)
9 内核例程 [非标准]

man 命令的格式如下:

shell
$ man [手册编号] cmd

比如,我们可以这样用:

shell
$ man man
$ man 3 mkdir

进入帮助界面后,我们可以按下面按键浏览帮助信息:

shell
d(down): 往下翻半页
u(up): 往上翻半页
f(forward): 往下翻一整页
b(backward):往上翻一整页
q(quit): 退出

如果遇到查询库函数查询不到的情况需要先,安装以后就可以进行查看了

shell
sudo apt install manpages-posix-dev -y

Tips

我们还可以查看指定语言的man手册条目,在这之前要先确定我们系统中已经安装的语言

bash
locale -a

# 输出示例
  ~ locale -a
C
C.utf8
POSIX
en_US.utf8
zh_CN.utf8
  ~

我们可以使用以下命令来指定要查看哪种语言的man手册条目

bash
man -Lzh_CN man   # 中文
man -Len_US man   # 英文
man -Len man   # 英文

如果是未知语言,会自动fallback到系统默认语言。其实前面的locale -a只是起到确定-L参数的值的作用,不在里面的其实也可以,只要安装了对应的man pages,比如意大利语: man -Lit man

man手册阅读步骤

  1. 先阅读名字

  2. 看声明和返回值

    • -->
    • -->
  3. 细节 按需查看

在线man手册

结语

本文仅对 Linux 系统的概念进行了初步的介绍,Linux 的世界博大精深,还有很多值得探索的领域。希望本文能激发你对 Linux 的兴趣,鼓励你深入学习。

面试题

内核态和用户态的区别是什么?

内核态和用户态的区别主要体现在三个方面:

  1. 权限:用户态程序受限,无法直接操作硬件;内核态拥有最高权限。
  2. 内存访问:用户态仅能访问用户空间,内核态可访问全部地址。
  3. 切换机制:通过系统调用、中断或异常触发切换。

以C++的new操作为例,常规分配在用户态完成,但内存不足时会触发内核态的系统调用。在网络编程中,频繁切换会带来性能损耗,可通过零拷贝技术优化。理解这些机制有助于设计高性能后端系统。

Details

在C++后端开发岗位的面试中,回答“内核态和用户态的区别”需要从底层原理、实际编程场景和性能优化等多个维度展开,以下是系统性回答建议:

一、核心区别与原理

  1. 权限级别
    用户态:应用程序运行时的受限模式,CPU只能执行非特权指令(如普通运算),无法直接访问硬件设备或内核数据结构。例如,用户态程序无法直接操作网卡或磁盘,必须通过系统调用委托内核完成。
    内核态:操作系统内核运行的最高权限模式,CPU可执行所有指令(包括特权指令),能直接访问内存、硬件设备(如修改页表、响应中断)。例如,处理缺页异常时,内核会直接修改进程的内存映射表。

  2. 内存访问范围
    • 用户态仅能访问用户空间(如Linux的0x00000000-0xBFFFFFFF),而内核态可访问全部虚拟地址空间(包括内核共享的3GB-4GB区域)。

  3. 切换触发机制
    系统调用:用户程序主动请求内核服务(如文件读写、网络通信),通过syscall指令触发软中断(如x86的int 0x80)。
    硬件中断:外部设备触发(如网卡收到数据包),CPU暂停用户态进程,执行中断处理程序。
    异常:执行非法操作(如除零错误、页缺失)时自动切换到内核态处理。

二、C++开发中的实际场景

  1. 动态内存分配(new操作符)
    • 常规内存分配(如小对象)由用户态的内存池(如malloc管理的堆)完成,无需切换内核态。
    • 当用户态堆内存不足时,会通过brkmmap系统调用扩展虚拟内存,此时触发内核态切换。例如分配超大数组int* arr = new int[1e9]可能触发此过程。

  2. 网络编程优化
    • 频繁的read/write系统调用会产生切换开销,高性能场景可通过零拷贝技术(如sendfile)或用户态协议栈(如DPDK)减少切换次数。

三、回答加分点

  1. 切换成本分析
    • 上下文切换需保存寄存器、程序计数器等状态,并切换内核栈,通常需要数百到数千CPU周期。在IO密集型系统中,频繁切换可能成为性能瓶颈。

  2. 优化策略
    批量处理:合并系统调用(如writev替代多次write)。
    内存池设计:预分配大块内存减少内核态切换(参考tcmalloc/jemalloc原理)。

系统调用和库函数的区别是什么?

系统调用是操作系统内核提供的底层接口(如open()),需切换到内核态执行,直接操作硬件但开销大;库函数(如fopen())运行于用户态,封装了系统调用以提升易用性,适合高频或跨平台场景。两者在权限、性能、可移植性上各有优劣,实际开发中常结合使用。例如,printf()最终通过write()输出,但添加了缓冲优化。

Details

系统调用(System Call)与库函数(Library Function)是程序与操作系统交互的两种不同机制,它们在功能、执行层级、性能开销等方面存在显著差异。以下是两者的核心区别与联系:


一、定义与执行层级

  1. 系统调用
    定义:由操作系统内核提供的接口,允许用户程序请求内核服务(如文件操作、进程管理、硬件控制等)。
    执行层级:运行于内核态(Kernel Mode),需要从用户态切换到内核态,涉及中断和上下文切换。
    示例open()(打开文件)、fork()(创建进程)、write()(写入数据)。

  2. 库函数
    定义:由编程语言或第三方库提供的函数,封装了常用功能(如字符串处理、数学运算等),可能间接调用系统调用。
    执行层级:运行于用户态(User Mode),不涉及内核态切换。
    示例fopen()(文件打开)、printf()(格式化输出)、strcpy()(字符串复制)。


二、核心区别

对比维度系统调用库函数
权限与安全性直接访问硬件和内核资源,安全性由内核保障仅能访问用户空间资源,安全性较低
性能开销需用户态/内核态切换,开销较大(微秒级)无状态切换,开销小(纳秒级)
可移植性依赖操作系统内核,移植性差通过封装屏蔽底层差异,跨平台兼容性好
功能封装直接操作硬件或内核资源,功能单一可能整合多个系统调用,提供更高级的抽象(如缓冲I/O)

三、具体场景分析

  1. 文件操作
    • 系统调用:open()直接打开文件,返回文件描述符,无缓冲。
    • 库函数:fopen()封装了open(),并添加缓冲区管理,减少频繁读写开销。

  2. 网络通信
    • 系统调用:socket()创建套接字,send()发送原始数据。
    • 库函数:printf()可能调用write(),并处理格式化字符串。

  3. 性能权衡
    系统调用适用于需直接操作硬件的场景(如设备驱动开发)。
    库函数适合高频调用或需要跨平台的通用功能(如字符串处理)。


四、联系与协作

  1. 依赖关系:部分库函数通过封装系统调用实现功能。例如:
    fread()内部调用read()
    malloc()可能使用sbrk()调整堆内存。

  2. 互补性
    • 系统调用提供底层能力,库函数优化易用性(如缓冲机制)。
    • 开发者需根据场景选择:直接系统调用(高性能需求) vs. 库函数(开发效率)。


通过理解两者的差异与协作机制,开发者能更高效地平衡性能与开发复杂度。

系统调用和函数调用的开销

系统调用开销远高于函数调用,核心原因是用户态/内核态切换、权限检查、数据拷贝和上下文保存。单次系统调用耗时约200ns~15μs(是函数调用的几十倍),且涉及数千条CPU指令。优化手段包括缓冲批量处理(如writev)、零拷贝技术(如mmap)和异步I/O(如io_uring)。例如,高并发日志系统通过批量写入将系统调用次数降低90%。

Details

系统调用与函数调用的开销差异主要体现在用户态与内核态的切换、权限检查、上下文保存/恢复等环节。以下是两者的核心区别及开销分析:


一、开销差异的核心原因

  1. 用户态与内核态切换
    系统调用:需要从用户态切换到内核态,涉及CPU特权级别(如x86的Ring 3到Ring 0)和页表的切换。此过程需要保存用户态寄存器和栈信息,切换到内核栈,并触发中断处理程序(如int 0x80syscall指令),导致CPU流水线中断和缓存失效。
    函数调用:完全在用户态执行,仅涉及用户栈的压栈、跳转指令(如call),无需特权切换或上下文保存,CPU缓存命中率高。

  2. 权限检查与数据安全性
    系统调用:需验证参数有效性(如内存地址是否属于用户空间)、执行权限等安全机制,可能导致多次内存访问和条件判断。
    函数调用:无内核权限检查,参数传递和内存访问仅需用户态校验,效率更高。

  3. 数据拷贝与内存访问
    系统调用:输入/输出参数需在用户空间和内核空间之间拷贝(如read将数据从内核缓冲区复制到用户缓冲区),涉及额外的内存操作。
    函数调用:参数通过栈或寄存器直接传递,无跨空间拷贝开销。

  4. 中断与上下文切换
    系统调用:触发软中断或syscall指令,需保存/恢复完整的执行上下文(如SS、ESP、EFLAGS寄存器),导致CPU流水线刷新和TLB失效。
    函数调用:仅需保存返回地址和局部寄存器,无中断处理流程。


二、量化对比

  1. 时间开销
    系统调用:单次耗时约 200ns~15μs(具体因系统调用类型和硬件而异)。例如:空系统调用(如getpid)约200ns,带数据拷贝的系统调用(如read)可达微秒级。
    函数调用:单次耗时约 1~10ns,仅为系统调用的 1/20~1/100

  2. CPU指令数
    系统调用:单次需执行数千条指令(如权限检查、中断处理、数据拷贝)。实验显示,100万次read调用消耗约 10亿条指令
    函数调用:通常仅需几十条指令(如参数压栈、跳转、返回值处理)。

  3. 上下文切换开销
    系统调用:涉及用户态与内核态的完整上下文切换,单次消耗约 500~2000 CPU周期
    函数调用:无上下文切换,仅需处理栈帧变化。


三、优化策略(减少系统调用开销)

  1. 缓冲与批量处理
    • 使用缓冲区(如fopen代替open)或批量操作(如writev聚合多个write调用),减少调用次数。
    • 示例:高并发日志系统中,批量写入4KB数据而非单次写入小数据,可降低90%的系统调用次数。

  2. 零拷贝技术
    • 使用mmapsplice避免数据在用户空间与内核空间之间的冗余拷贝。

  3. 用户态替代方案
    • 利用vdso(Virtual Dynamic Shared Object)在用户态直接执行部分系统调用(如gettimeofday),避免切换内核态。

  4. 异步与非阻塞调用
    • 使用epollio_uring实现异步I/O,减少阻塞等待时间。


四、实际场景对比

场景系统调用函数调用
文件读写read/write需跨空间拷贝和权限检查fread/fwrite利用用户态缓冲减少调用
网络通信send/recv触发协议栈处理和软中断用户态协议栈(如DPDK)绕过内核开销
高并发日志系统频繁write导致CPU sys态占用40%+双缓冲队列+批量写入优化至5%以下