linux系统编程之文件与io(五)
上一节中已经学习了文件描述符的复制,复制方法有三种,其中最后一种fcntl还并未使用到,关于这个函数,不光只有复制文件描述符的功能,还有其它一些用法,本节就对其进行一一剖析:

fcntl常用操作:

这里,我们将上节当中用dup或dup2实现复制文件描述符改用fcntl,程序如下:

先将test2.txt的内容清空,以便进行测试,编译运行:


通过man来查看下它的说明:

【说明:关于这一的操作命令,等之后学到进程时再来学习,先这边记录一下】

上一节也有介绍过,先回顾一下都有哪些状态标志:

也就是说,通过这个命令,能更改文件状态标志,说来有些难理解,下面以实例代码来进行一一说明:

编译运行:

当输入内容时,read才读取完,并打印出输入的内容:

这时本来的文件状态,但是,可以fcntl函数,来改变这种阻塞状态为非阻塞状态,如下:

具体代码:
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while()
int main(int argc, char *argv[])
{
char buf[] = {};
int ret;
int flags;
flags = fcntl(0, F_GETFL, 0);//通过F_GETFL来获得标准输入的状态
if (flags == -1)
ERR_EXIT("fcntl get flag error"); ret = fcntl(0, F_SETFL, flags | O_NONBLOCK);//通过F_SETFL来改变文件的状态为非阻塞0_NONBLOCK,但是为了保留其它状态,所以设置之前需获得状态,再进行与操作
if (ret == -1)
ERR_EXIT("fcntl set flag error"); ret = read(, buf, );
if (ret == -)
ERR_EXIT("read error"); printf("buf=%s\n", buf);
return ;
}
编译运行:

实际上,对于上面这个错误,对应的错误代码是:

【注意:在设置状态时,一定得先用F_GETFL获取状态,然后再去用F_SETFL去设置,因为我们只想设置非阻塞的状态,对于其它状态如:写状态,读状态等想保留】
关于上面这段设置状态的代码,可以进行封装,以便进行复用,如下:
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h> #include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h> #define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while() void set_flag(int fd, int flags);//设置文件状态标志函数声明 int main(int argc, char *argv[])
{
char buf[] = {};
int ret; set_flag(, O_NONBLOCK);//这时,经过代码封装之后,代码就显得比较干净了 ret = read(, buf, );
if (ret == -)
ERR_EXIT("read error"); printf("buf=%s\n", buf);
return ;
}
//设置文件状态标志
void set_flag(int fd, int flags)
{
int val;
val = fcntl(fd, F_GETFL, 0);
if (val == -1)
ERR_EXIT("fcntl get flag error");
val |= flags;
if (fcntl(fd, F_SETFL, val) < 0)
ERR_EXIT("fcntl set flag error");
}

另外,我们还可以封装一个清除文件状态标志的函数,跟设置很类似,如下:
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h> #include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h> #define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while() void set_flag(int fd, int flags);
void clr_flag(int fd, int flags);//清除文件状态标志函数声明 int main(int argc, char *argv[])
{
char buf[] = {};
int ret; set_flag(, O_NONBLOCK);
clr_flag(, O_NONBLOCK);//清除了非阻塞的状态标记,也就是等于最终还是阻塞状态 ret = read(, buf, );
if (ret == -)
ERR_EXIT("read error"); printf("buf=%s\n", buf);
return ;
} void set_flag(int fd, int flags)
{
int val;
val = fcntl(fd, F_GETFL, );
if (val == -)
ERR_EXIT("fcntl get flag error");
val |= flags;
if (fcntl(fd, F_SETFL, val) < )
ERR_EXIT("fcntl set flag error");
}
//清除文件状态标志
void clr_flag(int fd, int flags)
{
int val;
val = fcntl(fd, F_GETFL, 0);
if (val == -1)
ERR_EXIT("fcntl get flag error");
val &= ~flags;
if (fcntl(fd, F_SETFL, val) < 0)
ERR_EXIT("fcntl set flag error");
}
编译运行:

【提示:设置状态中的:val |= flags;和清除状态中的:val &= ~flags;是怎么一回事,可以看一下C程序中的位操作,这个也可以参考:http://www.cnblogs.com/webor2006/p/3440026.html】


先来解释一下结构体字段:



说了这么多,可能还是有点不是很好理解,下面以实际代码来进行说明:
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h> #include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h> #define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while() int main(int argc, char *argv[])
{
int fd;
fd = open("test2.txt", O_CREAT | O_RDWR | O_TRUNC, );
if (fd == -)
ERR_EXIT("open error"); struct flock lock;
memset(&lock, 0, sizeof(lock));
lock.l_type = F_WRLCK;//加上排它锁
lock.l_whence = SEEK_SET;
lock.l_start = 0;
lock.l_len = 0; if (fcntl(fd, F_SETLK, &lock) == )
{
printf("lock success\n");
printf("press any key to unlock\n");
getchar();
lock.l_type = F_UNLCK;//释放锁
if (fcntl(fd, F_SETLK, &lock) == )
printf("unlock success\n");
else
ERR_EXIT("unlock fail");
}
else
ERR_EXIT("lock fail");
return ;
}
编译运行:


实际上,上面出错的错误代码也是EAGAIN,查看一下fcntl函数:

【注意:如果要给文件加读锁,则文件需要有读的权限;如果要给文件写写锁,则文件也需要有写的权限】
另外,设置文件锁,还有另外一种操作命令:F_SETLKW,它跟F_SETLK有啥区别呢?且看下代码:

编译运行:


【总结:F_SETLK设置锁时,如果进程没有成功设置上锁,则会立马给出错误提示;F_SETLKW设置锁时,如果进程没有成功设置上锁,会阻塞,类似于线程的同步一样,当对方的锁释放时,则才可以对文件进行上锁】
另外,如果想获得阻塞进程的ID,可以用F_GETLK,它会将id保存在flock结构体中的l_pid当中,关于这个,就不做实验了,比较简单,好了,关于linux系统编程中的文件/IO,就告一段落了,下回会进入linux系统编程的新的东东,下回见!
linux系统编程之文件与io(五)的更多相关文章
- linux系统编程之文件与io(一)
经过了漫长的学习,C语言相关的的基础知识算是告一段落了,这也是尝试用写博客的形式来学习c语言,回过头来看,虽说可能写的内容有些比较简单,但是个人感觉是有史起来学习最踏实的一次,因为里面的每个实验都是自 ...
- linux系统编程之文件与io(四)
今天继续学习文件与io,主要是学习文件共享及文件.复制文件描述符,有点抽象,主要是概念上的理解,但是很重要,下面一一来分解: 文件共享: 回顾一下,在linux系统调用中,是通过文件描述符来访问文件的 ...
- linux系统编程之文件与io(二)
今天继续学习文件与io,话不多说,开始进入正题: 文件的read和write系统调用: 说明:函数中出现在size_t和ssize_t是针对系统定制的数据类型: 下面以一个实现文件简单拷贝的示 ...
- linux系统编程之文件与IO(五):stat()系统调用获取文件信息
一.stat()获取文件元数据 stat系统调用原型: #include <sys/stat.h> int stat(const char *path, struct stat *buf) ...
- linux系统编程之文件与IO(一):文件描述符、open,close
什么是IO? 输入/输出是主存和外部设备之间拷贝数据的过程 设备->内存(输入操作) 内存->设备(输出操作) 高级I/O ANSI C提供的标准I/O库称为高级I/O,通常也称为带缓冲的 ...
- linux系统编程之文件与IO(七):时间函数小结
从系统时钟获取时间方式 time函数介绍: 1.函数名称: localtime 2.函数名称: asctime 3.函数名称: ctime 4.函数名称: difftime 5.函数名称: gmtim ...
- linux系统编程之文件与IO(四):目录访问相关系统调用
1. 目录操作相关的系统调用 1.1 mkdir和rmdir系统调用 1.1.1 实例 1.2 chdir, getcwd系统调用 1.2.1 实例 1.3 o ...
- linux系统编程之文件与IO(三):利用lseek()创建空洞文件
一.lseek()系统调用 功能说明: 通过指定相对于开始位置.当前位置或末尾位置的字节数来重定位 curp,这取决于 lseek() 函数中指定的位置 函数原型: #include <sys/ ...
- linux系统编程之文件与io(三)
上次我们利用文件的read和write来实现了简易的cp命令,其中将源文件拷贝到目标文件时,我们给目标文件的权限是写死的,而非根据源文件的权限生成的,如下: 今天就来解决这个问题,来学习获取文件权限相 ...
随机推荐
- python:封装连接数据库方法
config.py # 数据库测试环境 name = '***' password = '******' host_port_sid = '10.**.*.**:1521/bidbuat' Oracl ...
- 第16届(2019)全国大学生信息安全与对抗技术竞赛全国线下总决赛 Writeup
笔者<Qftm>原文发布<BitHack>:https://bithack.io/forum/469/answer/333 0x00 Begin 关于 ISCC 2019 北理 ...
- PHP设计模式 - 模板方法模式
模板模式准备一个抽象类,将部分逻辑以具体方法以及具体构造形式实现,然后声明一些抽象方法来迫使子类实现剩余的逻辑.不同的子类可以以不同的方式实现这些抽象方法,从而对剩余的逻辑有不同的实现.先制定一个顶级 ...
- quartz2.3.0(五)制定错过执行任务的misfire策略,用pause,resume模拟job暂停执行和继续执行
感谢兄台: <quartz-misfire 错失.补偿执行> misfire定义 misfire:被错过的执行任务策略 misfire重现——CronTrigger job任务类: pac ...
- [LOJ2065] [SDOI2016]模式字符串
题目链接 洛谷:https://www.luogu.org/problemnew/show/P4075 LOJ:https://loj.ac/problem/2065 Solution 这种题看起来就 ...
- go语言学习---使用os.Args获取简单参数(命令行解析)
实例1: //main package main import ( "fmt" "os" ) func main() { fmt.Println(os.Args ...
- IDENTITY、SCOPE_IDENTITY、IDENT_CURRENT的分析
https://www.cnblogs.com/daihuiquan/archive/2013/03/18/2956845.html IDENT_CURRENT.IDENTITY.SCOPE_IDEN ...
- python 基础(实例1——登陆小游戏)
一个简单的登陆小游戏,输入用户名和密码,如果和user_passwd.txt中内容匹配,则打印“welcome to login...”,如果三次输入错误则将该用户列入黑名单,无法再用该用户名登陆. ...
- 修改CentOS默认yum源为国内镜像
参考文档 https://blog.csdn.net/inslow/article/details/54177191 国内主要开源的开源镜像站点应该是网易和阿里云了. 修改为163yum源-mirro ...
- 定时任务 Quarzt
Quartz.Net是一个从java版的Quartz移植过来的定时任务框架,可以实现异常灵活的定时任务. Quartz 有三个概念分别是 计划者(ISchedeler).工作(IJob).触发器(Tr ...