如何安全地关闭文件指针

C语言之fileno()函数–获取已经打开的文件的文件描述符
fcntl系统调用可以用来对已打开的文件描述符进行各种控制操作以改变已打开文件的的各种属性
结合起来可以实现安全的文件指针关闭
在这里插入图片描述

open函数相关的: /* open 是系统调用 返回的是文件句柄*/

#include <sys/stat.h> 
#include <fcntl.h> 
   
int open(const char *pathname, int flags); 
int open(const char *pathname, int flags, mode_t mode);

fopen函数相关的: /* open是ANSIC标准中的C语言库函数,在不同的系统中应该调用不同的内核api */

FILE *fopen(const char *path, const char *mode); 
   
FILE *fdopen(int fd, const char *mode); 
   
FILE *freopen(const char *path, const char *mode, FILE *stream);

函数说明:fileno()用来取得参数stream 指定的文件流所使用的文件描述词.

void clearerr(FILE *stream); 
   
int feof(FILE *stream); 
   
int ferror(FILE *stream); 
   
int fileno(FILE *stream);

实例:

#include <stdio.h> 
int main(int argc, char **argv) 
{ 
    FILE * fp; 
    int fd; 
    fp = fopen("/etc/passwd", "r"); 
    fd = fileno(fp); 
    //等价于
  //fd=open("etc/passwd",RD_ONLY);
    printf("fd=%d\n", fd); 
    fclose(fp); 
    return 0; 
}

fcntl函数的用法总结
fcntl系统调用可以用来对已打开的文件描述符进行各种控制操作以改变已打开文件的的各种属性
函数原型:

[objc] view plain copy print?

#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);  

fcntl函数功能依据cmd的值的不同而不同。参数对应功能如下:

(1)F_DUPFD

与dup函数功能一样,复制由fd指向的文件描述符,调用成功后返回新的文件描述符,与旧的文件描述符共同指向同一个文件。

(2)F_GETFD

读取文件描述符close-on-exec标志

(3)F_SETFD

将文件描述符close-on-exec标志设置为第三个参数arg的最后一位

(4)F_GETFL

获取文件打开方式的标志,标志值含义与open调用一致

(5)F_SETF

设置文件打开方式为arg指定方式

文件记录锁是fcntl函数的主要功能。

记录锁:实现只锁文件的某个部分,并且可以灵活的选择是阻塞方式还是立刻返回方式

当fcntl用于管理文件记录锁的操作时,第三个参数指向一个struct flock *lock的结构体

[objc] view plain copy print?

struct flock  
{  
    short_l_type;    /*锁的类型*/  
    short_l_whence;  /*偏移量的起始位置:SEEK_SET,SEEK_CUR,SEEK_END*/  
    off_t_l_start;     /*加锁的起始偏移*/  
    off_t_l_len;    /*上锁字节*/  
    pid_t_l_pid;   /*锁的属主进程ID */  
};   

short_l_type用来指定设置共享锁(F_RDLCK,读锁)还是互斥锁(F_WDLCK,写锁).

当short_l_type的值为F_UNLCK时,传入函数中将解锁。

每个进程可以在该字节区域上设置不同的读锁。

但给定的字节上只能设置一把写锁,并且写锁存在就不能再设其他任何锁,且该写锁只能被一个进程单独使用。

这是多个进程的情况。

单个进程时,文件的一个区域上只能有一把锁,若该区域已经存在一个锁,再在该区域设置锁时,新锁会覆盖掉旧的锁,无论是写锁还时读锁。

l_whence,l_start,l_len三个变量来确定给文件上锁的区域。

l_whence确定文件内部的位置指针从哪开始,l_star确定从l_whence开始的位置的偏移量,两个变量一起确定了文件内的位置指针先所指的位置,即开始上锁的位置,然后l_len的字节数就确定了上锁的区域。

特殊的,当l_len的值为0时,则表示锁的区域从起点开始直至最大的可能位置,就是从l_whence和l_start两个变量确定的开始位置开始上锁,将开始以后的所有区域都上锁。

为了锁整个文件,我们会把l_whence,l_start,l_len都设为0。

(6)F_SETLK

此时fcntl函数用来设置或释放锁。当short_l_type为F_RDLCK为读锁,F_WDLCK为写锁,F_UNLCK为解锁。

如果锁被其他进程占用,则返回-1;

这种情况设的锁遇到锁被其他进程占用时,会立刻停止进程。

(7)F_SETLKW

此时也是给文件上锁,不同于F_SETLK的是,该上锁是阻塞方式。当希望设置的锁因为其他锁而被阻止设置时,该命令会等待相冲突的锁被释放。

(8)F_GETLK

第3个参数lock指向一个希望设置的锁的属性结构,如果锁能被设置,该命令并不真的设置锁,而是只修改lock的l_type为F_UNLCK,然后返回该结构体。如果存在一个或多个锁与希望设置的锁相互冲突,则fcntl返回其中的一个锁的flock结构。

====================================================================================================================================================

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

[objc] view plain copy print?
#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);
#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);
fcntl函数功能依据cmd的值的不同而不同。参数对应功能如下:

(1)F_DUPFD

与dup函数功能一样,复制由fd指向的文件描述符,调用成功后返回新的文件描述符,与旧的文件描述符共同指向同一个文件。

(2)F_GETFD

读取文件描述符close-on-exec标志

(3)F_SETFD

将文件描述符close-on-exec标志设置为第三个参数arg的最后一位

(4)F_GETFL

获取文件打开方式的标志,标志值含义与open调用一致

(5)F_SETF

设置文件打开方式为arg指定方式

文件记录锁是fcntl函数的主要功能。

记录锁:实现只锁文件的某个部分,并且可以灵活的选择是阻塞方式还是立刻返回方式

当fcntl用于管理文件记录锁的操作时,第三个参数指向一个struct flock *lock的结构体

[objc] view plain copy print?
struct flock
{
short_l_type; /锁的类型/
short_l_whence; /偏移量的起始位置:SEEK_SET,SEEK_CUR,SEEK_END/
off_t_l_start; /加锁的起始偏移/
off_t_l_len; /上锁字节/
pid_t_l_pid; /*锁的属主进程ID */
};
struct flock
{
short_l_type; /锁的类型/
short_l_whence; /偏移量的起始位置:SEEK_SET,SEEK_CUR,SEEK_END/
off_t_l_start; /加锁的起始偏移/
off_t_l_len; /上锁字节/
pid_t_l_pid; /*锁的属主进程ID */
};

short_l_type用来指定设置共享锁(F_RDLCK,读锁)还是互斥锁(F_WDLCK,写锁).

当short_l_type的值为F_UNLCK时,传入函数中将解锁。

每个进程可以在该字节区域上设置不同的读锁。

但给定的字节上只能设置一把写锁,并且写锁存在就不能再设其他任何锁,且该写锁只能被一个进程单独使用。

这是多个进程的情况。

单个进程时,文件的一个区域上只能有一把锁,若该区域已经存在一个锁,再在该区域设置锁时,新锁会覆盖掉旧的锁,无论是写锁还时读锁。

l_whence,l_start,l_len三个变量来确定给文件上锁的区域。

l_whence确定文件内部的位置指针从哪开始,l_star确定从l_whence开始的位置的偏移量,两个变量一起确定了文件内的位置指针先所指的位置,即开始上锁的位置,然后l_len的字节数就确定了上锁的区域。

特殊的,当l_len的值为0时,则表示锁的区域从起点开始直至最大的可能位置,就是从l_whence和l_start两个变量确定的开始位置开始上锁,将开始以后的所有区域都上锁。

为了锁整个文件,我们会把l_whence,l_start,l_len都设为0。

(6)F_SETLK

此时fcntl函数用来设置或释放锁。当short_l_type为F_RDLCK为读锁,F_WDLCK为写锁,F_UNLCK为解锁。

如果锁被其他进程占用,则返回-1;

这种情况设的锁遇到锁被其他进程占用时,会立刻停止进程。

(7)F_SETLKW

此时也是给文件上锁,不同于F_SETLK的是,该上锁是阻塞方式。当希望设置的锁因为其他锁而被阻止设置时,该命令会等待相冲突的锁被释放。

(8)F_GETLK

第3个参数lock指向一个希望设置的锁的属性结构,如果锁能被设置,该命令并不真的设置锁,而是只修改lock的l_type为F_UNLCK,然后返回该结构体。如果存在一个或多个锁与希望设置的锁相互冲突,则fcntl返回其中的一个锁的flock结构。

====================================================================================================================================================
linux下串口的阻塞和非阻塞操作

[cpp]

    有两个可以进行控制串口阻塞性(同时控制read和write):一个是在打开串口的时候,open函数是否带O_NDELAY;第二个是可以在打开串口之后通过fcntl()函数进行控制。

阻塞的定义:

   对于read,block指当串口输入缓冲区没有数据的时候,read函数将会阻塞在这里,移植到串口输入缓冲区中有数据可读取,read读到了需要的字节数之后,返回值为读到的字节数;

对于write,block指当串口输出缓冲区满,或剩下的空间小于将要写入的字节数,则write将阻塞,一直到串口输出缓冲区中剩下的空间大于等于将要写入的字节数,执行写入操作,返回写入的字节数。

非阻塞的定义:

对于read,no block指当串口输入缓冲区没有数据的时候,read函数立即返回,返回值为0。

对于write,no
block指当串口输出缓冲区满,或剩下的空间小于将要写入的字节数,则write将进行写操作,写入当前串口输出缓冲区剩下空间允许的字节数,然后返回写入的字节数。

static int set_opt(int fd, int nSpeed, int nBits, char nEvent, int nStop)     
{     
    struct termios newtio;     
    struct termios oldtio;     
         
    if(tcgetattr(fd,&oldtio) != 0)     
    {     
        perror("SetupSerial 1");     
        return -1;     
    }     
         
    bzero(&newtio,sizeof(newtio));     
    newtio.c_cflag |= CLOCAL |CREAD;     
    newtio.c_cflag &= ~CSIZE;     
      
/***********数据位选择****************/      
    switch(nBits)     
    {     
        case 7:     
        newtio.c_cflag |= CS7;     
        break;     
        case 8:     
        newtio.c_cflag |= CS8;     
        break;         
    }     
/***********校验位选择****************/    
    switch(nEvent)     
    {     
        case 'O':     
        newtio.c_cflag |= PARENB;     
        newtio.c_cflag |= PARODD;     
        newtio.c_iflag |= (INPCK | ISTRIP);     
            break;     
        case 'E':     
        newtio.c_iflag |= (INPCK |ISTRIP);     
        newtio.c_cflag |= PARENB;     
        newtio.c_cflag &= ~PARODD;     
            break;     
         case 'N':     
        newtio.c_cflag &= ~PARENB;     
            break;     
    }     
/***********波特率选择****************/     
    switch(nSpeed)     
    {     
        case 2400:     
        cfsetispeed(&newtio,B2400);     
        cfsetospeed(&newtio,B2400);     
            break;     
         case 4800:     
        cfsetispeed(&newtio,B4800);     
        cfsetospeed(&newtio,B4800);     
            break;     
         case 9600:     
        cfsetispeed(&newtio,B9600);     
        cfsetospeed(&newtio,B9600);     
            break;   
         case 57600:     
        cfsetispeed(&newtio,B57600);     
        cfsetospeed(&newtio,B57600);     
            break;     
         case 115200:     
        cfsetispeed(&newtio,B115200);     
        cfsetospeed(&newtio,B115200);     
            break;     
         case 460800:     
        cfsetispeed(&newtio,B460800);     
        cfsetospeed(&newtio,B460800);     
            break;               
         default:     
        cfsetispeed(&newtio,B9600);     
        cfsetospeed(&newtio,B9600);     
                break;     
    }     
/***********停止位选择****************/    
    if(nStop == 1){     
        newtio.c_cflag &= ~CSTOPB;     
    }     
    else if(nStop ==2){     
        newtio.c_cflag |= CSTOPB;     
    }     
    newtio.c_cc[VTIME] = 1;     
    newtio.c_cc[VMIN] = FRAME_MAXSIZE;   //阻塞条件下有效  
    
    tcflush(fd,TCIFLUSH);     
    if((tcsetattr(fd,TCSANOW,&newtio)) != 0)     
    {     
        perror("com set error");     
        return -1;     
    }     
    printf("set done!\n");     
    return 0;     
}     

[cpp] view plaincopy?

static int open_port(int fd,int comport)     
{       
/***********打开串口1****************/  
    if(comport == 1)     
    {     
        fd = open("/dev/ttyAT1",O_RDWR|O_NOCTTY|O_NDELAY);     
    if(fd == -1){     
        perror("Can't Open Serial Port");     
        return -1;     
        }     
    }     
 /***********打开串口2****************/   
    else if(comport == 2)     
    {     
        fd = open("/dev/ttyAT2",O_RDWR|O_NOCTTY|O_NDELAY);     
        if(fd == -1){     
            perror("Can't Open Serial Port");     
            return -1;     
        }     
    }     
/***********打开串口3****************/    
    else if(comport == 3)     
    {     
        fd = open("/dev/ttyAT3",O_RDWR|O_NOCTTY|O_NDELAY);     
        if(fd == -1){     
            perror("Can't Open Serial Port");     
            return -1;     
        }     
    }     
    if(comport == 1)  
    {  
        if(fcntl(fd,F_SETFL,FNDELAY) < 0)//非阻塞,覆盖前面open的属性  
        {     
            printf("fcntl failed\n");     
        }     
        else{     
        printf("fcntl=%d\n",fcntl(fd,F_SETFL,FNDELAY));     
        }   
    }  
    else  
    {  
        if(fcntl(fd,F_SETFL,0) < 0){   //阻塞,即使前面在open串口设备时设置的是非阻塞的,这里设为阻塞后,以此为准  
        printf("fcntl failed\n");     
        }     
        else{     
        printf("fcntl=%d\n",fcntl(fd,F_SETFL,0));     
        }   
    }     
    if(isatty(STDIN_FILENO) == 0){   
        
    printf("standard input is not a terminal device\n");     
    }     
    else{     
          
        printf("isatty sucess!\n");     
    }    
  
    printf("fd-open=%d\n",fd);     
    return fd;     
}  

所以,linux的串口的阻塞性通过fcntl()函数进行设置即可。

[cpp]
阻塞:fcntl(fd,F_SETFL,0) [cpp]
非阻塞:fcntl(fd,F_SETFL,FNDELAY

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值