高分求教,如何一边写文件一边读文件呢?(fopen)

DJay 2006-11-06 11:18:22
我的程序中有一个线程用fopen_s打开一个文件,对其进行写操作,其它线程欲对其进行读操作,但当一个线程打开文件后,其它线程就无法打开此文件,返回32号错误(ERROR_SHARING_VIOLATION
32 The process cannot access the file because it is being used by another process. )猜想应该是文件存取共享许可的问题,请问高手如何解决,有无什么api可以设置文件的共享形式的呢?
...全文
1605 9 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
9 条回复
切换为时间正序
请发表友善的回复…
发表回复
xwsn 2006-11-08
  • 打赏
  • 举报
回复
操作系统中不是有 加锁的例子吗?
YoungMaster 2006-11-08
  • 打赏
  • 举报
回复
CreateFile("文件名",
GENERIC_READ,//打开方式
FILE_SHARE_WRITE,//共享写,允许其他文件并发写此文件
NULL,
OPEN_EXISTING,//打开方式
FILE_ATTRIBUTE_NORMAL,//文件属性
NULL
);
为了并发访问时数据的正确,最好使用关键代码段或其他同步机制
left_zxp 2006-11-07
  • 打赏
  • 举报
回复
mark
飞哥 2006-11-07
  • 打赏
  • 举报
回复

LOCK_EX操作用于在文件上创建互斥锁,当互斥锁生效时,文件上不能存在其他的共享锁,包括已共享的锁。

#define LOCK_NB 0x04 /* don't block when locking */


使用这个,flock的调用将阻塞,直到锁定生效,不论如何,假如期望的操作LOCK_NB是ORed,flock的调用将向EWOULDBLOCK返回错误号成功(0)或者失败(1).

#define LOCK_UN 0x08 /* unlock file */


LOCK_UN用于移除文件上的锁

通过调用期望的flock,flock锁的优先级能够被提升或者降低。新的成功的调用将使最近生效的锁替换先前的锁。

DUP函数

int dup(int old);


像fcntl调用能够用作为现有的文件描述符复制描述符一样,dup函数也能复制文件描述符,dup调用能够返回一个跟old参数无法区别的新的文件描述符,这就意味着所有的read(),write()跟lseek()调用都会操作两个描述符(复制出来的新的跟原来的描述符),同时,所有fcntl的选项将保留,close on exec位出外, close on exec位会被关闭,所以你可以复制一个文件描述符,然后允许子进程去调用一个exec函数,这是dup函数非常普遍的一个用法。old参数用作复制,并且必须是一个有效的参数,描述目标描述符,新文件符至少应该是没使用过的文件描述符,它由成功调用dup函数后返回。它意味着如果你关闭STDIN_FILENO(该值为0)那么dup直接调用的新文件描述符的值将是STDIN_FILENO,假如dup函数调用因一些原因失败,将返回-1并且相应地设置errno.

DUP2函数

int dup2(int old, int new);


除了new参数是希望的目标值,dup2函数跟dup函数很相似。假如新参数已经参照了一个有效打开的描述符,并且它的值跟老参数的值不一样,那么新文件描述符将先被关闭。假如新参数等于老参数,那么函数不会做任何操作。成功调用DUP2的返回值将等于新参数。假如DUP2调用失败,将返回-1并且相应地设置errno.

5.5 进程间通信

来自于System V的主要的功能是基本的进程间通信,或者说IPC,这些一直在BSD里面被非常广泛地使用。IPC机制允许程序之间相互共享数据。这个很像我们已经介绍过的重定向,但是重定向是单向处理而不是双向处理。在示范程序中,重定向可以跟设置过STDIN_FILENO参数的CAT命令共享数据,但是有一个问题:cat命令不能跟重定向的程序共享数据,假设一个算法是从其他文件描述符读但是用open不灵活的时候,我们可以修改他们使其可以双向地相互共享数据。BSD提供了很多更好的用于进程间通信的方法。

PIPE函数

int pipe(int *array);


通过给定一个有效的二维数组调用pipe函数(如:int array[2]),pipe函数将分配两个文件描述符,假如成功,数组将包含两个有区别的并且允许单向通信的文件描述符, 打开的第一个文件描述符(array[0])用于读,打开的另一(array[1])个用于写,所以,自从成功调用pipe函数后,你能在这两个描述符之间得到一个单向通信通道,当写入其中一个时,你也将能够从另一个读取输出,管道函数比重定向好的地方是你不必使用文件,

这些文件描述符的行为与普通的文件描述符完全一样,然而它们没有任何文件与之关联,管道功能使unix shell加到其他命令的管道变得很有用,例如

bash$ find / -user frankie | grep -i jpg | more



这个例子里面,find命令将其输出通过管道传送给grep命令,grep再将其输出通过管道传送给more命令。当建立这个顺序的时候, shell将处理管道实际的设置,这些程序本身没有写到其他程序的想法,因为他们真的不需要知道。从这个例子你能看见自从调用了管道,正常来说进程就会 fork, 之后进程就能通信,假如要达到双向通信的目标,你需要创建两个管道,一个用于父进程到子进程的通信,另一个用作子进程到父进程的通信。

管道有下面两条通信规则:
1.如果管道读的这边关闭了,试图写到这个管道将会导致一个SIGPIPE信号发送给试图写的进程。
2.如果管道写的这边关闭了,试图从这个管道读将导致读返回0或文件结束。关闭写端是发送文件结束到该管道读端的唯一方法。

成功地调用pipe函数后将返回0,假如调用失败,将返回-1并且errno将被相应地设置,

备注:在更多的现代的BSD上,单个描述符上pipe函数支持双向通信,不论如何,这个特性不是很轻便,也因为如此,这个方式不建议使用。


Mkfifo函数



int mkfifo(const char *path, mode_t mode);



在相关的进程间通信时,管道是有用的,然而在没有关联关系的进程间通信时,使用mkfifo函数。Mkfifo函数实际上在文件系统创建一个文件。这个文件只是其他文件在通信时使用的一个标志。这些文件就叫做FIFO管道(先进先出管道)。当一个进程创建一个FIFO管道,写到这个FIFO管道并不会写到这个文件,而是被另一个进程读取。这种行为与管道非常相似,所以FIFO也被叫做命名管道。Mkfifo函数有两个参数。第一个参数是一个以null结尾的字符串,声明路径和文件名。第二个参数是该文件的存取模式。该存取模式是标准的unix文件所有者读写权限(参考/usr/include/sys/stat.h中的S_IRUSR, S_IRGRP等)。

一旦mkfifo函数成功调用,需要用open函数打开创建的fifo管道进行读写。如果调用失败,会返回-1而且错误代码会被相应地设置。

创建fifo管道与创建文件相类似,进程也必须有足够的权限来创建fifo管道,因为该fifo的用户ID会被设置为该进程的有效用户ID,而组ID会被设置为该进程的有效组ID。

关于FIFO重要的一点是,在缺省情况下,它们是阻塞的。因此,读取一个fifo管道会被阻塞,直到另一端把数据写进来,反之亦然。为了避免这种情况,可以使用O_NOBLOCK参数来打开。在这种情况下,你会获得以下行为:对读的调用会立即返回,返回值为0;或者对写的调用会导致一个 SIGPIPE信号。
飞哥 2006-11-07
  • 打赏
  • 举报
回复
文件上锁

当多个进程试图写同一个文件,将发生什么?它们相互冲突,已知的事情像文件上锁。结果就是每个文件描都有自己的描述符跟偏移量,当每个进程写自己的文件时,偏移量预先独立导致没有进程知道其他的进程也正在执行写操作。最后的文件将因为多个独立写文件的操作使混合后的文件变得相当于垃圾,直接给文件上锁是解决这个问题的一种方式。在任意时刻只能让一个进程能够写到文件,另一种办法是允许在一个叫做高级文件锁的scheme里的文件内部进行区域锁定。 fcntl函数能够提供这个功能,通常来说,锁有两种,一种是写,另一种是写,不同之处在于读锁不会干扰其它进程读取文件,但是特定的区域只能一个写锁存在。

当使用顾问锁的时候,下面的结构用作fcntl的第三个参数。

struct flock {

off_t l_start; /* starting offset */

off_t l_len; /* len = 0 means until end of file */

pid_t l_pid; /* lock owner */

short l_type; /* lock type: */

short l_whence; /* type of l_start */

};



让我们继续讨论每个元素的细节。

l_start

这是一个相对于l_whence,单位为字节的偏移量,换句话说,要求的位置实际上是l_whence + l_start

l_len

需设置为期望位置的长度,单位为字节,锁将从l_whence + l_start开始锁定l_len字节,如果你想整个文件用一把锁,那么设定l_len的值为0,如果l_len的值是一个负数,结果是不可预测的。

l_pid

需要设置为工作在锁上的进程的ID

l_type

需要设置为期望的锁的类型,下面是能够使用的值

* F_RDLCK - 读锁定
* F_WRLCK - 写锁定
* F_UNLCK - 用作清除锁定

l_whence

这是这个系统调用里面最混乱的部分,这个字段将决定l_start位置的偏移量,需要设为:
* SEEK_CUR - 在当前位置
* SEEK_SET - 在文件开始
* SEEK_END - 在文件末尾

fcntl的命令

下面的可用作fcntl的命令

#define F_GETLK 7


F_GETLK 命令尝试检查否能上锁,当使用这个命令,fnctl将检查是否有相冲突的锁,如果存在相冲突的锁,fnctl将改写flock结构,用冲突锁消息通过检查,如果没有相冲突的锁,那么在flock结构最初的信息将被保留,除非l_type字段被设成F_UNLCK

#define F_SETLK 8


F_SETLK命令试图获得flock结构描述的锁,如果锁不被承认,本次调用将不被阻塞。不管怎样,fcntl将直接返回EAGAIN,同时将设置相应的errno,当flock结构的l_type被设置为F_UNLCK时,你能够使用这个命令清除一个锁。

#define F_SETLKW 9


F_GETLK命令试图获得flock结构描述的锁,它将命令fnctl阻塞,直到赋予一个锁

5.4为什么用FLOCK

对大部分情况来说,高级文件锁定机制是有好处的。然而,POSIX.1接口有几个缺点。第一,当一个文件的任何一个文件描述符被关闭时,与该文件关联的所有锁定必须被删除。换句话说,如果你有一个进程打开一个文件,接着它调用一个函数打开同一个文件,读取然后关闭它,这样先前你对这个文件的所有锁定都会被删除。如果你并不确定一个库例程会做什么,这将会引起严重问题。第二,锁定是不会传递给子进程的,所以一个子进程必须独立地创建属于它自己的锁定。第三,所有在一个exec调用之前获得的锁定在由exec启动的进程释放,关闭这个文件或结束之前不会被释放。所以,如果你需要锁定文件的某一部分,那么调用exec不需要释放锁定或者关闭文件描述符,那部分区域将被锁定直到进程结束,你想要的或许不是预期的结果,不论如何,BSD的设计者用许多flock创建了非常简单的优先级文件上锁的接口.

flock用于锁定整个文件,是BSD优先选择的方法,跟fcntl高级锁定相反,flock机制允许锁定进入子进程,使用flock调用的其他好处是可以在文件级并且不在文件描述符级别完成锁定,在某些情况下可用优先选择,意味着多个参照同一个文件的文件描述符,例如DUP()调用,或者多个OPEN()调用,希望每一个参照相同的文件锁,带flock的文件锁跟一个写多个读的fcntl锁很类似,不论如何,当下面的操作被定义,调用flock时,锁的优先级能够被提升:

#define LOCK_SH 0x01 /* shared file lock */


LOCK_SH操作用于在文件上(类似fcntl的读锁)创建共享锁,在一个文件上,多个进程能够共享一把锁。

#define LOCK_EX 0x02 /* exclusive file lock */

飞哥 2006-11-07
  • 打赏
  • 举报
回复
使用文件锁
不能同时写文件,应该建立互斥,所以你加锁试试
----------

共享锁
doudouHuY 2006-11-07
  • 打赏
  • 举报
回复
直接就可以并存吧
DJay 2006-11-07
  • 打赏
  • 举报
回复
但我只需要一个线程写,n个线程读而已,如果将FILE*变量设为全局变量就可以,但想像下面这个示例程序一样就不行咯。
// FileTest.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <process.h>
#include <iostream>
#include <conio.h>
#include <windows.h>
using namespace std;

unsigned int __stdcall func(void* p)
{
FILE *file2;
if(fopen_s(&file2,"test.txt","r") != 0)
cout<<"open error"<<GetLastError()<<endl;
while(1)
{
if(fseek(file2,0,SEEK_END) == -1)
cout<<"seek error"<<endl;
cout<<ftell(file2)<<endl;
Sleep(2000);
}
fclose(file2);
return 0;
}

int _tmain(int argc, _TCHAR* argv[])
{
FILE *file;
if(fopen_s(&file,"test.txt","w+") != 0)
cout<<"open error"<<endl;
char buf[100];
memset(buf,1,100);
_beginthreadex(NULL,0,&func,NULL,0,NULL);
while(1)
{
if(fwrite(buf,100,1,file) == 0)
cout<<"write error"<<endl;
Sleep(1000);
}
system("pause");
fclose(file);
return 0;
}

lann64 2006-11-06
  • 打赏
  • 举报
回复
??
read only应该可以打开的呀。

70,020

社区成员

发帖
与我相关
我的任务
社区描述
C语言相关问题讨论
社区管理员
  • C语言
  • 花神庙码农
  • 架构师李肯
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧