《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{//fail
        perror("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){//success
        if(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 lock
    memset(&lock,0,sizeof(struct flock));
    lock.l_start = 0;
    lock.l_whence = SEEK_SET;
    lock.l_len=0;
    //set read lock
    lock.l_type=F_RDLCK;
    if(lock_test(fd,&lock)==0){//test succeeded
        lock.l_start=0;
        lock.l_whence=SEEK_SET;
        lock.l_len=0;
        lock.l_type=F_RDLCK;
        lock_set(fd,&lock);
    }
    //read data
    lseek(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 lock
    lock.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);
    }
    //release
    lock.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,..)

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Spring Cloud Alibaba

文章目录 Spring Cloud Alibaba1 介绍2 Nacos&#xff08;注册中心和配置中心&#xff09;2.1 示例 3 Sentinel&#xff08;流量控制和熔断降级&#xff09;3.1 流量控制示例 4 GateWay4.1 断言4.2 过滤器4.2.1 局部过滤器4.2.2 全局过滤器 4.3 网关限流 5 链路追踪Sleuth Spri…

QT案例 使用WMI获取win_32类的属性值,包括Win32提供程序类中的属性

最近涉及到读取WINDOWS 系统电脑设备的各种信息&#xff0c;在一些特殊的PE或者简化系统中是没有WMI查询工具的&#xff0c;所以就自己写了个查询大部分WMI属性值的工具&#xff0c;免去了查网站的功夫。涉及到的方法内容就汇总做个总结。 PS:因为工作中软件基本都是我一个人开…

改进lora-scripts,支持SDXL训练,以及启动脚本

分享下自己改进的一个lora训练脚本&#xff0c;在ubuntu下如果SD-WEBUI的环境已经搭好的话&#xff0c;只需要下载lora-script就可以支持训练了&#xff0c;直接命令行方式训练。 首先&#xff0c;我们需要克隆下项目&#xff1a; git clone https://github.com/Akegarasu/lo…

docker安装Prometheus

docker安装Prometheus Docker搭建Prometheus监控系统 环境准备(这里的环境和版本是经过测试没有问题,并不是必须这个版本) 主机名IP配置系统说明localhost随意2核4gCentOS7或者Ubuntu20.0.4docker版本23.0.1或者24.0.5,docker-compose版本1.29 安装Docker Ubuntu20.0.4版本…

NO-IOT翻频,什么是翻频,电信为什么翻频

1.1 翻频迁移最终的目的就是减少网络的相互干扰&#xff0c;提供使用质量. 1.2 随着与日俱增的网络规模的扩大&#xff0c;网内干扰已成了影响网络的质量标准之一&#xff0c;为了保障电信上网体验&#xff0c;满足用户日益增长的网速需求,更好的服务客户&#xff0c;电信针对…

JAVAEE大型金融支付-第1章-讲义-项目介绍

第1章 讲义-项目介绍与环境搭建 1.项目背景 1.1 项目背景 随着移动支付的盛行&#xff0c;商业银行、第三方支付公司、其它清算机构、消费金融公司等众多类型的机构&#xff0c;都在为商户 提供网络&#xff08;移动&#xff09;支付解决方案。另一方面&#xff0c;用户的支…

视频推拉流平台EasyDSS点播文件播放请求添加token验证的实现方法

EasyDSS视频直播点播平台可提供一站式的视频推拉流、转码、点播、直播、播放H.265编码视频等服务&#xff0c;搭配RTMP高清摄像头使用&#xff0c;可将设备的实时流推送到平台上&#xff0c;实现无人机视频推流直播等应用。今天我们来介绍下EasyDSS系统点播文件播放请求添加tok…

谷歌的开源供应链安全

本内容是对Go项目负责人Russ Cox 在 ACM SCORED 活动上演讲内容[1]的摘录与整理。 SCORED 是Software Supply Chain Offensive Research and Ecosystem Defenses的简称, SCORED 23[2]于2023年11月30日在丹麦哥本哈根及远程参会形式举行。 摘要 &#x1f4a1; 谷歌在开源软件供应…

Tor网络原理详解

引入 匿名通信是一种通过采用数据转发、内容加密、流量混淆等措施来隐藏通信内容及关系的隐私保护技术。为了提高通信的匿名性&#xff0c;这些数据转发链路通常由多跳加密代理服务节点构成&#xff0c;而所有这些节点即构成了匿名通信系统&#xff08;或称匿名通信网络&#…

Flask学习四:补充

插件 flask-caching 简介 Flask-Caching 是一个 Flask 扩展&#xff0c;旨在为 Flask 应用程序添加缓存功能。缓存是一种提高应用性能的技术&#xff0c;通过将常用数据暂时存储在一个快速访问的位置&#xff08;如内存或磁盘&#xff09;&#xff0c;从而减少对较慢资源&…

【Nginx】Nginx了解(基础)

文章目录 Nginx产生的原因Nginx简介Nginx的作用反向代理负载均衡策略动静分离 Nginx的Windows下的安装Linux下的安装Nginx常用命令 负载均衡功能演示 Nginx产生的原因 背景 一个公司的项目刚刚上线的时候&#xff0c;并发量小&#xff0c;用户使用的少&#xff0c;所以在低并发…

MIT6.5840-2023-Lab2C: Raft-Persistence

前置知识 见上一篇 Lab2A。 实验内容 实现 RAFT&#xff0c;分为四个 part&#xff1a;leader election、log、persistence、log compaction。 实验环境 OS&#xff1a;WSL-Ubuntu-18.04 golang&#xff1a;go1.17.6 linux/amd64 Part 2C: persistence 大部分的bug都与这…

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

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

SpringBoot 自动装配原理---源码详解

目录 SpringBoot 自动装配原理源码流程详解&#xff1a;流程总结&#xff1a;条件匹配解释&#xff1a;其他解释&#xff1a; SpringBoot 自动装配原理 源码流程详解&#xff1a; 1、先看启动类&#xff0c;启动这个main方法&#xff0c;然后调用这个run方法。 2、把 启动类作…

牛客网 DP35 【模板】二维前缀和

代码&#xff1a; import java.util.Scanner;// 注意类名必须为 Main, 不要有任何 package xxx 信息 public class Main {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextInt()) { //…

netty-daxin-3(rpc远程调用)

文章目录 nettyRpcObjectEncoder 与 ObjectDecoderjdk动态代理回顾Rpc调用过程简析服务端客户端 nettyRpc ObjectEncoder 与 ObjectDecoder ObjectEncoder继承自MessageToByteEncoder<Serializable>&#xff0c;它内部使用ByteBufOutputStream包装ByteBuf对象&#xff…

Python 爬虫之简单的爬虫(一)

爬取网页上所有链接 文章目录 爬取网页上所有链接前言一、基本内容二、代码编写1.引入库2.测试网页3.请求网页4.解析网页并保存 三、如何定义请求头&#xff1f;总结 前言 最近也学了点爬虫的东西。今天就先给大家写一个简单的爬虫吧。循序渐进&#xff0c;慢慢来哈哈哈哈哈哈…

TrustGeo代码理解(一)main.py

代码链接:https://github.com/ICDM-UESTC/TrustGeo 一、导入各种模块和数据库 # -*- coding: utf-8 -*- import torch.nnfrom lib.utils import * import argparse, os import numpy as np import random from lib.model import * import copy from thop import profile imp…

devc++如何建立一个c++项目?devc++提示源文件未编译?

打开devc APP后是这样的界面&#xff1b; 点击文件-> 新建->项目&#xff0c;这一点应该不难&#xff0c;主要是最后这个选择什么&#xff1f; 这样即可。 devc提示源文件未编译&#xff1f; 点击工具->编译选项&#xff1b; 如果不能解决&#xff0c;那就是可能路径…

NNDL 循环神经网络-梯度爆炸实验 [HBU]

目录 6.2.1 梯度打印函数 6.2.2 复现梯度爆炸现象 6.2.3 使用梯度截断解决梯度爆炸问题 【思考题】梯度截断解决梯度爆炸问题的原理是什么&#xff1f; 总结 前言&#xff1a; 造成简单循环网络较难建模长程依赖问题的原因有两个&#xff1a;梯度爆炸和梯度消失。 循环…