Linux系统编程中fcntl和ioctl的区别是什么?一文详解
作者:en.en..
前言
在 Linux 系统编程中,fcntl 和 ioctl 都是用于对文件描述符进行二次控制的系统调用。
二者容易混淆,但分工极其明确:
fcntl:POSIX 标准,所有文件描述符通用(文件、管道、socket、设备)
ioctl:设备私有接口,只用于硬件设备文件
本文按学习顺序:先 fcntl、后 ioctl,结构清爽、重点突出,适合入门与复习。
一、fcntl 文件描述符通用控制
1. 函数原型与头文件
#include <fcntl.h> int fcntl(int fd, int cmd, ...);
2. 参数说明
fd:任意合法文件描述符。 普通文件、无名管道、Socket、设备文件都支持,适用范围极广。
cmd:标准控制命令(POSIX 统一定义),跨平台兼容。
可变参数 ...:由 cmd 决定,绝大多数场景传递整数,少量命令使用结构体。
3. 返回值
成功:根据命令返回对应数值
失败:返回 -1,并自动设置 errno 错误码
4. 最经典使用场景(考试/面试重点)
fcntl 的核心用途:修改文件描述符的标准 IO 属性
为 无名管道、Socket 设置非阻塞 IO(最常考)
复制文件描述符(F_DUPFD)
设置 FD_CLOEXEC,防止子进程继承句柄泄露
文件区域读写锁,用于多进程文件同步
设置信号驱动 IO(SIGIO)
5. 经典代码示例:管道设置非阻塞
这是 fcntl 最基础、最标准的入门案例:
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
int main(void)
{
int pipefd[2];
pipe(pipefd);
// 获取原有文件状态标志
int flags = fcntl(pipefd[0], F_GETFL);
// 设置非阻塞属性
fcntl(pipefd[0], F_SETFL, flags | O_NONBLOCK);
close(pipefd[0]);
close(pipefd[1]);
return 0;
}二、ioctl 硬件设备专属控制
1. 函数原型与头文件
#include <sys/ioctl.h> int ioctl(int fd, unsigned long op, ...);
2. 参数说明
fd:硬件设备文件描述符(屏幕、串口、网卡、外设等)。普通文件不使用 ioctl。
op:设备控制指令(魔数)。 由设备驱动自定义,不同设备指令完全不通用。
可变参数 ...:完全由指令决定。 读取硬件信息必须传结构体指针,不能传结构体变量。
3. 返回值
成功:返回 0
失败:返回 -1,并设置 errno
4. 经典使用场景
ioctl 只干一件事:操作硬件独有功能
屏幕帧缓冲:获取分辨率、色深
终端/串口:波特率、窗口大小、回显模式
网卡:MAC 地址、MTU、网络参数
嵌入式外设:GPIO、声卡、摄像头、磁盘参数
5. 经典代码示例:ioctl 获取屏幕分辨率
需要屏幕结构体头文件:
#include <linux/fb.h>
核心结构体:
struct fb_var_screeninfo {
__u32 xres; // 横向分辨率
__u32 yres; // 纵向分辨率
__u32 bits_per_pixel;// 像素色深
};重点:FBIOGET_VSCREENINFO 必须传入结构体地址指针
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/fb.h>
int main(void)
{
int fd = open("/dev/fb0", O_RDWR);
if (fd < 0)
{
perror("open fb0");
return -1;
}
struct fb_var_screeninfo info;
if (ioctl(fd, FBIOGET_VSCREENINFO, &info) == -1)
{
perror("ioctl get screen");
close(fd);
return -1;
}
printf("分辨率:%u × %u\n", info.xres, info.yres);
printf("色深:%u bpp\n", info.bits_per_pixel);
close(fd);
return 0;
}三、fcntl 与 ioctl 核心区别
1. 适用范围不同
fcntl:所有 fd 通用(文件、管道、socket、设备)
ioctl:仅硬件设备文件专用
2. 标准性、移植性不同
fcntl:POSIX 标准,跨平台通用
ioctl:驱动自定义,不通用、不可移植
3. 经典学习场景不同
fcntl:无名管道设置非阻塞 IO
ioctl:获取屏幕分辨率、硬件信息
4. 传参特点不同
fcntl:大多传整数参数
ioctl:读硬件信息必须传结构体指针
四、最终使用口诀
通用文件属性、管道、socket 配置 → 用 fcntl
屏幕、串口、网卡、硬件设备控制 → 用 ioctl
五、总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。
