首页 > 代码库 > 第3章 文件I/O(4)_dup、dup2、fcntl和ioctl函数
第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 \n", strerror(errno)); exit(1); } } if (size < 0 ) { fprintf(stderr, "read error: %s\n",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_GETL或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\n", 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\n"); exit(1); } printf("The device‘s name is %s\n", name); while(1) { ret = read(fd, &event, sizeof(event)); if(ret < 0){ printf("read event error\n"); } if(EV_KEY == event.type) //EV_KEY表示按键类事件,类似的有EV_PWR(电源)、EV_SND(声音)事件 { //if the event is a key code printf(" key code is %d \n", event.code); //quit,press ‘q‘ to quit this application. if(event.code == 16){ break; } } } return 0; }
第3章 文件I/O(4)_dup、dup2、fcntl和ioctl函数