
每年校招季和社招季都会有一批准备投嵌入式岗位的同学被同一个问题卡住——打开招聘JD“熟悉Linux操作系统”这行字明明白白写在任职要求里但没有任何一家公司告诉你“熟悉”到底意味着会敲几条命令还是能读内核源码。我见过不少简历上写着“熟悉Linux”的候选人面试时被问了几个很实在的问题就露馅了板子上电后某个驱动加载失败你会怎么定位两个线程同时访问一个全局变量怎么保证不出问题用gdb调试一个正在运行的进程命令是什么这些问题教科书上都出现过但很多人真的没有在Linux环境下动手解决过。这篇文章想把“熟悉Linux”这件事拆开讲清楚。它不是要你背命令而是要说清楚企业招聘嵌入式岗位时隐藏在JD背后的真实技术标准以及如果你想达到这个标准应该沿着什么样的路线去学。全文会从基础命令、C语言工程、系统编程、网络通信、调试排查、驱动协作到学习路线逐一展开给你一个可以直接对照执行的认知框架。1. 企业招聘里的“熟悉Linux”究竟在考什么先看一个典型的嵌入式Linux开发岗位JD任职要求熟悉Linux操作系统熟悉C语言了解多进程/多线程编程熟悉TCP/IP网络编程有嵌入式Linux项目开发经验者优先。如果你只看字面会觉得要求并不高。但只要进入面试环节问题会立刻变得具体Linux下如何查看一个进程占用的CPU和内存进程和线程有什么区别什么场景下用进程什么场景下用线程编译一个嵌入式程序时为什么需要交叉编译工具链设备树Device Tree的作用是什么应用程序访问硬件设备是直接操作物理地址吗gdb、strace、top、dmesg这些工具你用过哪些这些问题背后实际是企业在考察四个层次的能力第一层环境操作能力。你是否能熟练使用Linux命令行完成文件操作、进程查看、网络配置、日志分析。这一层考察的是“能不能干活”。第二层系统编程能力。你是否理解操作系统的基本机制能否用C语言写出多进程、多线程、网络通信等真实业务代码。这一层考察的是“会不会写程序”。第三层调试排错能力。程序崩溃、进程卡死、内存泄漏、网络异常你是否能利用Linux下的各种工具独立定位问题。这一层考察的是“能不能独立解决问题”。第四层硬件协同能力。你是否理解Linux下的设备模型知道应用程序、驱动、硬件三者之间怎么协作。这一层考察的是“是否具备嵌入式开发的完整视野”。所以“熟悉Linux”在招聘JD里是一句模糊的话但在面试官心里是一套非常清晰的打分标准。多数候选人挂在第二层和第三层——不是不会写C而是不会在Linux环境下写出符合工程规范的C不是不会编程而是出了问题不知道怎么查。2. 为什么嵌入式开发离不开Linux从裸机到操作系统的进化要理解Linux在嵌入式开发中的位置需要先回顾嵌入式软件的发展脉络。早期的MCU开发是典型的裸机开发。程序员直接操作寄存器控制GPIO、UART、定时器所有代码都在一个main函数的大循环里跑。这种方式的优点是简单直接、资源占用极低缺点是扩展性差、任务管理靠手工调度一旦业务逻辑变多代码就会迅速失控。这也是为什么在嵌入式社区能看到大量类似“从超级大循环到事件驱动嵌入式架构升级的分水岭”这样的讨论。当设备功能越来越复杂需要同时处理网络、显示、存储、用户交互时裸机开发已经不够用了。这时候有两条路一是引入RTOS实时操作系统比如FreeRTOS、RT-Thread解决多任务调度和资源管理问题二是直接引入Linux获得完整的进程管理、内存管理、文件系统和网络协议栈。一条经验界限是这样的如果你的产品需要跑复杂的协议栈、需要多任务并发处理、需要丰富的文件系统支持、需要比较完善的调试工具链Linux往往是更合适的选择。例如路由器、智能摄像头、工业HMI人机交互界面、车载信息娱乐系统基本都是嵌入式Linux的典型应用场景。对比维度裸机开发RTOSLinux任务调度手工/前后台抢占式调度抢占式调度支持时间片轮转内存管理无保护可选较为基础完整MMU支持进程地址空间隔离文件系统无/扩展Flash简单文件系统完整VFS支持ext4、FAT、NFS等网络协议栈需自研或移植有轻量协议栈完整TCP/IP协议栈调试手段断点、printf断点、信号量分析gdb、strace、perf等丰富工具链资源需求低中高需要MMU一般Cortex-A级别开发效率低中高这个表格不是非此即彼的否定而是帮你理解为什么市面上大量的嵌入式岗位特别是消费电子、工业控制、物联网网关方向都明确要求熟悉Linux。Linux在嵌入式系统中的角色可以理解为“硬件资源的中枢管理者”。它负责CPU调度、内存分配、设备驱动、文件管理、网络通信。应用层程序员写的C代码跑在Linux提供的进程和线程模型之上通过系统调用来读写文件、收发网络数据、操作设备节点。这些机制正是面试考察的核心内容。3. 基础层Linux命令、文件系统与Shell要学到什么程度很多初学者对“学Linux”的理解停留在“会敲命令”。这有一定道理但远远不够。命令是通往系统内部的工具真正的重点是你能否借助这些命令理解系统状态、发现系统问题。嵌入式开发中高频出现的基础命令大致分四类类别常用命令典型用途文件操作ls、cd、cp、mv、rm、find、tar文件与目录管理、打包解包系统状态top、free、ps、uname、dmesg查看CPU、内存、进程、内核日志网络工具ifconfig、ping、netstat、tcpdump网络配置与连通性诊断文本处理grep、cat、sed、awk、vi/vim日志过滤、配置修改、脚本处理嵌入式Linux开发中有四个目录需要特别理解/dev设备节点目录。Linux遵循“一切皆文件”的理念硬件设备在应用层体现为设备文件比如串口对应/dev/ttyS0、/dev/ttyUSB0应用通过open/read/write操作这些文件来访问硬件。/proc内核运行时的虚拟文件系统反映进程和系统的实时信息。比如/proc/cpuinfo查看CPU信息/proc/meminfo查看内存信息。/sys提供内核设备模型的视图常用来控制设备状态。比如调整LED亮度、查看设备树信息很多地方需要访问/sys/class。/etc系统配置目录嵌入式系统中的大部分配置如网络配置、启动脚本都放在这里。为什么理解目录比背命令更重要因为嵌入式排错往往就是从这些目录里找线索。举一个典型排查案例假设一块开发板启动后某个USB设备没有生成对应的/dev/ttyUSB0节点你要怎么做# 1. 查看内核日志定位驱动加载过程 dmesg | grep -i usb # 2. 查看USB总线上的设备连接 lsusb # 3. 查看设备树中是否使能了该USB接口 ls /sys/firmware/devicetree/base/ # 4. 查看系统是否有对应的设备节点 ls -l /dev/ttyUSB* # 5. 查看插入设备瞬间的完整内核日志 dmesg | tail -n 50这个排查过程看起来是在用命令实际上是在理解“设备树声明硬件 - 驱动匹配设备 - 内核创建设备节点 - 应用层访问设备文件”这条完整链路。只会背命令的人看到dmesg只当它是一个日志工具真正熟悉Linux的人知道每一步输出意味着什么知道下一步该往哪个方向查。Shell脚本同样是基础能力。企业里常见需求包括写一个自动化构建脚本编译完成后自动打包固件写一个开机启动脚本按顺序启动应用程序写一个日志清理脚本避免设备长期运行后磁盘被占满。这些任务不需要复杂的Shell技巧但需要你具备基本脚本编写能力。4. 编程层C语言、编译工具链与交叉编译是硬门槛如果说Linux命令是外壳C语言就是嵌入式开发的骨架。企业招聘嵌入式岗位时C语言的考察深度远超学校期末考试。先回答一个常见疑惑为什么嵌入式开发至今以C语言为主核心原因是C语言兼顾“高级语言的表达能力”和“底层硬件的操作能力”。你可以用指针访问内存映射的寄存器可以用结构体定义硬件寄存器布局可以嵌入汇编完成关键操作同时C语言生成的代码足够高效在资源受限的嵌入式设备上能稳定运行。但这不意味着“会C语言”就是及格水平。企业真正要求的是“能在Linux环境下完成C语言工程开发”的能力这里有几个关键点第一理解编译过程。C语言从源码到可执行文件要经过预处理、编译、汇编、链接四个阶段。嵌入式开发中经常会遇到“头文件找不到”“符号未定义”“链接脚本错误”这类问题不懂编译过程你连错误信息都读不懂。第二掌握交叉编译。嵌入式的程序通常不是直接在开发板上写代码编译而是在PC上用交叉编译工具链生成目标板架构的可执行文件再传输到板子上运行。本质区别在于“编译环境”和“运行环境”的架构不同。# 以arm架构为例交叉编译工具链的使用方式 arm-linux-gnueabihf-gcc -o hello hello.c -static # 查看编译出的文件属于什么架构 file hello # 查看依赖的动态库判断能否在目标板上运行 arm-linux-gnueabihf-readelf -d hello | grep NEEDED # 将编译产物拷贝到目标板后添加执行权限并运行 chmod x hello ./hello注意这里不要背命令而是要理解其中的逻辑arm-linux-gnueabihf-gcc是编译工具它生成的是ARM架构的机器码file命令验证产物架构readelf检查动态库依赖判断目标板上是否有对应的库文件。交叉编译问题在嵌入式开发中几乎天天遇到也是面试常考点。第三会用构建工具组织多文件工程。企业项目不是单文件练习一个工程往往包含几十个源文件、多个目录、多种编译选项。这时需要借助Makefile或CMake管理构建过程。一个最简单的Makefile示例如下# 文件路径Makefile CC arm-linux-gnueabihf-gcc TARGET app OBJS main.o uart.o sensor.o $(TARGET): $(OBJS) $(CC) -o $(TARGET) $(OBJS) main.o: main.c sensor.h $(CC) -c main.c uart.o: uart.c uart.h $(CC) -c uart.c sensor.o: sensor.c sensor.h $(CC) -c sensor.c clean: rm -f $(TARGET) $(OBJS)这个Makefile定义的规则很基础每个.o文件由对应的.c文件编译生成最终由所有.o文件链接成目标文件。实际项目中通常还会加入-Wall开启警告、-g加入调试信息、-O2优化等级等选项。能独立完成一个多文件工程的编译配置是进入企业项目的基本门槛。第四具备扎实的指针和内存功底。搜一下“C语言指针”就知道指针是C语言学习中的高频难点。嵌入式开发中函数指针注册回调、结构体指针操作硬件寄存器、二级指针链表管理、动态内存分配malloc/free配对都极为常见。面试中常考的问题包括结构体对齐、指针与数组的区别、野指针与内存泄漏、sizeof和strlen的区别等。这些内容没有捷径只能通过大量写代码、读代码来建立肌肉记忆。5. 系统编程层进程、线程、IPC的真实开发场景嵌入式Linux和裸机开发的最大区别之一就是引入了操作系统层面的并发机制。企业招聘JD里常写的“熟悉多进程/多线程”背后是真实的产品需求——设备要同时采集传感器数据、维护网络连接、响应用户输入、刷新显示界面如果所有功能串行执行任何一个慢操作都会拖垮整个系统。先梳理进程和线程的核心区别维度进程线程资源拥有独立地址空间、独立资源共享进程地址空间切换开销大涉及内核态切换小共享上下文通信方式需借助IPC机制可直接共享全局变量稳定性一个进程崩溃不影响其他进程一个线程崩溃可能导致整个进程退出适用场景功能隔离、大型模块高频小任务、数据共享在嵌入式产品中进程和线程的选择往往取决于需求。例如一个网络网关设备主控程序、Web服务、日志服务可以拆成独立进程运行利用Linux的进程隔离防止单点故障而一个音频采集程序内部采集线程、处理线程、发送线程共享同一份音频数据用多线程更合适。多线程编程最常见的坑就是对共享资源的并发访问控制。看下面这个简化示例// 文件路径thread_demo.c #include stdio.h #include pthread.h static int counter 0; static pthread_mutex_t lock PTHREAD_MUTEX_INITIALIZER; void *worker(void *arg) { for (int i 0; i 100000; i) { pthread_mutex_lock(lock); counter; pthread_mutex_unlock(lock); } return NULL; } int main(void) { pthread_t t1, t2; pthread_create(t1, NULL, worker, NULL); pthread_create(t2, NULL, worker, NULL); pthread_join(t1, NULL); pthread_join(t2, NULL); printf(counter %d\n, counter); pthread_mutex_destroy(lock); return 0; }编译运行gcc -o thread_demo thread_demo.c -pthread ./thread_demo如果去掉互斥锁由于counter不是原子操作两个线程会同时读取、修改、写回counter最终结果可能小于200000。这是并发编程里的经典竞争条件问题。嵌入式开发中这类问题可能出现在传感器数据采集、环形缓冲区读写、日志写入等各个场景。除了互斥锁Linux下还有信号量、自旋锁、读写锁、条件变量等同步机制以及管道、消息队列、共享内存、信号、Socket等进程间通信IPC方式。面试中常被追问“互斥锁和自旋锁有什么区别”“信号量和互斥锁有什么不同”这些问题考察的都是“你是否真的理解并发场景下的系统开销与适用边界”。在嵌入式实际项目中还要特别关注“优先级反转”和“死锁”问题。一个线程持有锁后被更高优先级的线程抢占导致其他低优先级线程持续等待这就是优先级反转多个线程互相等待对方持有的资源就会造成死锁。这两类问题在嵌入式系统里会导致设备“假死”排查起来非常费劲。理解它们的成因写出规范的无死锁代码是企业考察系统编程能力的重要维度。6. 网络编程嵌入式设备联网的基础能力今天的嵌入式设备几乎默认联网。智能家居设备通过Wi-Fi接入局域网工业传感器通过以太网上报数据车载设备通过TCP/UDP与云端通信。因此“熟悉计算机网络”和“熟悉TCP/IP编程”成为嵌入式岗位的常见要求。这个方向需要掌握的内容分三层第一层TCP/IP基础。理解TCP三次握手、四次挥手理解IP地址、端口、子网掩码知道TCP与UDP的适用场景。嵌入式设备资源有限、链路可能不稳定实际项目中往往需要根据需求权衡可靠性与实时性。第二层Socket编程。Linux下网络编程的标准接口是Socket。一个最小化的TCP服务端示例// 文件路径tcp_server.c #include stdio.h #include string.h #include unistd.h #include sys/socket.h #include netinet/in.h #include arpa/inet.h #define PORT 8080 int main(void) { int server_fd, client_fd; struct sockaddr_in addr; char buffer[1024] {0}; server_fd socket(AF_INET, SOCK_STREAM, 0); if (server_fd 0) { perror(socket); return -1; } int opt 1; setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, opt, sizeof(opt)); addr.sin_family AF_INET; addr.sin_addr.s_addr INADDR_ANY; addr.sin_port htons(PORT); if (bind(server_fd, (struct sockaddr *)addr, sizeof(addr)) 0) { perror(bind); return -1; } if (listen(server_fd, 5) 0) { perror(listen); return -1; } printf(server listening on port %d\n, PORT); client_fd accept(server_fd, NULL, NULL); read(client_fd, buffer, sizeof(buffer)); printf(received: %s\n, buffer); write(client_fd, ack from server, 15); close(client_fd); close(server_fd); return 0; }编译运行gcc -o tcp_server tcp_server.c ./tcp_server这段代码演示了Socket编程的标准流程创建socket、设置地址复用、绑定端口、监听、接受连接、收发数据。实际嵌入式项目中还要考虑非阻塞IO、select/poll/epoll多路复用、断线重连、心跳机制等问题。特别是多路复用当设备需要同时处理多个TCP连接时不可能为每个连接创建一个线程去阻塞等待这时候epoll是Linux下比较高效的方案。第三层应用层协议。嵌入式设备与云平台通信通常不会直接裸用TCP而是使用MQTT、HTTP、CoAP等应用层协议。MQTT在物联网场景中使用很广泛因为它基于发布/订阅模型支持低带宽、不稳定网络环境下的消息传输。面试时如果能在聊到网络编程时提及“我们设备用MQTT上报数据QoS级别选择了1是为了在可靠性和实时性之间平衡”会明显体现你的实战理解。学习网络编程时一个很容易出现的问题是“纸上谈兵”——能画出OSI七层模型却写不出一个能互通的客户端和服务端。建议一定要在Linux环境下手动完成一个Socket通信小程序再逐步加入多客户端、断线重连、分包粘包处理这些才是企业真正需要的实战能力。7. 调试与排查企业真正看重的工程能力把“调试与排查”放在单独一章是因为这恰好是衡量“熟悉Linux”水平的分水岭。会用Linux的人可能只会敲命令但真正熟悉Linux的人一定具备一套完整的排查方法论。嵌入式开发中典型的调试排查场景包括程序段错误Segmentation Fault、进程崩溃、内存泄漏、CPU占用过高、网络不通、设备节点不生成。每一种问题Linux下都有对应的工具和手段。场景一程序崩溃如何定位崩溃位置假设你有一个程序运行时直接崩溃了如果你只会在代码里到处加printf效率会非常低。更好的做法是用gdb分析core dump文件# 编译时加入调试信息 gcc -g -o demo demo.c # 开启core dump ulimit -c unlimited # 运行程序直到崩溃 ./demo # 使用gdb查看崩溃位置 gdb ./demo core # 在gdb内部查看调用栈 (gdb) bt (gdb) info registersbt命令可以打印崩溃时的函数调用栈直接定位到出错的函数和行号。这是Linux下排查崩溃类问题最标准的流程。场景二进程卡死真的“死”了吗嵌入式设备另一个常见问题是“程序好像卡住了”。这时不能急着重启先确认进程状态# 查看进程状态Z是僵尸D是不可中断R是运行 ps aux | grep app # 查看进程打开的文件和线程 ls -l /proc/pid/fd ls -l /proc/pid/task # 查看系统调用层面卡在哪里 strace -p pid # 查看CPU和内存占用 top -H -p pidstrace是可以跟踪进程系统调用的工具它能把进程正在执行的系统调用实时打印出来。例如进程卡在read()调用上等待数据说明它可能在阻塞等待某个IO事件如果进程反复执行某个系统调用并返回错误说明可能陷入了异常循环。这些判断是纯靠看代码很难做到的。场景三CPU占用过高什么线程在偷跑# 用top查看进程CPU占用 top # 按线程维度查看 top -H # 采样分析 perf top场景四内存泄漏怎么定位嵌入式设备内存有限长期运行的设备一旦内存泄漏最终会耗尽内存导致系统崩溃。常见的手段包括使用valgrind检测内存泄漏或者监控/proc/pid/status里的VmRSS数值是否持续增长。在产品开发中更推荐在代码层面建立内存统计机制从架构上避免泄漏发生。除了工具使用更核心的是培养“分层排查”的思维。遇到网络不通先看链路层ping网关通不通、再看网络配置ifconfig看IP、掩码、路由、再看应用层netstat看端口监听遇到设备节点不生成先看硬件连接lsusb、再看内核日志dmesg、再看设备树配置。排查顺序清晰很多问题会迅速缩小范围。这些调试经验面试官没法直接考察但会在“你做过什么项目”“项目中遇到过什么问题”这类问题里反复试探。如果你能完整讲出“当时设备出现XX现象我用XX命令发现XX异常分析出是XX原因最终通过XX方案解决”这就是比任何证书都更有说服力的证明。8. 驱动与内核应用开发要不要学驱动很多准备嵌入式方向的同学有一个认知误区觉得嵌入式开发等于写驱动于是把大量时间花在读Linux内核源码、研究字符设备驱动框架上。但实际上企业里的嵌入式岗位通常有明确的分工岗位方向核心工作Linux要求重点嵌入式应用开发业务逻辑、协议栈、UI、控制逻辑系统编程、网络、文件系统、调试嵌入式驱动开发外设驱动、内核模块、设备树适配内核机制、硬件原理、并发与内存管理BSP/系统开发板级支持包、系统启动、根文件系统引导流程、内核配置、交叉编译工具链从招聘数量来看嵌入式应用开发岗位远多于驱动开发岗位。大部分产品和设备应用层需要实现的功能很多驱动层往往由芯片厂商、核心板厂商或者BSP工程师提供应用工程师拿到的是已经能跑起来的Linux系统和设备节点。所以如果你刚进入嵌入式方向优先把应用开发能力打扎实是性价比更高的选择。但这不意味着应用开发可以完全不懂驱动和内核。应用工程师至少要理解几个概念第一应用层如何访问硬件。在Linux下应用程序不直接操作物理地址而是通过设备节点访问硬件。打开/dev/xxx文件调用ioctl()控制设备通过read/write和驱动交换数据。理解这个机制你才知道为什么一个串口设备在应用层只是一个文件。第二看懂设备树的基本信息。设备树是描述硬件资源的机制。应用开发有时需要确认某个外设是否被正确配置。比如不确认触摸屏对应的I2C控制器是否使能可以在设备树源文件或/sys/firmware/devicetree/base/路径下查找节点。不要求你会写设备树但至少要能看懂结构。第三理解内核日志。dmesg是应用工程师排查硬件异常的第一入口。插入U盘没有反应打开串口没有输出先看dmesg里有没有对应的驱动日志。掌握从内核日志到设备节点的分析方法你会节省大量调试时间。第四了解IO内存映射。应用层通过mmap映射/dev/mem或设备驱动的物理内存地址可以实现高效数据交互。例如framebuffer显示就是通过mmap映射显存地址实现的。对于真正想深入驱动开发的同学建议的学习路径是先掌握Linux应用编程再学习字符设备驱动框架、内核模块编译、设备树、中断和内核并发处理。但这件事可以放到“已经有应用开发基础”之后而不是一上来就啃内核源码。内核源码是一个庞大的迷宫没有应用层经验和硬件理解作为地图深入进去很容易迷失方向。9. 学到什么水平才算达标分阶段量化标准与学习路线写了这么多最后落到那个最初的问题企业要求“熟悉Linux”究竟要到什么水平这里给一个分阶段的参考标准你可以对照自测。阶段能力描述自测任务是否达到企业“熟悉Linux”一般要求入门能操作命令会用vim能跑通编译在Linux下从写代码到编译运行一个多文件C工程不足基础理解进程/线程会Socket会用gdb编写一个TCP服务端支持多客户端连接定位一次段错误崩溃勉强达到门槛进阶能处理并发竞争会排查系统级问题用strace定位进程卡死原因用valgrind排查内存泄漏符合多数岗位预期深入理解内核与驱动能独立完成系统适配编写一个字符设备驱动或完成BSP移植超出多数应用岗位要求如果你的目标是拿到一个嵌入式Linux应用开发岗位比较务实的目标是达到第三阶段。这意味着你不仅能“用”Linux还能在Linux上独立完成工程开发、问题定位和性能分析。对应的学习路线建议按下面这个顺序推进阶段一环境搭建与命令基础。2-3周安装Linux发行版推荐从Ubuntu或Debian系的桌面版开始系统学习文件和目录操作、用户与权限、文本处理每天用命令行代替图形界面操作刻意练习阶段二C语言强化与编译工具链。3-4周系统复习指针、结构体、内存管理在Linux下用gcc、Makefile组织多文件工程学习静态库和动态库的编译与使用阶段三系统编程。4-6周深入学习进程与线程、同步互斥、IPC学习文件IO与标准IO的区别用gdb和strace排查自己代码里的问题阶段四网络编程。3-4周掌握TCP/UDP Socket编程学习select/poll/epoll多路复用了解MQTT、HTTP等常用应用层协议的交互流程阶段五嵌入式交叉开发。4-6周入手一块常见的ARM开发板学习交叉编译、烧录、文件系统跑通一个完整项目比如通过串口读取传感器数据通过TCP上报到服务器同时支持远程配置在这个过程中熟悉板子上的设备树、内核日志、常用外设调试方法阶段六项目综合与总结。持续进行用Git管理代码形成规范的工程习惯整理自己遇到过的问题和排查过程写入技术笔记尝试阅读少量内核源码或驱动框架代码建立底层视野一个建议学习过程中不要只跟着教程“跑通就完事”一定要制造问题、解决问题。比如在开发板上故意把一个线程的锁去掉观察数据竞争现象把某个驱动的设备树节点禁用观察系统行为变化。这些主动破坏练习带来的理解深度远超一遍遍读教程。最后再说一个容易被忽略的点面试时不要只说自己“熟悉Linux”要说清楚你“用Linux做了什么”。能讲清楚一个从问题现象、排查过程、根因分析、解决方案到经验总结的完整故事比在简历上写十个技术名词更有说服力。回到最初的问题——企业要求“熟悉Linux”到底学到什么水平真正的判断标准不是命令数量而是你是否能在一台陌生板子上独立完成从编译、部署、运行到定位问题的完整工作闭环。做到这一步你面试时就可以很自然地跟面试官说我不只是学过Linux我是在Linux上做出过东西的人。