linux系统下fcntl函数解析

        fcntl()函数可以对一个已经打开的文件描述符执行一系列控制操作,譬如复制一个文件描述符(与 dup 、dup2 作用相同)、获取 / 设置文件描述符标志、获取 / 设置文件状态标志等,类似于一个多功能文件描述符管理工具箱。fcntl() 函数原型如下所示(可通过 "man 2 fcntl" 命令查看):
#include <unistd.h>
#include <fcntl.h>
int fcntl(int fd, int cmd, ... /* arg */ )
        函数参数和返回值含义如下:
        fd 文件描述符。
        cmd 操作命令。此参数表示我们将要对 fd 进行什么操作, cmd 参数支持很多操作命令,大家可以打开 man 手册查看到这些操作命令的详细介绍,这些命令都是以 F_XXX 开头的,譬如 F_DUPFD F_GETFD 、F_SETFD 等,不同的 cmd 具有不同的作用, cmd 操作命令大致可以分为以下 5 种功能:
        ⚫ 复制文件描述符(cmd=F_DUPFD 或 cmd=F_DUPFD_CLOEXEC );
        ⚫ 获取/设置文件描述符标志(cmd=F_GETFD cmd=F_SETFD );
        ⚫ 获取/设置文件状态标志( cmd=F_GETFL cmd=F_SETFL );
        ⚫ 获取/设置异步 IO 所有权( cmd=F_GETOWN cmd=F_SETOWN );
        ⚫ 获取/设置记录锁( cmd=F_GETLK cmd=F_SETLK );
        这里列举出来,并不需要全部学会每一个 cmd 的作用,因为有些内容并没有给大家提及到,譬如什么 异步 IO 、锁之类的概念,在后面的学习过程中,当学习到相关知识内容的时候再给大家介绍。
         fcntl 函数是一个可变参函数,第三个参数需要根据不同的 cmd 来传入对应的实参,配合 cmd 来使用。
        返回值:执行失败情况下,返回 -1 ,并且会设置 errno ;执行成功的情况下,其返回值与 cmd (操作命令)有关,譬如 cmd=F_DUPFD (复制文件描述符)将返回一个新的文件描述符、 cmd=F_GETFD (获取文件描述符标志)将返回文件描述符标志、cmd=F_GETFL (获取文件状态标志)将返回文件状态标志等。
fcntl 使用示例
(1)复制文件描述符
        前面给大家介绍了 dup dup2 ,用于复制文件描述符,除此之外,我们还可以通过 fcntl 函数复制文件描述符, 可用的 cmd 包 括 F_DUPFD F_DUPFD_CLOEXEC , 这 里 就 只 介 绍 F_DUPFD ,F_DUPFD_CLOEXEC 暂时先不讲。
        当 cmd=F_DUPFD 时,它的作用会根据 fd 复制出一个新的文件描述符,此时需要传入第三个参数,第 三个参数用于指出新复制出的文件描述符是一个大于或等于该参数的可用文件描述符(没有使用的文件描述符);如果第三个参数等于一个已经存在的文件描述符,则取一个大于该参数的可用文件描述符。
测试:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main(void) {
 int fd1, fd2;
 int ret;
 /* 打开文件 test_file */
 fd1 = open("./test_file", O_RDONLY);
 if (-1 == fd1) {
     perror("open error");
     exit(-1);
 }
 /* 使用 fcntl 函数复制一个文件描述符 */
 fd2 = fcntl(fd1, F_DUPFD, 0);
 if (-1 == fd2) {
     perror("fcntl error");
     ret = -1;
     goto err;
 }
 printf("fd1: %d\nfd2: %d\n", fd1, fd2);
 ret = 0;
 close(fd2);
err:
 /* 关闭文件 */
 close(fd1);
 exit(ret);
}
}
        在当前目录下存在 test_file 文件,上述代码会打开此文件,得到文件描述符 fd1 ,之后再使用 fcntl 函数复制 fd1 得到新的文件描述符 fd2 ,并将 fd1 fd2 打印出来,接下来编译运行:
        可知复制得到的文件描述符是 4 ,因为在执行 fcntl 函数时,传入的第三个参数是 0 ,也就时指定复制得到的新文件描述符必须要大于或等于 0 ,但是因为 0~3  都已经被占用了,所以分配得到的 fd 就是 4 ;如果传入的第三个参数是 100 ,那么 fd2 就会等于 100 ,大家可以自己动手测试。
(2)获取/设置文件状态标志
        cmd=F_GETFL 可用于获取文件状态标志, cmd=F_SETFL 可用于设置文件状态标志。 cmd=F_GETFL 时不需要传入第三个参数,返回值成功表示获取到的文件状态标志;cmd=F_SETFL 时,需要传入第三个参数,此参数表示需要设置的文件状态标志。这些标志指的就是我们在调用 open 函数时传入的 flags 标志,可以指定一个或多个(通过位或 | 运算符组合),但是文件权限标志(O_RDONLY、O_WRONLY、O_RDWR)以及文件创建标志(O_CREAT、
O_EXCL O_NOCTTY O_TRUNC )不能被设置、会被忽略;在 Linux 系统中,只有 O_APPEND O_ASYNC、O_DIRECT、O_NOATIME 以及 O_NONBLOCK 这些标志可以被修改,这里面有些标志并没有给大家介绍过,后面我们在用到的时候再给大家介绍。所以对于一个已经打开的文件描述符,可以通过这种方式添加或移除标志。
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MSG_TRY "try again\n"

int main(void)
{
	char buf[10];
	int flags, n;

	flags = fcntl(STDIN_FILENO, F_GETFL); //获取stdin属性信息
	if(flags == -1){
		perror("fcntl error");
		exit(1);
	}
	flags |= O_NONBLOCK;
	int ret = fcntl(STDIN_FILENO, F_SETFL, flags);
	if(ret == -1){
		perror("fcntl error");
		exit(1);
	}

tryagain:
	n = read(STDIN_FILENO, buf, 10);
	if(n < 0){
		if(errno != EAGAIN){		
			perror("read /dev/tty");
			exit(1);
		}
		sleep(3);
		write(STDOUT_FILENO, MSG_TRY, strlen(MSG_TRY));
		goto tryagain;
	}
	write(STDOUT_FILENO, buf, n);

	return 0;
}

 

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Linux 系统下串口编程可以使用 C 或 C++ 语言来实现。 首先,需要在系统中打开串口设备文件,可以使用 `open` 函数打开串口设备文件,例如: ``` int fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY | O_NDELAY); if (fd < 0) { perror("Open serial port failed!"); return -1; } ``` 然后,可以使用 `ioctl` 函数来设置串口的相关参数,例如波特率、数据位、停止位和校验位等。 接着,可以使用 `read` 和 `write` 函数来进行串口的数据读写。 最后,在结束程序时,需要使用 `close` 函数关闭串口设备文件。 需要注意的是,在 Linux 系统中,串口设备文件的名称可能不同,例如 `/dev/ttyS0`、`/dev/ttyUSB0` 等,需要根据实际系统情况进行设置。 ### 回答2: Linux下的串口编程主要是通过Linux提供的串口设备驱动程序进行的,下面是一个基本的串口编程流程: 1. 打开串口设备:通过打开串口设备文件,比如/dev/ttyS0或/dev/ttyUSB0,使用open系统调用函数。 2. 配置串口参数:通过设置串口的各种参数,比如波特率、数据位、停止位、校验等,使用tcgetattr和tcsetattr系统调用函数。 3. 读写数据:使用read和write系统调用函数进行串口数据的读取和写入,可以通过设置超时时间避免阻塞。 4. 关闭串口设备:使用close系统调用函数关闭已打开的串口设备。 在Linux中,还可以使用库函数实现更方便的串口编程,比如使用C语言的termios库或者C++语言的boost库。这些库提供了更高级的接口和封装,简化了串口编程的操作。 对于串口编程,需要注意以下几点: 1. 在进行串口编程前,需要确认串口设备是否可用,并设置相应的用户权限。 2. 在配置串口参数时,需要保证与外部设备相匹配,否则数据的接收和发送可能会出错。 3. 在读写数据时,需要处理数据的格式转换,比如将字符型数据转为整型数据,在进行数据的解析和处理时注意数据的格式。 总之,Linux下的串口编程是基于底层的设备驱动程序进行的,通过打开设备文件、配置参数,使用读写系统调用函数实现对串口的数据读写。使用库函数可以更加方便地进行串口编程。 ### 回答3: Linux下串口编程是指在Linux操作系统中使用C或C++语言编写程序来操作串口通信。开发者可以通过串口编程来实现与外部设备的数据交互,例如与传感器、单片机、嵌入式系统等进行数据通信。 在Linux中,串口设备通常以设备文件的形式存在于/dev目录下,例如/dev/ttyS0表示串口0,/dev/ttyUSB0表示USB串口设备。通过打开设备文件,就可以和串口进行读写操作。 在进行串口编程时,需要借助相关的头文件和库函数,例如#include <stdio.h>、#include <fcntl.h>等。首先需要打开串口设备文件,可以使用open函数来打开。然后使用ioctl函数来设置串口参数,例如波特率、数据位、停止位和校验位等。接下来就可以使用read函数来读取串口数据,或者使用write函数来向串口发送数据。 在编程过程中,还可以利用select函数或者线程来监听和处理串口数据的接收和发送。通过select函数能够实现异步操作,当串口有数据可读或者可以写入时,就可以进行相应的处理。 在编写串口程序时,需要注意串口的配置参数和通信协议的一致性。正确设置波特率、数据位、停止位和校验位等参数,以保证数据的正确传输。同时,还需要注意对串口的互斥访问,避免多个程序同时对同一个串口进行操作导致冲突。 总之,Linux下的串口编程是一项常见的嵌入式系统开发任务,通过编写相应的程序代码,可以实现与外部设备的串口通信,实现数据的发送和接收。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值