fcntl函数讲解

前言

fcntl系统调用可以用来对已打开的文件描述符进行各种控制操作以改变已打开文件的的各种属性

函数原型:

#include<unistd.h>
#include<fcntl.h>
int fcntl(int fd, int cmd);
int fcntl(int fd, int cmd, long arg);
int fcntl(int fd, int cmd ,struct flock* lock);

int fcntl(int fd, int cmd, ... /* arg */ );

(经常用这个fcntl函数改变非阻塞)

参数:
fd:文件描述符
cmd:设置的命令
F_GETFL(常用)
F_SETFL(常用)
arg:可有可无,由第二个参数决定,比如get时候没有,set时候有值
返回值:
文件状态标志
-1 :失败

步骤:
int flag;
flag = fcntl(sockfd, F_GETFL, 0);
flag |= O_NONBLOCK;
fcntl(sockfd, F_SETFL, flag);

若你想设置回阻塞,只需要把Flags改为iFlags&~O_NONBLOCK即可。



另外一个常用的作用就是使用文件锁

概述

在多数unix系统中,当多个进程/线程同时编辑一个文件时,该文件的最后状态取决于最后一个写该文件的进程。

对于有些应用程序,如数据库,各个进程需要保证它正在单独地写一个文件。这时就要用到文件锁。

文件锁(也叫记录锁)的作用是,当一个进程读写文件的某部分时,其他进程就无法修改同一文件区域。

能够实现文件锁的函数主要有2个:flock和fcntl

早期的伯克利版本只支持flock,该函数只能对整个文件加锁,不能对文件的一部分加锁。

fcntl是在flock基础上构造的函数,它提供了一个简化的接口。它们允许对文件中任意字节区域加锁,短至一个字节,长至整个文件。

int fcntl(int fd, int cmd ,struct flock* lock);

成功则返回0,若有错误则返回-1,错误原因存于errno。

函数传入值cmd
F_DUPFD:复制一个现存的描述符
F_GETFD:获得fd的close-on-exec(执行时关闭)文件描述符标志,若标志未设置,则文件经过exec()函数之后仍保持打开状态
F_SETFD:设置close-on-exec 标志,该标志由参数arg 的FD_CLOEXEC位决定
F_GETFL:得到open设置的标志
F_SETFL :改变open设置的标志
F_GETLK:根据lock参数值,决定是否可以上文件锁
F_SETLK:设置lock参数值的文件锁

这里我们要了解一个结构体:

struct flock {
short l_type;/* one of F_RDLCK, F_WRLCK, F_UNLCK /
short l_whence;/
SEEK_SET, SEEK_CUR, SEEK_END /
off_t l_start;/
offset in bytes, relative to l_whence /
off_t l_end;/
length, in bytes, 0 means lock to EOF /
off_t l_pid;/
returned with F_GETLK */
};
在这里插入图片描述

其中,

  1. 锁类型:共享读锁F_RDLCK,独占性写锁F_WRLCK,解锁F_UNLCK
  2. 加锁或解锁区域的起始字节偏移量(l_start, l_whence)
  3. 区域字节长度(L_len)
  4. 进程的id持有的锁能阻塞当前进程,仅由F_GETLK返回
  5. 锁可以在文件尾处开始或者越过尾端开始,但是不能在文件起始位置之前开始
  6. 若l_len=0, 表示锁的范围可以扩大到最大可能偏移量,这意味着不管向文件中追加多少数据,它们都可以处于锁的范围内,而且起始位置可以任意
  7. 设置l_start和l_whence指向文件的起始位置,并且指定l_len=0,以实现对整个文件加锁(一般l_start=0, l_whence=SEEK_SET)
重点了解一下cmd的方式:
  1. F_GETLK:判断由flockptr所描述的锁是否会被另一把锁所排斥(阻塞),如果存在一把锁阻止创建由flockptr所描述的锁,由该现有锁的信息将重写flockptr指向的信息。如果不存在这种情况,则除了将l_type设置为F_UNLCK之处,flockptr所指向结构中的其他信息保持不变
  2. F_SETLK:设置由flockptr所描述的锁,如果程序试图获得一把锁,而系统阻止程序获得该锁,则fcntl会立即返回错误,errno设置为EACCES或EAGAIN。当l_type=F_UNLCK时,此命令用来清除指定的锁
  3. F_SETLKW:F_SETLK的阻塞版本(wait)。如果程序尝试获得的锁无法被授予,调用进程会进入休眠直到进程获得锁或者信号中断

注意:用F_GETLK 测试能否创建一把锁,然后用F_SETLK尝试建立锁之间并非原子操作,也就是说两次调用之间有可能另一进程插入并创建了相同的锁。如果不希望在等待锁变为可用时产生阻塞,就必须处理由F_SETLK返回的可能出错值

我们要清楚这个操作不是原子性的,也就是说在用F_GETLK 测试能否创建一把锁,然后用F_SETLK尝试建立锁之间,两次调用之间有可能另一进程插入并创建了相同的锁。所以我们都必须要处理一下由F_SETLK返回的值。

要点:

  1. 任意多个进程在一个给定的字节上可以有一把共享的读锁(F_RDLCK),但是在一个给定的字节上只能有一个进程有一把独占性写锁(F_WRLCK)
  2. 如果在一个给定字节上已经有一把或多把读锁,则不能在该字节上再加写锁,如果在一个字节上已经有一把独占性写锁,则不能再对它加任何读锁
  3. 对于单个进程而言,如果进程对某个文件区域已经有了一把锁,然后又试图在相同区域再加一把锁,则新锁会替换旧锁
  4. 加读锁时,该描述符必须是读打开,加写锁时,该描述符必须是写打开


不错的博客,请点击!

  • 12
    点赞
  • 68
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
`i2c_smbus_write_byte_data()`函数是Linux提供的用于向I2C从设备写入一个字节数据的API函数,其函数原型如下: ```c __s32 i2c_smbus_write_byte_data(struct i2c_client *client, __u8 command, __u8 value); ``` 该函数的三个参数含义如下: - `client`:指向I2C从设备的`struct i2c_client`结构体指针。 - `command`:表示要向从设备写入的寄存器地址。 - `value`:表示要写入的数据。 函数返回值为操作结果,返回0表示成功,否则表示失败。 该函数实现的是SMBus协议中的“写字节数据”操作。在执行该函数前,需要先通过`ioctl(fd, I2C_SLAVE, addr)`函数将I2C总线上的设备地址设置为从设备的地址。 该函数的具体实现过程如下: 1. 通过`i2c_smbus_xfer()`函数向从设备发送一个SMBus消息,消息类型为“写字节数据”。 2. 在SMBus消息中,第一个字节表示要写入的寄存器地址(即`command`参数),第二个字节表示要写入的数据(即`value`参数)。 3. 如果操作成功,则返回0;否则返回错误码。 以下是一个简单的使用`i2c_smbus_write_byte_data()`函数向I2C从设备写入一个字节数据的例程: ```c #include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <linux/i2c-dev.h> #include <unistd.h> #define I2C_ADDR 0x50 // 从设备地址 int main() { int fd; int ret; char buf[10]; __u8 command = 0x10; // 寄存器地址 __u8 value = 0x55; // 待写入的数据 fd = open("/dev/i2c-1", O_RDWR); if (fd < 0) { printf("Failed to open I2C bus\n"); return -1; } if (ioctl(fd, I2C_SLAVE, I2C_ADDR) < 0) { printf("Failed to set I2C address\n"); close(fd); return -1; } ret = i2c_smbus_write_byte_data(fd, command, value); if (ret < 0) { printf("Failed to write byte data\n"); close(fd); return -1; } close(fd); return 0; } ``` 需要注意的是,该函数仅适用于向从设备写入一个字节数据。如果要写入多个字节数据,需要使用其他的API函数,如`i2c_smbus_write_i2c_block_data()`函数
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值