《Linux C编程实战》笔记:一些系统调用

目录

dup和dup2函数

fcntl函数

示例程序1

示例程序2

ioctl函数


dup和dup2函数

#include <unistd.h>
int dup(int oldfd);
int dup2(int oldfd, int newfd):
  • dup 函数复制 oldfd 参数所指向的文件描述符。

  • 参数:

    • oldfd:要复制的文件描述符的整数值。
  • 返回值: 返回新的文件描述符,如果出错,则返回 -1,并设置 errno

新的文件描述符和oldfd共享所有的锁定,读写指针和各项权限和标志位,如果用lseek对某个文件描述符操作,另一个文件描述符的读写指针也会跟着变动

  • dup2 函数将 newfd 参数指定的文件描述符设置为 oldfd 参数指定的文件描述符的副本。

  • 参数:

    • oldfd:要复制的文件描述符的整数值。
    • newfd:要设置为副本的新文件描述符的整数值。
  • 返回值: 返回新的文件描述符(即 newfd),如果出错,则返回 -1,并设置 errno

示例

#include <stdio.h>
#include <unistd.h>int main() {int fd1 = open("file.txt", O_RDONLY);int fd2 = open("newfile.txt", O_WRONLY | O_CREAT, 0644);dup2(fd1, 10);  // 将文件描述符 10 设置为 fd1 的副本dup2(fd2, 11);  // 将文件描述符 11 设置为 fd2 的副本// 现在文件描述符 10 与 fd1 指向相同的文件,文件描述符 11 与 fd2 指向相同的文件close(fd1);close(fd2);return 0;
}

若newfd已经被程序使用,系统会将其关闭以释放该文件描述符;若newfd==oldfd,则不会关闭该文件。

fcntl函数

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

功能根据cmd的值的不同而不同,失败一般返回-1

复制文件描述符(F_DUPFD):

int new_fd = fcntl(old_fd, F_DUPFD, 0);

这个命令用于复制文件描述符 old_fd,并返回一个新的文件描述符 new_fd

fcntl 函数的 F_DUPFD 命令中,第三个参数(常被称为 arg)是用于指定新的文件描述符的最小值。这个参数告诉系统在哪里开始搜索未使用的文件描述符来复制。如果指定的值是 0,系统会选择一个未使用的最小文件描述符号。如果你希望指定一个特定的文件描述符,可以传递你想要的值。

F_GETFD

int flags = fcntl(fd, F_GETFD);

用于获取文件描述符 fd 的标志,例如 close-on-exec 标志。

如果 FD_CLOEXEC 标志被设置,说明文件描述符在 exec 被调用时会被关闭。否则,说明文件描述符会保持打开状态。

可以这样判断

int flags = fcntl(fd, F_GETFD);
if (flags & FD_CLOEXEC) {printf("FD_CLOEXEC flag is set.\n");} else {printf("FD_CLOEXEC flag is not set.\n");}

F_SETFD

fcntl(fd, F_SETFD, FD_CLOEXEC);//比如arg是FD_CLOEXEC

用于设置文件描述符 fd 的标志,例如设置 close-on-exec 标志。

F_GETFL

int flags = fcntl(fd, F_GETFL);

用于获取文件描述符 fd 的文件状态标志,如读写模式和非阻塞标志。

F_SETFL

fcntl(fd, F_SETFL, O_NONBLOCK);

用于设置文件描述符 fd 的文件状态标志,如设置非阻塞标志。

Linux系统只能设置O_APPEND,O_NONBLOCK,O_ASYNC,含义与open函数里的一致

示例程序1

演示fcntl的一些功能

#include <cstdio>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<cerrno>
using namespace std;
//自定义错误处理函数
void my_err(const char *err_string,int line){fprintf(stderr,"line:%d ",line);perror(err_string);exit(1);
}
int main(){int ret;int access_mode;int fd;if((fd=open("exanple_64",O_CREAT|O_TRUNC|O_RDWR,S_IRWXU))==-1)my_err("open",__LINE__);//设置文件打开方式if((ret=fcntl(fd,F_SETFL,O_APPEND))==-1)my_err("fcntl",__LINE__);//获取文件打开方式if((ret=fcntl(fd,F_GETFL,0))==-1)//0可以去掉my_err("fcntl",__LINE__);access_mode=ret&O_ACCMODE;if(access_mode==O_RDONLY)printf("example_64 access mode:read only");else if(access_mode==O_WRONLY)printf("example_64 access mode:write only");else if(access_mode==O_RDWR)printf("example_64 access mode:read+write");if(ret&O_APPEND)printf(" ,append");if(ret&O_NONBLOCK)printf(",nonblock");if(ret&O_SYNC)printf(",sycn");printf("\n");return 0;
}

运行结果是

结果解释:虽然open的时候设置的是O_TRUNC,是将文件截断为0,但是后面的代码用fcntl修改打开方式为O_APPEND,所以O_APPEND是存在的。(不过效果依然是截断,文件会清空。因为open的时候设置的是截断)

ret&O_ACCMODE是取得文件打开方式的掩码,它其实就是3 。因为文件打开方式有三种,是2位的,所以需要&11(二进制),也就是3 。

if(ret & O_APPEND) 这句代码用于检查文件状态标志中是否设置了 O_APPEND 标志。

接下来的fcntl函数3种功能都和文件记录锁有关,因此先介绍一下文件记录锁。
当有多个进程同时对某一文件进行操作时,就有可能发生数据的不同步,从而引起错误,该文件的最后状态取决于写该文件的最后一个程序。但是对于有些应用程序,如数据库,有时进程需要确保它正在单独写一个文件。为了向进程提供这种功能,Linux系统提供了记录锁机制。
Linux 的文件记录锁能提供非常详细的控制,它能对文件的某一区域进行文件记录锁的控制。当fentl用于管理文件记录锁的操作时,第三个参数指向一个struct flock *lock的结构:

struct flock {short  l_type;   /* 锁的类型: F_RDLCK(共享读锁)、F_WRLCK(独占写锁)、F_UNLCK(释放锁) */short  l_whence; /* l_start 的解释方式: SEEK_SET(相对于文件的起始位置)、SEEK_CUR(相对于当前文件位置)、SEEK_END(相对于文件的末尾) */off_t  l_start;  /* 锁的起始位置,即锁定范围的起始偏移量 */off_t  l_len;    /* 锁定的字节数,即锁定范围的长度 */pid_t  l_pid;    /* 执行 F_GETLK 命令时,包含持有锁的进程的进程ID */
};
  • l_type:锁的类型,可以是以下之一:

    • F_RDLCK:共享读锁。
    • F_WRLCK:独占写锁。
    • F_UNLCK:释放锁。
  • l_whence:指定 l_start 的解释方式,可以是以下之一:

    • SEEK_SET:相对于文件的起始位置。
    • SEEK_CUR:相对于当前文件位置。
    • SEEK_END:相对于文件的末尾。
  • l_start:锁的起始位置,即锁定范围的起始偏移量。

  • l_len:锁定的字节数,即锁定范围的长度。

    l_len 字段为0通常表示锁定或解锁整个文件。

  • l_pid:在执行 F_GETLK 命令时,将包含持有锁的进程的进程ID。

多个进程在一个给定的字节上可以有一把共享的读锁,但是在一个给定字节上的写锁则只能由一个进程单独使用。进一步而言,如果在一个给定字节上已经有一把或多把读锁,则不能在该字节上再加写锁;如果在一个字节上已经有一把独占性的写锁,则不能再对它加任何读锁(锁的不兼容规则)。一个进程只能设置某一文件区域上的一种锁。如果某一文件区域已经存在文件记录锁了,则如果此时再设置新的锁在该区域的话,旧的锁将会被新的锁取代。

为了锁整个文件,通常的方法是将l_start说明为0,l_whence说明为SEEK_SET,l_len说明为0。

F_GETLK - 获取文件锁信息:

struct flock lock;

fcntl(fd, F_GETLK, &lock);

此命令用于获取文件描述符 fd 上的锁信息,这些信息会被存储在 struct flock 结构体中。通过检查结构体中的字段,可以了解关于文件锁的信息,例如锁定的类型、锁定范围等。

F_SETLK - 设置文件锁:

fcntl(fd, F_SETLK, &lock);

此时,fcntl系统调用被用来设置或释放锁,当l_type取 F_RDLCK 或F_WDLCK时,在由l_whence、l_ start和l_len指定的区域上设置锁;当l_type取F_UNLCK时则释放锁。如果锁被其他进程占用,则返回-1并设置errno为 EACCES 或EAGAIN。
需要注意的是,当设置一个共享锁(读锁)时,第一个参数fd所指向的文件必须以可读方式打开;当设置一个互斥锁(写锁)时,第一个参数fd所指向的文件必须以可写方式打开;当设置两种锁时,第一个参数fd所指向的文件必须以可读可写方式打开。当进程结束或文件描述符fd被close系统调用时,锁会自动释放。

F_SETLKW - 设置文件锁并等待:

fcntl(fd, F_SETLKW, &lock);

此命令与 F_SETLK 类似,但是如果无法获取所请求的锁,F_SETLKW 会使调用进程进入阻塞状态,直到可以获取锁为止。这可以用于在获取锁之前等待其他进程释放锁。

成功返回0,失败返回-1。

示例程序2

#include<iostream>
#include<cstring>
#include <cstdio>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<cerrno>
using namespace std;
//自定义错误处理函数
void my_err(const char *err_string,int line){fprintf(stderr,"line:%d ",line);perror(err_string);exit(1);
}
int lock_set(int fd,struct flock *lock){if(fcntl(fd,F_SETLK,lock)==0){//set success if(lock->l_type==F_RDLCK)printf("set read lock,pid:%d\n",getpid());else if(lock->l_type==F_WRLCK)printf("set write lock,pid:%d\n",getpid());else if(lock->l_type==F_UNLCK)printf("release lock,pid:%d",getpid());}else{//failperror("lock operation fail\n");return -1;}return 0;
}
//test,only success return 0
int lock_test(int fd,struct flock *lock){if(fcntl(fd,F_GETLK,lock)==0){//successif(lock->l_type==F_UNLCK){//释放可行printf("lock can be set in fd");return 0;}else{//已经有其他锁了,则失败if(lock->l_type==F_RDLCK)printf("can't set lock,read lock has been set by:%d\n",lock->l_pid);else if(lock->l_type==F_WRLCK)printf("can't be set lock,write lock has been set by:%d\n",lock->l_pid);return -2;}}else{perror("get incompatible locks fail");return -1;}
}
int main(){int fd;int ret;struct flock lock;char read_buf[32];if((fd=open("example_65",O_CREAT|O_TRUNC|O_RDWR,S_IRWXU))==-1)my_err("open",__LINE__);if(write(fd,"test lock",10)!=10)my_err("write",__LINE__);//init lockmemset(&lock,0,sizeof(struct flock));lock.l_start = 0;lock.l_whence = SEEK_SET;lock.l_len=0;//set read locklock.l_type=F_RDLCK;if(lock_test(fd,&lock)==0){//test succeededlock.l_start=0;lock.l_whence=SEEK_SET;lock.l_len=0;lock.l_type=F_RDLCK;lock_set(fd,&lock);}//read datalseek(fd,0,SEEK_SET);if((ret=read(fd,read_buf,10))==-1)my_err("read",__LINE__);read_buf[ret]='\0';printf("%s\n",read_buf);getchar();//set write locklock.l_type=F_WRLCK;if(lock_test(fd,&lock)==0){lock.l_start=0;lock.l_whence=SEEK_SET;lock.l_len=0;lock.l_type=F_WRLCK;lock_set(fd,&lock);}//releaselock.l_type=F_UNLCK;lock_set(fd,&lock);close(fd);return 0;
}

运行结果:

结果解释:首先测试能不能在文件上加读锁,测试成功,所以加了读锁,然后读文件内容为:test lock。之后测试能不能加写锁,发现可以,就加了写锁。最后释放锁。

注意这是在同一个进程内的,所以先加读锁再加写锁,会覆盖写锁。但是如果是不同进程就不可以先加读锁再加写锁。

为了演示锁的不兼容性,这次程序在不同终端执行

再在另一个终端执行

可以看到两个读锁都可以设置

这时候在第二个终端任意按一个键,按理说接下来要设置写锁,但是

失败了,因为进程1已经设置了读锁,进程2就不能设置写锁了。

现在,既然进程2已经释放锁了,我们回到进程1按下按键试试

因为进程2的锁已经释放了,只有进程1本进程的读锁,同一进程可以先读锁后写锁,所以成功。

fcntl还有一些功能如下图

ioctl函数

#include <sys/ioctl.h>
int ioctl (int fd, int request,..)

我暂时也看不懂示例程序,所以这个函数就跳过吧

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/227008.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[笔记] wsl 下使用 qemu/grub 模拟系统启动(单分区)

背景 最近在学习操作系统&#xff0c;需要从零开始搭建系统&#xff0c;由于教程中给的虚拟机搭建的方式感觉还是过于重量级&#xff0c;因此研究了一下通过 qemu 模拟器&#xff0c;配合 grub 完成启动系统的搭建。 qemu 介绍 qemu 是一款十分优秀的系统模拟器&#xff0c;…

@PostMapping接收String类型的参数

接口这样定义&#xff1a; PostMapping("/aaa") public void getById(String param)参数这样测试&#xff1a;

C++特殊类和类型转换剖析

目录 一、特殊类 1.1拒绝被拷贝的类 1.2 限制在堆上创建类 1.3 限制在栈上创建的类 1.4 不能被继承的类 二、类型转换 2.1 static_cast 2.2 reinterpret_cast 2.3 const_cast 2.4 dynamic_cast 一、特殊类 什么是特殊类&#xff1f;在普通类的设计基础上&#xff0c…

基于Java+vue的音乐网站设计与实现(源码+文档+数据库)

摘 要 在此基础上&#xff0c;提出了一种基于javavue的在线音乐排行榜系统的设计与实现方法。本系统分为两个大的功能&#xff0c;即&#xff1a;前端显示、后端管理。而在前台&#xff0c;则是播放不同的歌曲&#xff0c;让人可以在上面观看不同的歌曲&#xff0c;也可以观看…

CSS学习

CSS学习 1. 什么是css?2.css引入方式2.1 内嵌式2.2 外联式2.3 行内式2.4 引入方式特点 3. 基础选择器3.1 标签选择器3.2 类选择器3.3 id选择器3.4 通配符选择器 4. 文字基本样式4.1 字体样式4.1.1 字体大小4.1.2 字体粗细4.1.3 倾斜4.1.4 字体4.1.5 字体font相关属性连写 4.2 …

地图自定义省市区合并展示数据整合

需求一&#xff1a;将省级地图下的两个市合并成一个区域&#xff0c;中间的分割线隐藏。 1、访问下方地址&#xff0c;搜索并下载省级地图json文件。 地址&#xff1a;https://datav.aliyun.com/portal/school/atlas/area_selector 2、切换到边界生成器&#xff0c;上传刚刚下…

论文降重同义词替换的实践经验与改进建议 快码论文

大家好&#xff0c;今天来聊聊论文降重同义词替换的实践经验与改进建议&#xff0c;希望能给大家提供一点参考。 以下是针对论文重复率高的情况&#xff0c;提供一些修改建议和技巧&#xff0c;可以借助此类工具&#xff1a; 标题&#xff1a;论文降重同义词替换的实践经验与改…

Datawhale 12月组队学习 leetcode基础 day3 递归

这是一个新的专栏&#xff0c;主要是一些算法的基础&#xff0c;对想要刷leedcode的同学会有一定的帮助&#xff0c;如果在算法学习中遇到了问题&#xff0c;也可以直接评论或者私信博主&#xff0c;一定倾囊相助 进入正题&#xff0c;今天咱们要说的是递归&#xff0c;递归是是…

Qt中槽函数在那个线程执行的探索和思考

信号和槽是Qt的核心机制之一&#xff0c;通过该机制大大简化了开发者的开发难度。信号和槽属于观察者模式&#xff08;本质上是回调函数的应用&#xff09;。是函数就需要考虑其是在那个线程中执行&#xff0c;本文讨论的就是槽函数在那个线程中执行的问题。 目录 1. connect…

大数据存储技术(3)—— HBase分布式数据库

目录 一、HBase简介 &#xff08;一&#xff09;概念 &#xff08;二&#xff09;特点 &#xff08;三&#xff09;HBase架构 二、HBase原理 &#xff08;一&#xff09;读流程 &#xff08;二&#xff09;写流程 &#xff08;三&#xff09;数据 flush 过程 &#xf…

IS-IS原理与配置3

IS-IS原理与配置 • IS-IS&#xff08;Intermediate System to Intermediate System&#xff0c;中间系统到中间系统&#xff09;是ISO &#xff08;International Organization for Standardization&#xff0c;国际标准化组织&#xff09;为它的CLNP &#xff08;ConnectionL…

OSWBB 部署实现

1、OSWatcher (oswbb) 是一个可供用户下载的工具&#xff0c;可以用来抓取操作系统的性能指标。 是一组shell程序&#xff0c;程序中调用: top, vmstat, iostat, mpstat, netstat,and traceroute等os的监控工具 。OSWatcher 的使用是基于 standard licensing terms 并且不需要…

Alibaba分布式事务组件Seata XATCC实战

1. Seata XA模式实战 XA协议最主要的作用是就是定义了RM-TM的交互接口&#xff0c;XA规范除了定义的RM-TM交互的接口(XA Interface)之外&#xff0c;还对两阶段提交协议进行了优化。 1.1 整体机制 在 Seata 定义的分布式事务框架内&#xff0c;利用事务资源&#xff08;数据…

python3GUI--仿win风格天气By:PyQt5

文章目录 一&#xff0e;前言二&#xff0e;展示1.首页-白色1.首页-白色22.首页-黑色3.天气预报视频4.天气资讯-白色5.天气资讯-黑色6.收藏夹-白色7.收藏夹-黑色8.搜索9.mini-白色10.mini-黑色11.光遇天气 三&#xff0e;心得四&#xff0e;总结五&#xff0e;参考 一&#xff…

编译 pywinhook v1.6.2 的环境设置和步骤

准备做一个鼠标事件响应程序。 查了一下相关python的第三方类库&#xff0c;发现有 pyhook。 一、起源 pyhook 1、pyhook是最早的版本 pyhook支持的python版本比较低&#xff0c;代码在 https://sourceforge.net/projects/pyhook/ 2、之后产生了两个并行版本 pyHook3 和 p…

来聊聊Spring的循环依赖

文章目录 首先了解一下什么是循环依赖简述解决循环依赖全过程通过debug了解Spring解决循环依赖全过程Aservice的创建递归来到Bservice的创建然后BService递归回到了getAservice的doGetBean中故事再次回到Aservice填充BService的步骤 总结成流程图为什么二级就能解决循环依赖问题…

【Qt开发流程】之UDP

概述 UDP (User Datagram Protocol)是一种简单的传输层协议。与TCP不同&#xff0c;UDP不提供可靠的数据传输和错误检测机制。UDP主要用于那些对实时性要求较高、对数据传输可靠性要求较低的应用&#xff0c;如音频、视频、实时游戏等。 UDP使用无连接的数据报传输模式。在传…

如何实现订单自动取消

由于Redis具有过期监听的功能&#xff0c;于是就有人拿它来实现订单超时自动关闭的功能&#xff0c;但是这个方案并不完美。今天来聊聊11种实现订单超时自动关闭的方案&#xff0c;总有一种适合你&#xff01;这些方案并没有绝对的好坏之分&#xff0c;只是适用场景的不大相同。…

图的搜索(二):贝尔曼-福特算法、狄克斯特拉算法和A*算法

图的搜索&#xff08;二&#xff09;&#xff1a;贝尔曼-福特算法、狄克斯特拉算法和A*算法 贝尔曼-福特算法 贝尔曼-福特&#xff08;Bellman-Ford&#xff09;算法是一种在图中求解最短路径问题的算法。最短路径问题就是在加权图指定了起点和终点的前提下&#xff0c;寻找从…

Vue3使用了Vite和UnoCSS导致前端项目启动报错:Error:EMFILE:too many open files

一个 Vue3 的项目&#xff0c;用的是 Vite 打包&#xff0c;通过 npm run dev 运行时&#xff0c;遇到了以下错误&#xff08;尤其是引入了 Element-Plus 后&#xff09;&#xff1a; Error: EMFILE: too many open files&#xff0c;后面是具体的文件路径。。甚至到了 node_mo…