Linux C 面试题及详细答案(企业真实面试版)_第1页
Linux C 面试题及详细答案(企业真实面试版)_第2页
Linux C 面试题及详细答案(企业真实面试版)_第3页
Linux C 面试题及详细答案(企业真实面试版)_第4页
Linux C 面试题及详细答案(企业真实面试版)_第5页
已阅读5页,还剩4页未读, 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

LinuxC面试题及详细答案(企业真实面试版)一、C语言基础面试题1、static关键字在C语言中有哪些用法?详细答案:static主要有三种核心用法,是面试高频基础考点:1)修饰局部变量普通局部变量存储在栈区,函数调用结束后立即释放;static修饰的局部变量存储在静态数据区,程序运行期间始终保留,只会初始化一次,下次调用函数会沿用上次的值。2)修饰全局变量普通全局变量可以跨文件访问,static全局变量仅限当前源文件使用,其他文件无法通过extern引用,实现了文件级别的封装隔离。3)修饰函数static函数仅能在当前源文件内部调用,外部文件不可见、不可调用,常用于封装模块内部私有接口。2、const关键字的作用及常见使用场景?详细答案:const用于修饰只读数据,防止数据被误修改,提升代码健壮性,核心场景如下:1)修饰普通变量:变量值不可修改,定义时必须初始化;2)修饰指针:-constchar*p:指针指向的内容不可改,指针本身地址可改;-char*constp:指针地址不可改,指向的内容可改;-constchar*constp:指针地址和指向内容都不可改;3)修饰函数参数:防止函数内部修改传入的实参数据,常用于字符串、数组、结构体传参;4)修饰函数返回值:避免返回值被恶意修改,多用于返回常量字符串、只读数据。3、指针和数组的区别?为什么数组名不是指针?详细答案:很多人混淆二者,本质是完全不同的两种数据类型:1)本质区别:数组是连续内存空间,存的是数据本身;指针是存储内存地址的变量,占用4/8字节(32/64位系统)。2)sizeof结果不同:sizeof(数组名)得到数组总内存大小;sizeof(指针)永远是指针本身大小。3)取值不同:数组名代表数组首元素地址,是常量地址,不可赋值;指针是变量,可随意修改指向。4)传递参数差异:数组作为函数参数会退化为指针,丢失数组长度信息;原生指针传参不会变化。总结:数组名在多数表达式中隐式转换为指针,但本身不是指针。4、结构体字节对齐的原理和作用?如何手动设置对齐?详细答案:CPU读取内存时,按固定字节块读取(4字节、8字节),未对齐的数据会让CPU读取多次,降低效率。字节对齐就是让结构体成员内存地址符合硬件读取规则。1)默认对齐规则:结构体每个成员的偏移地址,必须是自身大小和系统默认对齐数的较小值的整数倍;结构体整体大小,必须是最大成员对齐数的整数倍。2)作用:提升CPU内存访问效率,部分硬件平台强制要求内存对齐,否则程序崩溃。3)手动设置对齐:Linux下可通过GCC指令设置__attribute__((aligned(n)))//设置对齐字节数__attribute__((packed))//取消对齐,紧凑存储,节省内存二、Linux系统编程面试题(进程、信号、IO)1、fork()函数的工作原理?父子进程的区别与资源拷贝机制?详细答案:fork()是Linux创建子进程的系统调用,调用一次、返回两次:父进程返回子进程PID,子进程返回0,调用失败返回-1。1)资源拷贝机制(核心考点):Linux采用写时拷贝(COW)机制,并非直接复制完整内存。fork初期父子进程共享物理内存页,仅拷贝页表;当任意一方修改数据时,内核才会为修改的内存页单独拷贝一份,保证进程地址空间独立。该机制极大节省了内存和创建耗时。2)父子进程区别:-PID不同:父进程PID唯一,子进程PID由内核分配;-返回值不同:父返子PID,子返0;-资源归属:文件描述符、信号状态、缓冲区数据父子独立,共享代码段;-执行顺序:无固定顺序,由系统调度器决定。2、什么是僵尸进程、孤儿进程?如何处理僵尸进程?详细答案:1)孤儿进程:父进程先退出,子进程被init进程(PID=1)收养,孤儿进程无危害,会正常回收资源。2)僵尸进程:子进程退出、父进程未调用wait/waitpid读取子进程退出状态,子进程PCB资源无法释放,残留在内核中,即为僵尸进程。僵尸进程会占用系统PID资源,大量存在会导致系统无法创建新进程。3)解决方法:-父进程主动调用wait()/waitpid()阻塞或非阻塞回收子进程;-父进程注册SIGCHLD信号处理函数,子进程退出会发送该信号,在信号函数中批量回收;-父进程fork两次,二级子进程由init进程自动回收。3、Linux常见信号有哪些?SIGINT、SIGKILL、SIGCHLD区别?详细答案:信号是Linux进程间异步通信的简单方式,常见核心信号:1)SIGINT(2号信号):键盘Ctrl+C触发,终止前台进程,可被捕获、忽略、自定义处理;2)SIGKILL(9号信号):强制杀死进程,不可捕获、不可忽略、不可自定义,用于结束卡死、僵尸、阻塞进程;3)SIGCHLD(17号信号):子进程退出/暂停时,自动发送给父进程,默认忽略,是解决僵尸进程的核心信号;4)SIGSEGV(11号信号):段错误,非法访问内存(野指针、数组越界、栈溢出)触发;5)SIGQUIT(3号信号):Ctrl+\触发,退出进程并生成core转储文件。4、阻塞IO、非阻塞IO、IO多路复用的区别?详细答案:1)阻塞IO:默认IO模式,调用read/write时,若无数据则线程休眠阻塞,直到数据就绪返回,简单但并发能力差,单线程只能处理一个IO;2)非阻塞IO:通过fcntl设置O_NONBLOCK,无数据时立即返回-1,不阻塞线程,需要循环轮询,CPU占用高;3)IO多路复用:单线程监听多个文件描述符,任意一个就绪则通知程序处理,无需轮询,CPU效率高,是高并发服务核心模型。三者核心差异:阻塞IO卡线程、非阻塞IO耗CPU、多路复用高效支持多IO并发。5、select、poll、epoll的区别(面试必考)详细答案:三者都是LinuxIO多路复用实现方式,性能和机制差异极大:1)select:-最大监听文件描述符有限(默认1024);-每次调用需要重新拷贝fd集合、遍历所有fd检测就绪状态;-时间复杂度O(n),适合少量IO场景。2)poll:-突破1024文件描述符限制;-无需重置fd集合,但仍需要遍历所有fd;-时间复杂度O(n),性能略优于select,无本质提升。3)epoll(Linux独有):-基于事件驱动,仅返回就绪的文件描述符,无需遍历全部;-时间复杂度O(1),支持海量文件描述符监听;-支持LT水平触发(默认)和ET边缘触发(高效模式);-高并发服务器(Nginx、Redis)核心依赖。三、Linux网络编程面试题1、TCP三次握手、四次挥手过程?为什么三次握手、四次挥手?详细答案:三次握手(建立连接):1)客户端发送SYN报文,请求建立连接;2)服务端收到SYN,回复SYN+ACK,确认客户端请求,并向客户端发起连接请求;3)客户端收到SYN+ACK,回复ACK,连接建立。核心原因:三次握手可以双向确认收发能力正常,同时避免失效连接报文占用服务端资源,两次握手无法校验客户端接收能力。四次挥手(断开连接):1)主动关闭方发送FIN,告知对方不再发送数据;2)被动关闭方回复ACK,确认关闭,此时连接半关闭,被动方仍可发送数据;3)被动方数据发送完毕后,发送FIN;4)主动关闭方回复ACK,连接彻底断开。核心原因:TCP是全双工通信,收发通道独立,关闭两个通道需要四次交互,无法合并报文。2、TCP粘包问题产生原因及解决方法?详细答案:1)产生原因:TCP是流式协议,无数据边界,内核会根据缓冲区大小合并、拆分数据包,导致多次发送的数据被合并接收,出现粘包、拆包现象。2)解决方案(企业常用三种):-固定包长:每次收发固定字节数据,不足补零,简单高效;-包头+包体:头部存储数据包总长度,接收端先读头部,根据长度读取完整包体;-特殊分隔符:数据包结尾添加唯一分隔符,接收端按分隔符拆分数据包。详细答案:作用:1)保证被动关闭方收到最后一次ACK,避免对方重发FIN报文;2)等待网络中残留的失效TCP报文彻底过期,避免新连接收到旧连接的脏数据。四、Linux运维与调试面试题1、段错误(SIGSEGV)常见原因及排查方法?详细答案:常见原因:1)野指针、空指针解引用;2)数组越界访问内存;3)修改只读内存段(字符串常量、代码段);4)栈空间溢出、重复释放内存。排查方法:1)开启core转储,通过gdb调试core文件定位崩溃行;2)使用gdb直接运行程序,触发崩溃后自动暂停,查看调用栈;3)使用valgrind检测内存越界、内存泄漏、野指针问题。2、gdb常用调试命令有哪些?详细答案:-gcc-g编译:添加调试信息,必须开启才能gdb调试;-break行号/函数名:设置断点;-run:运行程序;-next:单步跳过,不进入子函数;-step:单步进入,进入子函数内部;-print变量:打印变量值;-bt:查看函数调用栈,排查崩溃位置;-continue:继续运行到下一个断点;-quit:退出调试。3、Linux内存泄漏的原因及排查手段?详细答案:内存泄漏原因:动态申请的内存(malloc/calloc/realloc)使用完毕后,未调用free释放,程序长期运行导致内存占用持续升高,最终内存耗尽。排查手段:1)valgrind--leak-check=full精准检测内存泄漏位置、大小;2)top、free、ps查看进程内存占用变化;3)mtrace跟踪内存申请释放日志,定位异常内存。五、高频手写代码面试题(带答案)1、手写Linux守护进程代码详细答案代码:c

#include<stdio.h>

#include<stdlib.h>

#include<unistd.h>

#include<sys/stat.h>

voiddaemonize(void)

{

pid_tpid;

//1.创建子进程,父进程退出

pid=fork();

if(pid<0)

{

perror("fork");

exit(1);

}

if(pid>0)

{

exit(0);

}

//2.创建新会话,脱离终端

setsid();

//3.修改工作目录为根目录

chdir("/");

//4.重置文件权限掩码

umask(0);

//5.关闭标准输入输出错误文件描述符

close(STDIN_FILENO);

close(STDOUT_FILENO);

close(STDERR_FILENO);

}

intmain()

{

daemonize();

while(1)

{

//守护进程业务逻辑

sleep(1);

}

return0;

}原理说明:通过fork脱离终端、setsid创建独立会话、关闭默认文件描述符、修改工作目录,彻底脱离终端,实现后台常驻运行。2、手写简单TCP服务端代码(基础版)详细答案代码:c

#include<stdio.h>

#include<stdlib.h>

#include<string.h>

#include<unistd.h>

#include<sys/socket.h>

#include<netinet/in.h>

#definePORT8080

#defineBUF_LEN1024

intmain()

{

intsockfd,connfd;

structsockaddr_inserv_addr;

charbuf[BUF_LEN]={0};

//1.创建套接字

sockfd=socket(AF_INET,SOCK_STREAM,0);

if(sockfd<0)

{

perror("socket");

return-1;

}

//2.绑定端口IP

serv_addr.sin_family=AF_INET;

serv_addr.sin_port=htons(PORT);

serv_addr.sin_addr.s_addr=INADDR_ANY;

if(bind(sockfd,(structsockaddr*)&serv_addr,sizeof(serv_addr))<0)

{

perror("bind");

return-1;

}

//3.开启监听

listen(

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论