一、Linux基础捡漏

常用命令:
1、cd:改变当前路径到新的路径

	 cd ..                      #返回上一级
	 cd ../../                  #返回上上级
	 cd /home/user/Documents    #切换到指定路径

2、ls:查看目录中的文件

	 ls -l                  	#查看文件详情
	 ls -a                		#查看所有文件包括隐藏文件
	 ls -l /var/log    			#详细列出/var/log下的文件

3、rm:删除文件或目录

	 rm -r						#递归删除目录
	 rm -f						#强制删除
	 rm old_file.txt			#删除文件
 	 rm -r old_directory		#递归删除目录

4、mkdir:创建新目录,支持多级目录创建

	 mkdir new_folder			#创建单级目录
	 mkdir -p project/docs		#创建多级目录

5、chmod:修改目录或文件的权限

	 chmod 755 script.sh		#设置文件所有者可读写执行,其他用户可读执行
	 chmod +x script.sh			#为所有用户添加执行权限

6、tail:显示文件末尾内容,常用于监控日志文件

	 tail -n 					#指定行数
	 tail -f					#实时跟踪新内容
	 tail -n 10 log.txt			#显示文件最后10行
	 tail -f app.log			#实时跟踪日志更新

7、grep:在文件或输入中搜索文本模式,支持正则表达式,适合过滤内容

	 grep -i					#忽略大小写
	 grep -r					#递归搜索目录
	 grep "error" /var/log/syslog  #在syslog中搜索error
	 grep -r "function" src/    #递归搜索src目录下的所有文件

8、ps:显示当前进程的快照信息,如进程ID(PID),状态等

	 ps -aux 	#显示所有用户进程

9、top:实时显示系统进程动态,包括CPU,内存占用,类似任务管理器

	 top 		#启动实时进程监控(直接运行top,按q退出)

10、scp:(secure copy)通过ssh安全复制文件或目录到远程主机,加密传输

	scp file.txt user@remote:/home/user/	#复制文件到远程主机

11、ssh:(secure shell)远程登陆到另一台主机,执行命令或管理服务器

	 ssh user@192.168.1.100		#登录到IP地址为192.168.1.100的主机

12、rz:(receive zmodem)在终端接收文件(使用zmodem协议)

	 rz 						#运行rz,然后选择文件上传

13、sz:(send zmodem)发送文件到本地

	 sz file.txt

14、cat:(concatnate)连接并显示文件内容,也可用于查看文件或合并文件

	 cat file.txt								    #显示文件内容
	 cat file1.txt file2.txt > combined.txt			#合并文件

15、more:分页显示文件内容,支持向前翻页(不能向后)

	 more long_file.log								#分页查看文件

16、less:改进版more,支持前后翻页和搜索,更适合浏览大文件

	 less /var/log/auth.log							#查看日志文件

17、wc:(word count)统计文件行数,单词数和字节数

	 wc -l											#统计行数
	 wc -w											#统计单词数
	 wc -l data.csv									#统计文件行数

18、du:(disk usage)估算文件或目录的磁盘使用空间

	 du -h											#人类可读格式
	 du -s											#总计
	 du -sh /home									#显示/home目录的总大小

19、find

	find /var -name"*.log"							#在/var目录下搜索所有.log文件

二、SQL入门题

175.组合两个表
在这里插入图片描述

SELECT 
    p.FirstName,
    p.LastName,
    a.city,
    a.state
FROM Person p
LEFT JOIN 
Address a ON p.PersonID = a.PersonID

181.超过经理收入的员工
在这里插入图片描述

select e.name AS Employee
from Employee e,
     Employee m
where e.managerid = m.id
    and e.salary > m.salary;

182.查找重复的电子邮箱
在这里插入图片描述

select email
from person
group by email
having count(*) > 1

196.删除重复的电子邮箱
在这里插入图片描述

DELETE p1 
FROM Person p1, Person p2 
WHERE p1.email = p2.email 
AND p1.id > p2.id;

三、Python进阶,三个面试考点

1、GIL 锁的原理,为什么 Python 多线程不能跑满多核?
GIL(Global Interpreter Lock,全局解释器锁)是 CPython 解释器(Python 的默认实现)中的一个核心机制。下面我将逐步解释其原理,并说明为什么它导致 Python 多线程程序无法充分利用多核 CPU。

  • GIL 锁的原理
    • GIL 是一个互斥锁(mutex),它确保在任何时刻只有一个线程能执行 Python 字节码。这意味着,即使有多个线程在运行,只有一个线程能“持有”GIL 并执行 Python 代码。其他线程必须等待 GIL 被释放后才能继续执行。
  • 为什么需要 GIL?
    • CPython 的内存管理机制(如引用计数)不是线程安全的。如果没有 GIL,多个线程同时修改对象引用计数可能导致数据竞争或内存错误。例如,当线程 A 和线程 B 同时增加一个对象的引用计数时,可能发生竞争条件,引发程序崩溃。GIL 通过强制单线程执行字节码来避免这个问题2。
  • 工作流程:
    • 当一个线程开始执行 Python 字节码时,它必须先获取 GIL。
    • 执行一段时间(通常每 100 个字节码指令或基于时间片)后,线程会释放 GIL。
    • 其他线程竞争获取 GIL,获取成功的线程继续执行。
      在单核 CPU 上,这类似于线程轮流使用 CPU,不会显著降低性能。但在多核 CPU 上,GIL 成为瓶颈,因为它限制了并行性。
  • PYTHON 多线程不能跑满多核的原因
    • 在多核 CPU 上,Python 多线程无法充分利用所有核心,主要源于 GIL 的竞争机制

      • 核心问题:GIL 的竞争。在多核环境中,多个线程可以在不同 CPU 核心上同时运行。然而,GIL 是一个全局锁,只能被一个线程持有。因此:
        • 线程 A 在核心 1 上获取 GIL 并执行 Python 代码。
        • 线程 B 在核心 2 上即使空闲,也必须等待 GIL 被释放。
        • 这导致其他核心上的线程处于“闲置状态”,即使它们拥有 CPU 资源,也无法执行 Python 字节码。最终,程序本质上只有单线程在运行,无法实现真正的并行计算1。
    • 为什么优化困难?

      • GIL 与 CPython 的内存管理深度耦合。移除 GIL 需要重写整个解释器以支持线程安全的内存操作,这可能破坏现有生态。因此,在多核 CPU 上,Python 多线程更适合 I/O 密集型任务(如网络请求),而非 CPU 密集型任务(如数值计算)。
  • 总结:GIL 的原理是作为全局互斥锁保护内存安全,但它限制了 Python 多线程在多核 CPU 上的并行能力。解决方法是使用多进程(如 multiprocessing 模块)或替代解释器(如 PyPy),它们能绕过 GIL 实现多核利用。
    在这里插入图片描述

2、多进程 vs 多线程的区别,IO 密集型和 CPU 密集型分别用什么?

在并发编程中,多进程和多线程是实现高效程序执行的核心技术。它们各有优缺点,适用于不同场景。以下基于核心区别和场景需求,逐步解释您的疑问。所有分析均参考站内权威引用,确保内容可靠。

一、多进程与多线程的核心区别

  • 多进程和多线程在资源管理、执行方式和安全性上差异显著:
    • 资源分配与隔离:
      • 多进程:每个进程是独立的资源分配单位(如内存、CPU),拥有自己的地址空间。进程间数据隔离,通信需通过复杂机制(如管道、共享内存或Socket)34。
      • 多线程:线程是CPU调度的基本单位,属于同一进程的多个线程共享进程资源(如全局变量、文件描述符),但每个线程有自己的栈和寄存器。线程间通信更简单(通过共享变量),但需同步机制(如互斥锁)避免冲突34。
    • 性能与开销:
      • 多进程:启动和切换开销大,占用资源多(如内存),但能并行利用多核CPU(无GIL限制)。适合计算密集任务23。
      • 多线程:启动和切换开销小,更轻量级,但在Python等语言中受全局解释器锁(GIL)限制,无法真正并行(只能并发)。适合IO等待型任务24。
    • 安全性与容错:
      • 多进程:进程间隔离性强,一个进程崩溃不影响其他进程,安全性高34。
      • 多线程:线程共享资源,一个线程崩溃可能导致整个进程失败(尤其在OS线程模型为多对一时),需谨慎处理线程安全3。
    • 适用场景总结:
      • 多进程:CPU密集型计算(需多核并行)、需高隔离性的任务。
      • 多线程:IO密集型计算(需高效处理阻塞)、任务轻量且数目有限23。

二、IO密集型和CPU密集型场景下的选择

  • 根据任务类型(CPU密集型或IO密集型),选择多进程或多线程能显著提升效率:

    • CPU密集型场景(如科学计算、图像处理):
      • 特点:任务主要消耗CPU资源,计算量大,几乎无IO等待。
      • 推荐选择多进程:多进程可并行利用多核CPU,避免GIL瓶颈(如Python中),最大化计算效率。例如,处理大型矩阵运算时,多进程能将任务拆分到多个核心执行23。
      • 为什么不推荐多线程:GIL限制导致线程无法并行,只能轮流执行,无法发挥多核优势。
    • IO密集型场景(如网络请求、文件读写):
      • 特点:任务涉及大量等待(如IO操作),CPU经常空闲。
      • 推荐选择多线程或多协程:多线程:轻量级,线程切换开销小,能高效处理阻塞操作(如一个线程等待IO时,其他线程可继续运行)。例如,Web服务器处理并发请求时,多线程能快速响应多个客户端24。
      • 为什么不推荐多进程:进程启动和通信开销大,可能成为性能瓶颈,且IO等待无法通过并行加速。
  • 补充:在Python中,IO密集型任务还可用协程(如asyncio),它比线程更轻量(无切换开销),但实现复杂且库支持有限2。

三、总结与建议

  • 通用原则:

    • CPU密集型 → 优先多进程(利用多核并行)。
    • IO密集型 → 优先多线程(减少等待开销),协程为高级选项。
  • Python注意点:因GIL存在,多线程在CPU密集型任务中性能差;多进程通过multiprocessing模块实现,但需注意进程间通信成本34。

  • 实际应用:例如,视频编码(CPU密集型)用多进程;API调用(IO密集型)用多线程。
    在这里插入图片描述

3、装饰器的实现,写一个简单的计时装饰器练手

  • 装饰器(Decorator)是 Python 的强大特性,允许在不修改原函数代码的情况下增强函数功能。以下是实现步骤和计时装饰器示例:
  • 装饰器实现原理
    • 基本结构:装饰器是一个接受函数作为参数的高阶函数
    • 闭包特性:内部函数可以访问外部函数的参数(被装饰的函数)
    • 语法糖:使用 @decorator_name 简化应用
def decorator(func):          # ➊ 接收被装饰函数
    def wrapper(*args, **kwargs):  # ➋ 接收任意参数
        # 前置增强逻辑
        result = func(*args, **kwargs)  # ➌ 调用原函数
        # 后置增强逻辑
        return result
    return wrapper            # ➍ 返回增强后的函数

  • 简单计时装饰器实现
import time
from functools import wraps  # 保留原函数元信息

def timer(func):
    @wraps(func)  # 保留原函数属性
    def wrapper(*args, **kwargs):
        start = time.perf_counter()  # 高精度计时
        result = func(*args, **kwargs)  # 执行原函数
        elapsed = time.perf_counter() - start
        
        # 打印计时信息
        print(f"[{elapsed:.8f}s] {func.__name__}({args}, {kwargs}) -> {result}")
        return result
    return wrapper

  • 使用示例
@timer
def factorial(n):
    if n <= 1:
        return 1
    time.sleep(0.1)  # 模拟耗时操作
    return n * factorial(n-1)

print(factorial(5))

  • 输出示例
[0.00000120s] factorial((1,), {}) -> 1
[0.10015230s] factorial((2,), {}) -> 2
[0.20030580s] factorial((3,), {}) -> 6
[0.30045810s] factorial((4,), {}) -> 24
[0.40061050s] factorial((5,), {}) -> 120
120

  • 关键点说明
    • @wraps(func) 保留原函数的名称和文档字符串
    • time.perf_counter() 提供纳秒级计时精度
    • *args, **kwargs 确保装饰器适用于任意参数的函数
    • 递归函数计时会显示每次调用的独立耗时13
    • 此装饰器可用于性能分析、代码优化等场景,通过 @timer 即可为任何函数添加计时功能

四、算法热身

- 两数之和
提示:给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出和为目标值 target 的那两个整数,并返回它们的数组下标。
在这里插入图片描述

class Solution:
    def twoSum(self, nums, target):
        hash_map = {}
        
        for i, num in enumerate(nums):
            complement = target - num
            
            if complement in hash_map:
                return [hash_map[complement], i]
            
            hash_map[num] = i

更多推荐