zoukankan      html  css  js  c++  java
  • 第3章 文件I/O(4)_dup、dup2、fcntl和ioctl函数

    5. 其它I/O系统调用

    (1)dup和dup2函数

    头文件

    #include<unistd.h> 

    函数

    int dup(int oldfd);

    int dup2(int oldfd, int newfd);

    返回值

    若成功返回新文件描述符,出错返回-1

    功能

    文件描述符的复制(将oldfd复制给newfd

    参数

    old:原先的文件描述符

    newfd: 新文件描述符

    备注

    (1)由dup返回的新文件描述符一定是当前可用文件描述符中最小数值

    (2)用dup2则可以用newfd参数指定新描述符的数值。如果newfd己经打开,则先将其关闭。如果old等于newfd,则dup2返回newfd,而不关闭它。

    (3)在进程间通信时可用来改变进程的标准输入和标准输出设备。(4)注意,复制的只是3个内核结构中的文件描述符中的文件表项指针也就是newfd与oldfd指向了同一个文件表项。但要注意并不复制文件表项本身和inode节点项这两个内核数据结构。

    【编程实验】1. 自定义的cat命令

    //io.h

    #ifndef __IO_H__
    #define __IO_H__
    
    extern void copy(int fdin, int fdout);//文件复制
    extern void set_fl(int fd, int flag); //设置文件状态标志
    extern void clr_fl(int fd, int flag); //取消文件状态标志
    
    
    #endif

    //io.c

    #include "io.h"
    #include <unistd.h>
    #include <errno.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <fcntl.h>
    
    //编译命令:gcc -o obj/io.o -Iinclude -c src/io.c
    
    #define BUFFER_LEN 1024 //与分区文件块大小一致。可以通过
                            //tune2fs -l /dev/sda1命令查看
                            
    
    void copy(int fdin, int fdout)
    {
        char buffer[BUFFER_LEN];
        
        ssize_t size;
    
        //保证从文件开始处复制
        lseek(fdin, 0L, SEEK_SET); 
        lseek(fdout, 0L, SEEK_SET); 
    
        while((size = read(fdin, buffer, BUFFER_LEN)) > 0){
            if(write(fdout, buffer, size) != size)
            {
               fprintf(stderr, "write error: %s 
    ", strerror(errno));
               exit(1);
            }
        }
    
        if (size < 0 )
        {
            fprintf(stderr, "read error: %s
    ",strerror(errno));
            exit(1);  //return 1;
        }   
    }
    
    void set_fl(int fd, int flag) //设置文件状态标志
    {
        //获取原来的文件状态标志
        int val = fcntl(fd, F_GETFL);
    
        //增加新的文件状态标志
        val |= flag;
    
        //重新设置文件状态标志
        if(fcntl(fd, F_SETFL, val) < 0)
        {
            perror("fcntl error");
        }
    }
    
    void clr_fl(int fd, int flag) //取消文件状态标志
    {
    
        //获取原来的文件状态标志
        int val = fcntl(fd, F_GETFL, val);
    
        //清除指定的文件状态标志(置0)
        val &= ~flag;
    
        //重新设置文件状态标志
        if(fcntl(fd, F_SETFL, val) < 0 )
        {
            perror("fcntl error");
        }
    }

    //cat.c

    #include "io.h"
    #include <unistd.h>
    #include <fcntl.h>
    #include <stdio.h>
    
    //模拟cat命令,设编译后的程序名也叫cat
    //实验1:bin/cat file1.txt file2.txt
    //实验2:bin/cat < file1.txt
    //实验3:bin/cat > file1.txt
    
    int main(int argc, char* argv[])
    {
        int fd_in = STDIN_FILENO;    //0
        int fd_out = STDOUT_FILENO;  //1
    
        int i = 0;
        for(i=1; i<argc; i++){
            //1.输入cat file1.txt file2.txt时会将file1和file2
            //  分别拷贝到标准输出,即输出到屏幕
            fd_in = open(argv[1], O_RDONLY);
            if(fd_in < 0)
            {
                perror("open error");
                continue;
            }
    
            copy(fd_in, fd_out);
            close(fd_in);
        }
        //2.如果cat命令后面不带参数,则直接接受键盘输入。
        //3.如果输入cat < file1.txt,由于<表示输入重定向,Linux
        //会将file1.txt这个参数作为输入,复制给STDIN_FILENO。而不会将其
        //传给argv参数。即这种情况下,argc==1,表示程序本身,argv中不包含file1.txt。
        if(argc == 1) copy(fd_in, fd_out);
        
        return 0;
    }

    【编程实验】2.dup和dup2实现重定向

    //io.h和io.c与上例相同

    #include "io.h"
    #include <unistd.h>
    #include <fcntl.h>
    #include <string.h>
    #include <stdlib.h>
    /*
     * bin/mcat + file1.txt (+为输入重定向)
     * bin/mcat - file1.txt (-为输出重定向)
     */
    
    int main(int argc, char* argv[])
    {
        int fd_in;
        int fd_out;
    
        int i=0;
    
        for(i=1; i<argc; i++){
            if(!strcmp("+", argv[i])){
                fd_in = open(argv[++i], O_RDONLY);
                if(fd_in < 0){
                    perror("open error");
                    exit(1);
                }
    
                //将标准输入重定向到文件(即,将fd_in的文件表项指针复制给STDIN_FILENO
                //因此,STDIN_FILENO文件描述符与fd_in就同时指向同一个文件表项,也就
                //同时指向了file1.txt这个文件。
                if(dup2(fd_in, STDIN_FILENO) != STDIN_FILENO){
                    perror("dup2 error");
                    exit(1);
                }
    
                close(fd_in);
            }else if(!strcmp("-", argv[i])){
    
                fd_out = open(argv[++i], O_WRONLY | O_CREAT | O_TRUNC, 0777);
                if(fd_out < 0){
                    perror("open error");
                    exit(1);
                }
    
                //将标准输出重定向到文件(即,将fd_out的文件表项指针复制给STDOUT_FILENO
                //因此,STDOUT_FILENO文件描述符与fd_out就同时指向同一个文件表项,也就
                //同时指向了file1.txt这个文件。
                if(dup2(fd_out, STDOUT_FILENO) != STDOUT_FILENO){
                    perror("dup2 error");
                    exit(1);
                }
    
                close(fd_out);
            }else{
                  fd_in = open(argv[i], O_RDONLY);
                  if(fd_in < 0){
                      perror("open error");
                      exit(1);
                  }
    
                  if(dup2(fd_in, STDIN_FILENO) != STDIN_FILENO)
                  {
                      perror("dup2 error");
                      exit(1);
                  }
    
                  close(fd_in);
            }
    
            copy(STDIN_FILENO, STDOUT_FILENO);
        }
    
       //命令后面不带参数的情况
       if(argc ==1)
          copy(STDIN_FILENO, STDOUT_FILENO);
          
        return 0;
    }

    (2)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);

    返回值

    若成功则依赖于cmd,出错为-1。

    功能

    可以改变己经打开文件的性质常见的功能:

    (1)复制一个现存的描述符,新文件描述符作为函数返回值(cmd=F_DUP)

    (2)获取/设置文件描述符标志(cmd=F_GETFD或F_SETFD)

    (3)获取/设置文件状态标志(cmd=F_GETFL或F_SETFL)

    (4)获取/设置文件锁(cmd=F_SETLK、F_GETLK或F_SETKLW),此时第3个参数为struct flock结构体。

    参数

    cmd的常见取值:

    (1)F_DUPFD:复制文件描述符,新的文件描述符作为函数返回值返回。

    (2)F_GETFD/F_SETFD:获取/设置文件描述符,通过第3个参数设置。

    (3)F_GETFL/F_SETFL:获取/设置文件状态标志,通过第3个参数设置。可以更改的标志有:O_APPEND、O_NONBLOCK、O_SYNC、O_ASYNC但要注意,O_RDONLY、O_WRONLY和O_RDWR不适用。

    【编程实验】设置文件状态标志

    //io.h和io.c文件与上例相同

    //file_append_flag.c

    #include "io.h"
    #include <unistd.h>
    #include <stdio.h>
    #include <stdlib.h> //exit
    #include <string.h> //strlen
    #include <fcntl.h>  //O_WRONLY
    
    int main(int argc, char* argv[]){
        if( argc < 3){
            printf("usage: %s content destfile
    ", argv[0]);
            exit(1);
        }
    
        int fd = open(argv[2], O_WRONLY); //注意,这里没设置为追加
        
        if(fd < 0){
            perror("open error");
            exit(1);
        }
    
        //设置追加文件标志
        set_fl(fd, O_APPEND);
        
        sleep(10); //为了把定位与写入过程隔开,以便演示多进程同时写入同一文件
                   //时会出现后启动进程格覆盖之前进程写过的内容。
                   
        //往文件尾部追加内容
        size_t size = strlen(argv[1])*sizeof(char);
        if(write(fd, argv[1], size)!=size){
            perror("write error");
            exit(1);
        }
    
        close(fd);
    
        return 0;
    }

    (3)ioctl函数

    头文件

    #include<unistd.h>

    #include <sys/ioctl.h>

    函数

    int ioctl(int fd, int request,…);

    返回值

    成功为0,出错为-1

    功能

    控制I/O设备 ,提供了一种获得设备信息和向设备发送控制参数的手段

    备注

    I/O操作的杂物箱。不能用本章中其他函数表示的I/O操作通常都能用ioctl表示。终端I/O是ioctl的最大使用方面,主要用于设置的I/O控制。

    【编程实验】读取键盘内容

    #include <unistd.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <fcntl.h>
    #include <sys/ioctl.h>  //for ioctl  
    #include <linux/input.h> //keyboard
    
    //说明本程序须在虚拟机,而不能在远程终端上运行!
    
    int main(int argc, char * argv[])
    {
        int fd = -1;
        char name[256]="unknow";
        struct input_event event;
    
        int ret = 0;
    
        if(( fd = open("/dev/input/event2", O_RDONLY)) < 0){
            perror("open error");
            exit(1);
        }
    
        //通用EVIOCGNAME命令获取设备名字
        if(ioctl(fd, EVIOCGNAME(sizeof(name)), name) < 0){
            perror("evdev ioctl error
    ");
            exit(1);
        }
    
        printf("The device's name is %s
    ", name);
        
        while(1)
        {   
            ret = read(fd, &event, sizeof(event));
            if(ret < 0){
                printf("read event error
    ");
            }
    
            if(EV_KEY == event.type) //EV_KEY表示按键类事件,类似的有EV_PWR(电源)、EV_SND(声音)事件
            {
                //if the event is a key code
                printf(" key code is %d 
    ", event.code);
    
                //quit,press 'q' to quit this application.
                if(event.code == 16){
                    break;
                }
            }
        }
    
        return 0;
    }
  • 相关阅读:
    注册表开启或关闭ie浏览器-局域网设置-自动监测设置
    关于win32接口文档位置
    网站添加天气预报插件
    json
    re正则
    配置文件
    logging日志模块
    hashlib加密
    sys模块
    os模块
  • 原文地址:https://www.cnblogs.com/5iedu/p/6341292.html
Copyright © 2011-2022 走看看