-
从 Centos 切换到 Ubuntu
从 Centos 切换到 Ubuntu 1. 前言 Centos 是一个不错的发行版,但是它的各个版本都已经停止维护了。CentOS 7 于 2024 年 6 月 30 日正式停止支持,CentOS 8 更是早在 2021 年 12 月 31 日就停止维护了。而且后续学习中不支持部分软件版本,从长远使用、学习、生态等多方面考虑,将其切换到 Ubuntu 是不错之举。 其实从一开始就直接接触 Ubuntu 也是不错的选择,但这些都是后话了,各有优劣吧。从 Centos 切换到 Ubuntu 的朋友可以体验到不同系统的优劣,直接使用 Ubuntu 倒是可以省去一些小麻烦,但归根结底,二者基本可以做到无缝互通,命令大差不差,遇到不一样的简单查一下就行了。下面开始教学如何从 Centos 切换到 Ubuntu,基本上比较简单,少部分涉及 Centos 的一点基础。 2. 系统切换 到自己的云服务器厂商后台找到切换镜像,选择 Ubuntu 22.04 版本进行安装,几分钟后就会完成,注意完成后要重新设置密码! 重要提醒:切换系统前一定要备份好重要数据,系统切换会清空所有数据! 3. 使用 Xshell 进行连接 这里的操作和连接 Centos 一模一样,直接输入公网 IP 和对应的账密就能进行登录。 1. 创建普通账户 同样的,我们还是创建一个普通账户: adduser 用户名 紧接着会让我们设置和确认密码,正常输入就行。完了后就会发现和 Centos 的区别:它会让我们设置该账户的信息,比如全名、房间号码、工作电话、家庭电话等。为了方便可以一路回车表示默认,个人使用会比较方便快捷,当然,如果你想要进行设置也行。 2. 赋予 sudo 权限 关键步骤:创建用户后需要赋予 sudo 权限,否则无法执行管理员命令,这里有两个方法: 方法一: 和 Centos 一样,使用 root 账户执行命令:vim /etc/sudoers,按下 i 键进入插入模式,找到大约第 100 行左右的位置(附近会有 root ALL=(ALL) ALL 的字眼),在其下方添加以下内……
SE_Wang 2025-12-26
29 0 0 -
【Linux系统编程】(十七)揭秘 Linux 进程创建与终止:从 fork 到 exit 的底层逻辑全解析
在 Linux 操作系统的世界里,进程是资源分配与调度的基本单位,就像一个个忙碌的 工人,支撑着整个系统的高效运转。而进程的创建与终止,正是这些 “工人” 从诞生到完成使命离场的完整生命周期。其中,fork 函数是创建新进程的核心工具,exit、_exit 等函数则主导了进程的优雅退出。本文将带大家深入底层,详细拆解 Linux 进程创建与终止的每一个关键环节,让你彻底搞懂这背后的技术原理与实践技巧。下面就让我们正式开始吧! 一、进程创建:fork 函数的 “分身术” 1.1 fork 函数初识:一次调用,两次返回的神奇操作 在 Linux 中,要创建一个新进程,fork 函数是当之无愧的核心。它就像一台 “分身机器”,能让一个已存在的进程(父进程)复制出一个全新的进程(子进程),两个进程拥有相同的代码段、数据段(初始状态),却能各自独立运行,开启不同的执行旅程。 首先,我们来看 fork 函数的基本用法。它的头文件和函数原型如下(bash 环境中调用需借助 C 语言编译执行,后续代码案例均提供完整可运行方案): #include <unistd.h> pid_t fork(void); 光看原型可能觉得平平无奇,但 fork 函数有一个极具迷惑性的特点:一次调用,两次返回。这是什么意思呢?简单来说,父进程调用 fork 后,内核会完成一系列操作,最终父进程和子进程都会从 fork 函数返回,但返回值却截然不同: 子进程中,fork 返回 0; 父进程中,fork 返回子进程的 PID(进程 ID); 若调用失败,fork 返回 - 1。 为了让大家更直观地感受这个过程,我们来看一个完整的实战代码。先编写 C 语言代码文件 fork_demo.c: #include <stdio.h> #include <unistd.h> #include <stdlib.h> int main(void) { pid_t pid; printf("Before: pid is %d\n", getpid()); // 打印父进程PID // 调用fork创建子进程 if ((pid = fork()) == ……
SE_Wang 2025-12-25
11 0 0 -
Ubuntu20.04安装50系显卡驱动[不黑屏版本]
[硬件信息:AMD 9700X + 5070] [安装驱动有3种方法,本文是.run方法安装。有一种是直接在终端用命令ubuntu-drivers devices就能安装,比较快捷,但版本更新比较慢,我就想用.run文件这种方法安装,失败了起码4次,困扰了我一天的问题😭,踩了很多坑终于解决了!!!😎] 前期工作: 进入BIOS禁用安全模式! 1.下载驱动文件 1)去Nidia官网下载对应显卡的驱动:下载 NVIDIA 官方驱动 | NVIDIA 2)下载.run格式的驱动文件 3)把.run文件存到没有中文的文件路径中,这是关键的一点。 2.查看安装的是gdm3还是lightdm 1)输入下面命令查看输出,为进入tty2模式做准备: cat /etc/X11/default-display-manager 2)我的输出: /usr/sbin/gdm3 3.禁用nouveau驱动 1)编辑: sudo gedit /etc/modprobe.d/blacklist.conf 2)在文件末尾添加以下两行,然后保存退出: blacklist nouveau options nouveau modeset=0 3)更新initramfs: sudo update-initramfs -u 4)重启: sudo reboot 4.关闭图形界面 1)按下 Ctrl + Alt + F2 进入TTY命令行界面。 2)输入用户名和密码登录。 # 若出现菱形图案,输入以下解决 export LANG="UTF-8" export LANGUAGE="UTF-8" 3)根据步骤2的结果执行以下命令之一关闭图形界面: sudo systemctl stop gdm3 # 或者 sudo systemctl stop lightdm 5.给安装的.run文件赋予执行权限 1)找到你下载的驱动文件: 如我的存放在根目录了。 2)给驱动文件赋予执行权限: cd / sudo chmod +x NVIDIA-Linux-x86_64-570.169.run 6.执行安装 sudo ./NVIDIA-Linux-x86_64-570.169.run 7.安装过程的选项(非常重要!!!选错了可能会黑屏) 1)Multiple kernel module types are available for this system. Which would you like to use? 务必选择 MIT Licensed Open-Source Driver(MIT开源版) (……
SE_Wang 2025-12-24
19 0 0 -
【Linux系统编程】(十)从入门到精通!Linux 调试器 gdb/cgdb 超全使用指南,程序员必备调试神器
前言 作为 Linux 下 C/C++ 开发的核心工具,gdb 调试器是排查代码 bug、理解程序运行流程的必备技能。很多新手面对黑屏命令行调试望而却步,甚至资深开发者也可能只掌握基础用法。而 cgdb 作为 gdb 的增强版,更是解决了纯命令行调试看不到代码的痛点。本文将结合实战案例,从基础配置到高级技巧,全面拆解 gdb/cgdb 的使用方法,让你彻底掌握 Linux 下的调试精髓!下面就让我们正式开始吧! 一、gdb 调试基础:从环境准备到核心概念 1.1 为什么需要 gdb 调试? 在开发过程中,我们难免会遇到代码逻辑错误、变量取值异常、程序崩溃等问题。printf 打印调试虽然简单,但存在诸多局限:需要手动添加打印语句、重新编译,无法实时观察变量变化,也难以定位崩溃点。而 gdb 调试器可以直接加载可执行程序,支持断点设置、单步执行、变量监视、堆栈查看等功能,让你像 "上帝视角" 一样看透程序运行的每一个细节。 1.2 gdb 调试的前提:编译调试版本 Linux 下 gcc/g++ 默认生成的是 release 版本程序,不包含调试信息,无法使用 gdb 调试。因此,必须在编译时添加-g选项,生成包含调试信息的 debug 版本。 示例代码(mycmd.c): #include <stdio.h> int Sum(int s, int e) { int result = 0; for(int i = s; i <= e; i++) { result += i; } return result; } int main() { int start = 1; int end = 100; printf("I will begin\n"); int n = Sum(start, end); printf("running done, result is: [%d-%d]=%d\n", start, end, n); return 0; } 编译命令对比: # 默认release版本,不支持gdb调试 gcc mycmd.c -o mycmd file mycmd # 查看程序信息,输出"not stripped"但无debug_info # debug版本,添加-g选项,支持gdb调试 gcc mycmd.c -o mycmd -g file mycmd # 输出"with debug_info, not stripped",表明包……
SE_Wang 2025-12-23
27 0 0 -
【LInux】进程程序替换与shell实现:从fork到exec的完整闭环
进程程序替换与shell实现:从fork到exec的完整闭环 💬 欢迎讨论:这是Linux系统编程系列的第六篇文章。在前五篇中,我们学习了进程的创建(fork)、状态管理和资源回收(wait/waitpid)。但fork出的子进程只能执行父进程的代码副本,如果我们想让子进程执行一个全新的程序,该怎么办?这就是本篇要深入讲解的进程程序替换技术。更重要的是,我们将把fork、exec、wait三大核心技术结合起来,实现一个真正的命令行解释器! 👍 点赞、收藏与分享:这篇文章包含了大量原理分析和一个完整的shell实现,如果对你有帮助,请点赞、收藏并分享! 🚀 循序渐进:建议先学习前五篇文章,理解fork、进程状态和wait机制,这样学习本篇会更轻松。 一、进程程序替换 1.1 为什么需要程序替换 在学习程序替换之前,我们先思考一个问题:fork创建的子进程有什么局限性? 让我们回顾一下fork的行为: int main() { printf("父进程开始\n"); pid_t id = fork(); if(id == 0) { // 子进程执行的还是父进程的代码 printf("我是子进程\n"); } else { printf("我是父进程\n"); } return 0; } fork后,子进程获得了父进程的代码副本,它执行的仍然是父进程程序的代码。虽然我们可以通过if-else让父子执行不同的代码分支,但本质上它们运行的是同一个程序的代码。 那么问题来了:如果我想让子进程执行一个完全不同的程序,比如执行ls命令,该怎么办? 这时就需要**程序替换(Program Replacement)**技术。 1.1.1 shell如何执行命令 让我们看一个日常操作: $ ls -l total 64 -rwxr-xr-x 1 user user 8960 Dec 10 10:30 a.out -rw-r--r-- 1 user user 256 Dec 10 10:25 test.c 当你在shell中输入ls -l时,发生了什么? shell(bash)是一个进程,它读取你输入的命令 shell调用fork()创建子进程 子进程调用exec加载ls程序 子进程开始执行ls的代码……
SE_Wang 2025-12-22
15 0 0 -
《从内核视角看 Linux:环形缓冲区 + 线程池的生产消费模型实现》
【一】环形生产消费模型介绍 “环形”生产消费模型:队列采⽤数组模拟,⽤模运算来模拟环状特性,例如: 特点: (1)缓存大小初始后是固定的 (2)也符合数据“先进先出”的特点 (3)通过首尾下标来访问数据。Head:只要有空位置就可以一直存放数据 Tail:只要有数据就可以一直获取数据 而位置是否有数据我们可以根据信号量(引用计数,本质为临界资源数量)判断! (此模型较于普通的“生产消费模型”可以根据信号量做到并发执行,信号量代表资源数量) 为什么信号量不用担心两个线程同时获取一个信号量? 核心原因是sem_wait操作的原子性—— 它将 “检查信号量值” 和 “修改信号量值” 封装为一个不可分割的步骤,确保任何时刻只有一个线程能成功获取资源 例如:核心思想就是“连续”,不给其它线程可乘之机 原子操作的关键是 “没有中间状态”: 信号量值要么是 1(没被拿),要么是 0(被拿了),不会出现 “0.5” 这种中间值; 线程要么拿到资源(信号量值变 0),要么没拿到(阻塞),不会出现 “两个线程都认为自己拿到了” 的情况。 这就是为什么哪怕两个线程 “同时” 抢最后一个信号量,也绝对不会同时拿到 —— 原子性把 “争夺” 变成了 “排队”,先到先得 【二】信号量使用 (1)创建信号量对象 原型: sem_t 对象变量名; (2)初始化信号量 原型: #include <semaphore.h> int sem_init(sem_t *sem, int pshared, unsigned int value); 参数: sem:指向信号量对象的指针(需提前声明,如sem_t empty_sem;) pshared:0 表示信号量用于线程间同步(环形模型必选);非 0 表示用于进程间同步(不常用) value:信号量初始值(表示可用资源的初始数量) 作用:初始化一个信号量 (3)信号量等待 原型: int sem_wait(sem_t *sem); 参数:指向目标信号量对象的指针 作用:尝试获取信号量资源(将信号量值减 1) 若当……
SE_Wang 2025-12-19
11 0 0 -
Linux 进程深度解析(一):从内核视角看懂进程的本质
在 Linux 系统中,我们每天都在和进程打交道 —— 执行 ls查看文件、用 top监控系统、启动应用程序,这些背后都是进程在工作。但你真的懂进程吗?课本说 “进程是程序的执行实例”,但内核视角下的进程远比这复杂。 这篇文章将带你跳出教科书式的抽象概念,用更贴近底层的视角、更通俗的比喻和更实际的命令,让你一次性看透 Linux 进程的本质。 一、先破误区:进程不是 “运行的程序” 那么简单 很多人对进程的理解停留在 “程序跑起来就是进程”,这个说法没错,但只触及了表面。 从用户视角看,执行./myapp或双击 QQ 图标,就是启动了一个进程。但从 Linux 内核的视角来看,它要管理的不是 “程序”,而是进程的资源和状态。CPU 该给谁用?内存该分配多少?进程在等什么资源?这些都需要一个精确的 “账本” 来记录。 所以,一个更准确的定义是:进程 = 内核数据结构(PCB) + 程序的代码与数据。 程序(如磁盘上的/bin/ls文件):是静态的,只是一堆二进制指令和数据,没人管它,它就静静地躺在那里。 进程:是动态的,当内核决定运行一个程序时,会为它创建一个专属的 “管理档案”——PCB(进程控制块),并把程序的代码和数据加载到内存。此时,它才成为一个能被内核调度、有生命周期的 “活物”。 二、拆解进程的两大核心组成 如果把进程比作一个 “项目团队”,那么 PCB 就是 “项目经理”,代码和数据则是 “执行任务的工程师”。两者缺一不可。 2.1 PCB:进程的 “全能管理档案” PCB 在 Linux 内核中是task_struct结构体,它是进程的灵魂,记录了内核管理进程所需的一切。我们可以把它想象成一张精密的 “身份信息表”,包含以下几类核心信息: 分类 核心信息 通俗解释与举例 标识类 PID(进程 ID)、PPID(父进程 ID)、UID(用户 ID) “你是谁,从哪来”。PID 是进程的唯一身份证号;PPID 记录了谁创建了它(父子关系);U……
SE_Wang 2025-12-18
14 0 0 -
Linux:多线程---深入互斥&&浅谈同步
线程分离: pthread_detach函数,可以是线程组内其他线程对目标线程进行分离,也可以是线程自己分离,分离后的线程不可被等待,如果强行等待也会返回错误码22。 问题一:为什么要有线程分离呢? 如果不关心线程的返回值,join是一种负担,这个时候,我们可以告诉系统,当线程退出时,自动释放线程资源。 归根结底,我们让线程分离,其实就是更改线程的原生线程库里的tcb内的分离的属性,而pthread_join就是识别到了该分离属性被更改为已分离,所以才会直接返回一个错误码。 1. 互斥 1.1 为什么需要互斥 多线程抢票模型代码演示: #include<iostream> #include<unistd.h> #include<pthread.h> #include<string> #include<vector> using namespace std; #define NUM 4 int ticket =100;//用多线程,模拟一轮抢票 class ThreadData { public: ThreadData(int number){ _thread_name ="thread-" + to_string(number); } public: string _thread_name; }; void* GetTicket(void* args) { ThreadData* td=static_cast<ThreadData*>(args); const char* name =td->_thread_name.c_str(); while(true) { if(ticket>0){ usleep(5000); printf("i am %s,get a ticket:%d\n",name,ticket); ticket--; }else break; } printf("%s ... quit\n",name); return nullptr; } int main() { vector<pthread_t> tids; vector<ThreadData*> thread_datas; for(int i=0;i<NUM;i++) { pthread_t tid; ThreadData* td=new ThreadData(i); thread_datas.push_back(td); pthread_create(&tid,nullptr,GetTicket,thread_datas[i]); tids.push_back(tid); } for(auto &e :tids) { pthread_join(e,nullptr); } for(auto &e :thread_datas) { delete e; } return 0; } 结……
SE_Wang 2025-12-17
11 0 0 -
[linux仓库]告别空洞理论!手写一个高性能日志模块,为线程池实战铺路
什么是池化技术?—— 一种“未雨绸缪”的智慧 让我们先抛开代码,来看一个生活中的例子: 想象一下,你经营着一家非常火爆的网约车公司。每当有乘客下单时,你才开始打电话招募司机、给他们注册、分配车辆。等这一套流程走完,乘客早已不耐烦地取消了订单。 聪明的做法是什么?你提前招募并培训好一批司机,让他们在几个热门地段的“司机站”里随时待命。订单一来,你立刻从站里派一位空闲的司机出发。任务完成后,司机不是解雇回家,而是返回站点继续等待下一个订单。 这个“司机站”就是“池”。池化技术的核心思想就是:将一批昂贵的、需要频繁使用的资源预先创建好并统一管理起来,当需要时直接从“池”中获取,用完后不是销毁,而是归还给“池”,以供后续复用。 为什么要池化?—— 因为“从零创建”的代价远比你想象的要高 正如下图提到的,池化技术旨在“减少底层重复工作”。 然而,如果我们直接一头扎进复杂的并发代码中,就好比在没有地图和手电筒的情况下探索一个漆黑的洞穴——我们很快就会迷失方向。 这个“手电筒”和“地图”,就是日志系统。在多线程环境中,断点调试(GDB)的作用会因为线程间的时序和调度问题而大打折扣。一个可靠的、能够记录关键信息和错误状态的日志系统,是我们分析、调试和监控我们未来线程池运行状态的生命线。 但在动手写日志系统之前,我们先要学习一种能让它变得无比灵活和强大的设计模式——策略模式 (Strategy Pattern)。 日志与策略模式 为了讲解日志模式,这里不得不提到设计模式,那么什么是设计模式呢? IT⾏业这么⽕, 涌⼊的⼈很多. 俗话说林⼦⼤了啥⻦都有. ⼤佬和菜鸡们两极分化的越来越严重. 为了让菜鸡们不太拖⼤佬的后腿, 于是⼤佬们针对⼀些经典的常⻅的场景, 给定了⼀些对应的解决⽅案,这个就是设计模式.(可是我怎么从来没有看到啊?在腾讯、阿里、谷歌等大厂中公布了一些开源……
SE_Wang 2025-12-16
12 0 0 -
【Linux】解决在扩充Ubuntu硬盘容量后,再启动时遇到的“Failed to start gdm.service” 错误
在使用 VMware 虚拟机运行 Ubuntu 时,有时会遇到启动卡在 “Failed to start gdm.service - GNOME Display Manager” 的问题。本文结合实际操作经验,详细分析问题原因及解决方案,帮助大家快速恢复系统正常运行。 一、问题现象 启动 Ubuntu 虚拟机时,系统卡在启动界面,提示 “Failed to start gdm.service - GNOME Display Manager”,无法进入图形界面。 二、问题根源分析 该问题主要与 snapd 及系统环境冲突有关,具体原因包括: snapd 组件冲突:snapd 是 Snap 包管理系统的后台服务,若其运行异常、配置错误或与图形界面组件(如 gdm)不兼容,会导致 gdm 服务启动失败。 硬件配置变动影响:虚拟机硬件调整(如扩充硬盘容量、修改内存)可能间接触发问题,例如: 硬盘扩容导致分区挂载异常、文件系统权限错乱。 snapd 相关数据目录因硬件变动损坏。 系统环境依赖问题:系统更新不完整、图形库文件损坏或权限配置冲突,可能加剧 snapd 与 gdm 的兼容性问题。 三、解决方案:通过恢复模式卸载 snapd 步骤 1:进入 GRUB 菜单 重启 Ubuntu 虚拟机,在进度条加载结束前长按 Shift 键,直到跳转到 GRUB 启动选项菜单。 步骤 2:选择恢复模式 在 GRUB 菜单中,选择 “Advanced options for Ubuntu” 并按回车。- 在子菜单中,选择最新内核版本对应的 “(recovery mode)” 选项(如 Ubuntu, with Linux 6.5.0-35-generic (recovery mode)),进入恢复模式。 步骤 3:进入救援模式并卸载 snapd 系统进入恢复模式后,选择倒数第二个选项 “root” 进入根目录命令行。 在下方的命令行中,输入以下命令彻底卸载 snapd 及其配置: apt autoremove --purge snapd 步骤 4:重启系统 卸载完成后,输入 reboot 重启虚拟机,此时系统可正常进入图形界面。 步骤 5:进入系统后安装卸载的snapd 在扩充硬盘和内存容量后,卸载 snapd……
SE_Wang 2025-12-15
77 0 0
