fcntl和select函数彻底搞明白.docx

上传人:小飞机 文档编号:3157003 上传时间:2023-03-11 格式:DOCX 页数:9 大小:40.90KB
返回 下载 相关 举报
fcntl和select函数彻底搞明白.docx_第1页
第1页 / 共9页
fcntl和select函数彻底搞明白.docx_第2页
第2页 / 共9页
fcntl和select函数彻底搞明白.docx_第3页
第3页 / 共9页
fcntl和select函数彻底搞明白.docx_第4页
第4页 / 共9页
fcntl和select函数彻底搞明白.docx_第5页
第5页 / 共9页
亲,该文档总共9页,到这儿已超出免费预览范围,如果喜欢就下载吧!
资源描述

《fcntl和select函数彻底搞明白.docx》由会员分享,可在线阅读,更多相关《fcntl和select函数彻底搞明白.docx(9页珍藏版)》请在三一办公上搜索。

1、fcntl和select函数彻底搞明白fcntl和select函数彻底搞明白 第一、fcntl函数详细使用 fcntl有强大的功能,它能够复制一个现有的描述符,获得/设置文件描述符标记,获得/设置文件状态标记,获得/设置异步I/O所有权,获得/设置纪录锁。 当多个用户共同使用,操作一个文件的情况,Linux通常采用的方法就是给文件上锁,来避免共享资源产生竞争的状态。 fcntl文件锁有两种类型:建议性锁和强制性锁 建议性锁是这样规定的:每个使用上锁文件的进程都要检查是否有锁存在,当然还得尊重已有的锁。内核和系统总体上都坚持不使用建议性锁,它们依靠程序员遵守这个规定。 强制性锁是由内核执行的。当

2、文件被上锁来进行写入操作时,在锁定该文件的进程释放该锁之前,内核会阻止任何对该文件的读或写访问,每次读或写访问都得检查锁是否存在。 使用fcntl文件锁进行I/O操作必须小心:进程在开始任何I/O操作前如何去处理锁,在对文件解锁前如何完成所有的操作,是必须考虑的。如果在设置锁之前打开文件,或者读取该锁之后关闭文件,另一个进程就可能在上锁/解锁操作和打开/关闭操作之间的几分之一秒内访问该文件。当一个进程对文件加锁后,无论它是否释放所加的锁,只要文件关闭,内核都会自动释放加在文件上的建议性锁, 所以不要想设置建议性锁来达到永久不让别的进程访问文件的目的(强制性锁才可以)_;强制性锁则对所有进程起作

3、用。 可以用fcntl 函数改变一个已打开的文件的属性,可以重新设置读、写、追加、非阻塞等标志(这些标志称为File StatusFlag),而不必重新open 文件。 #include #include int fcntl(int fd, int cmd); int fcntl(int fd, int cmd, long arg); int fcntl(int fd, int cmd, struct flock *lock); 这个函数和open 一样,也是用可变参数实现的,可变参数的类型和个数取决于前面的cmd 参数。 文件锁包括了 建议性锁 和 强制性锁。 建议性锁要求每个上锁的文件的进

4、程都要检查是否有锁存在,并且尊重已有的锁,在一般情况下,内核和系统都不使用建议性锁。 强制性锁是由内核执行的锁,当一个文件被上锁进行读写操作的时候,内核将阻止其他任何文件对其进行读写操作。每次读写操作都要检查是否有锁存在。 在Linux中实现上锁的函数有lock和fcntl。 lock用于对文件施加建议性锁 fcntl用于对文件施加建议性锁和强制性锁都行。同时还可以对文件某一条纪录进行上锁,也就是记录锁。 记录锁分为 读取锁 和 写入锁。 fcntl函数原型 #include #include #include int fcntl(int fd, /文件描述符 int cmd , /不同的命令

5、 struct flock *lock) /设置记录锁的具体状态 cmd取值: F_DUPFD 复制文件描述符 F_GETFD 获得fd的close-on-exec标志 F_SETFD 设置close-on-exec标志 F_GETFL 获得open设置标志 F_SETFL 设置lock描述的标志 F_GETLK 测试该锁是否被另外一把锁排斥 F_SETLKW 如果存在其他锁,则调用进程睡眠,如果捕捉到信号则睡眠中断 F_GETOWN 检索收到的SIGIO和SIGURG信号的进程号或者进程组号 F_SETOWN 设置进程号或进程组号 这里的lock结构体如下: struct flock sho

6、rt l_type; /*F_RDLCK(读取锁),F_WRLCK,F_UNLCK*/ off_t l_start; /*相对偏移量*/ short l_whence; /*SEEK_SET ,SEEK_CUR ,SEEK_END */ off_t l_len; /*加锁区域长度*/ pid_t l_pid; 成功:0 出错:-1 提示:如果加锁整个文件通常的方法是将l_start设置为0,l_whence设置为SEEK_SET, l_len设置为0。 下面的例子使用F_GETFL和F_SETFL这两种fcntl 命令改变STDIN_FILENO的属性上O_NONBLOCK 选项,实现非阻塞读

7、终端的功能。 用fcntl改变File Status Flag #include #include #include #include #include #define MSG_TRY try againn int main(void) char buf10; int n; int flags; flags = fcntl(STDIN_FILENO, F_GETFL); flags |= O_NONBLOCK; if (fcntl(STDIN_FILENO, F_SETFL, flags) = -1) perror(fcntl); exit(1); tryagain: n = read(STD

8、IN_FILENO, buf, 10); if (n 0) if (errno = EAGAIN) sleep(1); write(STDOUT_FILENO, MSG_TRY,strlen(MSG_TRY); goto tryagain; perror(read stdin); exit(1); write(STDOUT_FILENO, buf, n); return 0; 第二、select函数详细使用 select系统调用是用来让我们的程序监视多个文件句柄(file descriptor)的状态变化的。程序会停在select这里等待,直到被监视的文件句柄有某一个或多个发生了状态改变。 文

9、件在句柄在Linux里很多,如果你man某个函数,在函数返回值部分说到成功后有一个文件句柄被创建的都是的,如man socket可以看到“On success, a file descriptor for the new socket is returned.”而man 2 open可以看到“open and creat return the new file descriptor”,其实文件句柄就是一个整数,看socket函数的声明就明白了: int socket(int domain, int type, int protocol); 当然,我们最熟悉的句柄是0、1、2三个,0是标准输入,

10、1是标准输出,2是标准错误输出。0、1、2是整数表示的,对应的FILE *结构的表示就是stdin、stdout、stderr,0就是stdin,1就是stdout,2就是stderr。 比如下面这两段代码都是从标准输入读入9个字节字符: #include #include #include int main(int argc, char * argv) char buf10 = ; read(0, buf, 9); /* 从标准输入 0 读入字符 */ fprintf(stdout, %sn, buf); /* 向标准输出 stdout 写字符 */ return 0; /* *上面和下面的

11、代码都可以用来从标准输入读用户输入的9个字符* */ #include #include #include int main(int argc, char * argv) char buf10 = ; fread(buf, 9, 1, stdin); /* 从标准输入 stdin 读入字符 */ write(1, buf, strlen(buf); return 0; 继续上面说的select,就是用来监视某个或某些句柄的状态变化的。select函数原型如下: int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exc

12、eptfds, struct timeval *timeout); 函数的最后一个参数timeout显然是一个超时时间值,其类型是struct timeval *,即一个struct timeval结构的变量的指针,所以我们在程序里要申明一个struct timeval tv;然后把变量tv的地址&tv传递给select函数。struct timeval结构如下: struct timeval long tv_sec; /* seconds */ long tv_usec; /* microseconds */ ; 第2、3、4三个参数是一样的类型: fd_set *,即我们在程序里要申明几个

13、fd_set类型的变量,比如rdfds, wtfds, exfds,然后把这个变量的地址&rdfds, &wtfds, &exfds 传递给select函数。这三个参数都是一个句柄的集合,第一个rdfds是用来保存这样的句柄的:当句柄的状态变成可读的时系统就会告诉select函数返回,同理第二个wtfds是指有句柄状态变成可写的时系统就会告诉select函数返回,同理第三个参数exfds是特殊情况,即句柄上有特殊情况发生时系统会告诉select函数返回。特殊情况比如对方通过一个socket句柄发来了紧急数据。如果我们程序里只想检测某个socket是否有数据可读,我们可以这样: fd_set r

14、dfds; /* 先申明一个 fd_set 集合来保存我们要检测的 socket句柄 */ struct timeval tv; /* 申明一个时间变量来保存时间 */ int ret; /* 保存返回值 */ FD_ZERO(&rdfds); /* 用select函数之前先把集合清零 */ FD_SET(socket, &rdfds); /* 把要检测的句柄socket加入到集合里 */ tv.tv_sec = 1; tv.tv_usec = 500; /* 设置select等待的最大时间为1秒加500毫秒 */ ret = select(socket + 1, &rdfds, NULL,

15、NULL, &tv); /* 检测我们上面设置到集合rdfds里的句柄是否有可读信息 */ if(ret maxfd) maxfd = sa; if(sb maxfd) maxfd = sb; if(sc maxfd) maxfd = sc; 然后调用select函数: ret = select(maxfd + 1, &rdfds, NULL, NULL, &tv); /* 注意是最大值还要加1 */ 同样的道理,如果我们要检测用户是否按了键盘进行输入,我们就应该把标准输入0这个句柄放到select里来检测,如下: FD_ZERO(&rdfds); FD_SET(0, &rdfds); tv.tv_sec = 1; tv.tv_usec = 0; ret = select(1, &rdfds, NULL, NULL, &tv); /* 注意是最大值还要加1 */ if(ret 0) perror(select);/* 出错 */ else if(ret = 0) printf(超时n); /* 在我们设定的时间tv内,用户没有按键盘 */ else /* 用户有按键盘,要读取用户的输入 */ scanf(%s, buf);

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 生活休闲 > 在线阅读


备案号:宁ICP备20000045号-2

经营许可证:宁B2-20210002

宁公网安备 64010402000987号