Chinaunix首页 | 论坛 | 博客
  • 博客访问: 3350725
  • 博文数量: 1450
  • 博客积分: 11163
  • 博客等级: 上将
  • 技术积分: 11101
  • 用 户 组: 普通用户
  • 注册时间: 2005-07-25 14:40
文章分类

全部博文(1450)

文章存档

2017年(5)

2014年(2)

2013年(3)

2012年(35)

2011年(39)

2010年(88)

2009年(395)

2008年(382)

2007年(241)

2006年(246)

2005年(14)

分类: LINUX

2007-12-22 16:53:23


记录锁


1.记录锁的功能

       当一个进程正在读或修改文件的某个部分时,它可以阻止其他进程修改同一文件区。我们不应该从字面上去理解记录锁,实际上它应该叫“区域锁”,因为它锁定的只是文件的一个(也可能是整个文件)。这个区域用来存放多用户的共享区。

2.记录锁的分类

       记录锁分为共享读锁和独占写锁,前者也叫做共享锁后者也叫做排他锁。

3.加锁规则

      如果一个进程对共享区加了共享读锁,其他进程只能加共享读锁。如果一个进程加了独占写锁,其他进程就不能加任何锁。

4.死锁

       如果两个相互等待对方持有并且不释放(已被锁定)的资源是时,则这两个进程就处于死锁状态。如果一个进程已经控制了文件的一个加锁区域,然后它又试图对另一个进程控制的区域加锁,则它就会睡眠,在这种情况下,有发生死锁的可能性。

5.锁的隐含继承和释放

1)锁与进程和文件两方面有关系,它和前者关系是:当一个进程结束后,他对文件加的锁也就消失了。它和后者的关系是:当进程close文件描述符,切断文件和进程的联系进程所创建的锁也会消失。

2)由fork产生的子进程不继承父进程所设置的锁。这意味着,若一个进程得到一把锁,然后调用fork,那么对于父进程创建的锁而言,子进程被视为另一个进程,不会拥有该锁。

3)在执行exec后,新进程可以继承原执行的锁。因为执行exec前后还是一个进程。我们只是改变进程执行的程序,并没有创建新的进程。

6.要注意的问题

    记录锁只是提供竞争进入某段代码区的功能,不会导致对文件操作失败。也就是说,我们对文件进行加锁后,我们还是可以对文件进行操作。

 

1

名称:

fcntl

功能

对文加解锁。

头文件

#include

函数原形

int fcntl(int filedes,int cmd,…/*struct flock *flockptr */)

参数

filedes   文件描述符

cmd     测试锁或加锁

flockptr  指向flock结构的指针

返回值:

若成功返回0,若失败返回错误编号。

      

 

 

 

 对于记录锁,cmdF_GETLK,F_SETLKWF_SETLKW.

       F_GETLK判断由flockptr所描述的锁是否会被另外一把锁排斥。如果存在一把锁,他阻止创建由flockptr所描述的锁,则把该现存锁的信息写到flockptr指向的结构中。如果不存在这种情况除了将l_type设置为F_UNLCK之外,flockptr所描述的其他信息都不变。

F_SETLKF_SETLKW企图建立一把锁。F_SETLKF_SETLKW的区别是F_SETLKWF_SETLK的阻塞版本。如果存在其他锁,调用的进程就被阻塞直道捕捉到信号。

       第三个参数是一个指向flock结构的指针:

struct flock{

       short l_type;

       off_t l_start;

       shout l_whence;

       off_t l_len;

       pid_t l_pid;

};

flock结构说明:

所希望的锁类型:F_RDLCK(共享读锁)F_WRLCK(独占性写锁)F_UNLCK(解锁一个区域),这是由 l_type决定的。

要加锁或解锁区域的起始字节偏移量,这是由l_stattl_whence两者决定。

区域的字节长度,由l_len表示。

具有能阻塞当前进程的锁,其持有的ID存放在l_pid中。

       如若l_len0,则表示锁的区域从其起点(由l_startl_whence决定)开始直至最大可能位置为止。也就是不管添写到该文件中多少数据,它都处于的范围。

       如果想锁住整个文件,通常的方法是将l_start说明为0l_whence说明为SEEK_SET1_len说明为0

       还要注意的是,对文件加共享读锁时文件应以只读的方式打开,对文件加独占写锁时文件应以只读的方式打开。


I/O多路转接

       如果我们想从多个文件描述符读或写数据,如果我们用以前学过的函数(read,write等)去处理可能会阻塞在一个文件描述符上,不能处理其他的文件描述符。那是因为我们以前学的I/O处理函数,都是阻塞的I/O处理函数,它们的特点是,如果缓冲区里有数据它们就会把数据写到文件中,如果缓存区没有数据他们就会等待(阻塞)直到有数据可读。这就造成了他们无法对多个文件描述符进行操作。而对多个文件描述符进行操作在网络通信方面却是执关重要的。

       一种比较好的解决方案就是I/O多路转接技术。它现构造一张有关文件描述符的列表,然后调用一个函数,直到这些描述符中的一个已经准备好进行I/O时,该函数才返回。在返回时,它告诉进程那些描述符已经准备好可以进行I/Opoll,selsct,pselect这三个函数使我们能够执行I/O多路转接,下面就分别介绍它们。

 

2

名称:

select

功能

指行I/O多路转接

头文件

#include

函数原形

int select(int maxfdpl,fd_set *restrict readfds,fd_set *restrict writefds,fd_set *testrict exceptfds,struct timeval *testrict tvptr);

参数

maxfdpl   最大描述符加1

readfds     读描述符集   

writefds    写描述符集

excepfds    异常描述符集

tvptr      愿意等待的时间

返回值:

准备就绪的文件描述符数,若超时则返回0,若出错则返回-1

     

select函数使我们可以执行I/O多路转接。传向select的参数告诉内核:我们所关系的描述符。对于每个描述符我们所关心的状态。以及我们愿意等待的时间。从select返回时,内核告诉我们:以准备好的描述符的数量。对于读、写或异常这三个状态中的每一个,那些描述符已经准备好。

       这个函数比较复杂,我们一个一个参数的看。

       第一个参数maxfdp1的意思是“最大描述符加1。也可将第一个参数设置为FD_SETSIZE,这是中的一个常数,它说明了最大的描述符数(经常是1024)。如果将第三个参数设置为我们所关注的最大描述符编号值加一,内核就只需在此范围内寻找打开的位,而不必在三个描述符集中的数百位内搜索。

       中间的三个参数readfdswritefdsexceptfds是指向描述符集的指针。这三个描述符集说明了我们关心的可读(readfds)、可写(writefd)或处于异常条件(wxcepfds)的各个描述符。每个描述符集存放在一个fd_set数据类型中。这种结构相当于一个描述符的数组,它为每个可能的描述符设置1位。

               fd0     fd1     fd2     fd3       fdn

0

0

0

0

readfdsà

 

               fd0     fd1     fd2     fd3       fdn

0

0

0

0

writefdsà

 

               fd0     fd1     fd2     fd3       fdn

0

0

0

0

excepfdsà

 

       可用下面4个函数对描述符集进行操作。

 

       select的中间三个参数中的任意一个或全部都可以是空指针,这表示对相应状态不关系。如果所有三个指针都是空指针,则select提供了较sleep更精确的计时器。其等待时间可以小于1秒。

      

       tuptr指定最后等待的时间,它的结构是:

struct timeval{

       long tv_sec;

       long tv_usec; 微秒

};

有三种情况:

(1) tvptr==NULL:永远等待。如果捕捉到一个信号则中断此无限等待。当所指定的描述符中的一个已经准备好或捕捉到一个信号则返回。如果捕捉到一个信号,则select返回-1errno设置为EINTR.

(2) tvptr->tv_sec==0&&tvptr_usec==0 完全不等待。测试所有的描述符并立即返回。这是得到多个描述符的状态而不阻塞select函数的轮询方法。

(3tvptr->tv_sec!=0||tvptr_usec!=0 等待指定的秒数或微秒数。当指定的描述符之一已准备好,或当指定的时间值已超过时立即返回。如果在超时还没有一个描述符准备好,则返回值是0

 

3

名称:

FD_ISSET/FD_CLR/FD_SET/FD_ZERO

功能

描述符集处理函数

头文件

#include

函数原形

int FD_ISSET(int fd,fd_set *fdset);

void FD_CLR(int fd,fd_set *fdset);

void FD_SET(int fd,fd_set *fdset);

void FD_ZERO(fd_set *fdset);

参数

fdset     描述符集

fd        描述符

返回值:

fd在描述符集中则返回非0值,否则返回0FD_ISSET

     

调用FD_ZERO将一个指定的fd_set变量的所有位设置为0。调用FD_SET设置一个fd_set变量的指定位。调用FD_CLR将一指定位清除。最后调用FD_ISSET测试一指定位是否设置。声明了一个描述符集后,必须用FD_ZERO清除其所有位,然后在其中设置我们关心的各个位。


4

名称:

pselect

功能

指行I/O多路转接

头文件

#include

函数原形

int pselect(int masfdp1,fd_set *restrict readfds,fd_set *restrict writefds,fd_set excepfds,const struct timespec *restrict tsptr,const sigset_t *restrict sigmask);

参数

maxfdpl    最大描述符加1

readfds     读描述符集   

writefds    写描述符集

excepfds    异常描述符集

tsptr       愿意等待时间

sigmask    信号屏蔽集

返回值:

fd在描述符集中则返回非0值,否则返回0FD_ISSET

                                          pselectselect的一个变体,除以下几点外,pselectselect相同:

(1)       select的超时值用timeval结构指定,但pselect使用timespec结构指定。timespec以秒和纳秒表示超时值。

(2)       pselect的超时值被定义为const,这保证了调用pselect不会改变此值,。

(3)       对于paselect可以使用一可选的信号屏蔽字。若sigmask为空,那么在于信号有关的方面,pselectselect相同。否则,sigmask指向一信号屏蔽字,在调用pselect时,以原子操作的方式安装该信号屏蔽字。在返回时恢复以前的信号屏蔽字。

 

5

名称:

poll

功能

指行I/O多路转接

头文件

#include

函数原形

int poll(struct pollfd fdarray[],nfds_t nfds,int timeout);

参数

fdarray      存放描述符集的数组

nfds        fdarray数组元素个数

timeout     超时等待时间

返回值:

准备就绪的描述符数,若超时则返回0,若出错则返回-1

    

poll类似于select,但是其接口则有所不同。poll不时为每个状态(可读性,可写性和异常状态)构造一个描述符集,而是构造一个pollfd结构的数组,每个数组元素指定一个描述符编号以及其所关心的状态。

struct pollfd{

       int fd; 文件描述符

       shout events;

       shout revents;

};

       fdarray数组中的元素由nfds说明。

应将每个数组元素的events成员设置为下表的值。通过这些告诉内核我们对该描述符关系的时什么。返回时,内核设置revents成员,以说明对于该描述符已经发生了什么事件。

 

标志名

说明

POLLIN

POLLRDNORM

POLLRDBAND

POLLPRI

不阻塞地可读除高优先级外的数据(等效于POLLRDNORM|POLLRDBAD

不阻塞地可读普通数据(优先级波段为0

不阻塞地可读非0优先级波段数据

不阻塞地可读高优先级数据

POLLOUT

POLLWRNORM

POLLWRBAND

不阻塞地可写普通数据

POLLOUT相同

不阻塞地可写非0优先级波段数据

POLLERR

POLLHUP

POLLNVAL

已出错

已挂断

描述符不引用一打开文件

       表头四行测试可读性,接着三行测试可写性,最后三行则是测试异常状态。最后三行是由内核在返回时设置的。即使在events字段中没有指定这三个值,如果相应条件发生,则在revents中也它们。

       当一个描述符被挂断后,就不能再写向该描述符。但是仍可能从该描述符读取数据。

poll的最后一个参数说明我们愿意等待多少时间。如同sellect一样,有三种不同情形:

      (1) timeout==-1永远等待,当所指定的描述符中的一个已准备好,或捕捉到一个信号时则返回。如果捕捉到一个信号,则poll返回-1error设置为EINTR.

       (2)timeout==0 不等待。测试所有的描述符并立即返回。这是得到多个描述符的状态而不阻塞select函数的轮询方法。

       (3)timeout>0 等待timeout毫秒。当指定的描述符之一已准备好,或当指定的时间值已超过时立即返回。如果在超时还没有一个描述符准备好,则返回值是0

 

三、读写多个缓冲区

6

名称:

readv/writev

功能

散布读/聚集写

头文件

#include

函数原形

ssize_t readv(int filedes,const struct iovec *iov,int iovcnt);

ssize_t writev(int filedes,const struct iovec *iov,int iovcnt);

参数

filedes     文件描述符

iov       指向iovec结构数组的一个指针。

iovcnt     数组元素的个数

返回值:

若成功则返回已读、写的字节数,若出错则返回-1

     

readvwritev函数用于在一次函数调用中读、写多个非连续缓冲区。有时也将这两个函数成为散布读和聚集写。

       这两个函数的第二个参数是指向iovec结构数组的一个指针:

       struct iovec{

              void *iov_base;

              size_t iov_len;

       };

       writev以顺序iov[0]iov[iovcnt-1]从缓冲区中聚集输出数据。writev返回输出的字节总数,通常,它应等于所有缓冲区长度之和。

       readv则将读入的数据按上述同样顺序散布读到缓冲区中。readv总是先填满一个缓冲区,然后再填写下一个。readv返回读到的总字节数。如果遇到文件结尾,已无数据可读,则返回0


四、存储映射I/O

       存储映射I/O使一个磁盘文件与存储空间中的一个缓冲区相映射。于是当从缓冲区中取数据,就相当于读文件中的相应字节。与此类似,将数据存入缓冲区,则相应字节自动地写入文件。这样就可以在不使用readwrite的情况下执行I/O

 

6.

名称:

mmap

功能

I/O文件映射到一个存储区域中

头文件

#include

函数原形

void *mmap(void *addr,size_t len,int prot,int flag,int filedes,off_t off);

参数

addr      指向映射存储区的起始地址

len       映射的字节

prot      对映射存储区的保护要求

flag      flag标志位

filedes    要被映射文件的描述符

off       要映射字节在文件中的起始偏移量

返回值:

若成功则返回映射区的起始地址,若出错则返回MAP_FAILED

   

 addr参数用于指定映射存储区的起始地址。通常将其设置为0,这表示由系统选择该映射区的起始地址。

       filedes指要被映射文件的描述符。在映射该文件到一个地址空间之前,先要打开该文件。len是映射的字节数。

       off是要映射字节在文件中的起始偏移量。通常将其设置为0

       prot参数说明对映射存储区的保护要求。可将prot参数指定为PROT_NONE,或者是PROT_READ(映射区可读),PROT_WRITE(映射区可写),PROT_EXEC(映射区可执行)任意组合的按位或,也可以是PROT_NONE(映射区不可访问)。对指定映射存储区的保护要求不能超过文件open模式访问权限。

       flag参数影响映射区的多种属性:    

MAP_FIXED 返回值必须等于addr.因为这不利于可移植性,所以不鼓励使用此标志。

MAP_SHARED 这一标志说明了本进程对映射区所进行的存储操作的配置。此标志指定存储操作修改映射文件。

MAP_PRIVATE 本标志导致对映射区建立一个该映射文件的一个私有副本。所有后来对该映射区的引用都是引用该副本,而不是原始文件。

要注意的是必须指定MAP_FIXEDMAP_PRIVATE标志其中的一个,指定前者是对存储映射文件本身的一个操作,而后者是对其副本进行操作。

 

7

名称:

memcpy

功能

复制映射存储区

头文件

#include

函数原形

void *memcpy(void *dest,const void *src,size_t n);

参数

dest       待复制的映射存储区

src        复制后的映射存储区

n          待复制的映射存储区的大小

返回值:

返回dest的首地址

      

 

 

 

 memcpy拷贝n个字节从destsrc

 

8

名称:

munmap

功能

解除存储映射

头文件

#include

函数原形

int munmap(caddr_t addr,size_t len);

参数

addr      指向映射存储区的起始地址

len       映射的字节

返回值:

若成功则返回0,若出错则返回-1

     

进程终止时,或调用了munmap之后,存储映射区就被自动解除映射。关闭文件描述符filedes并不解除映射区。

       munmap不会影响被映射的对象。

9

名称:

mprotect

功能

改变映射存储区的权限

头文件

#include

函数原形

int mprotect(void *addr,size_t len,int prot);

参数

addr      指向映射存储区的起始地址

len       映射的字节

prot      对映射存储区的保护要求

返回值:

若成功则返回0,若出错则返回-1

       mprotect可以更改一个现存映射存储区的权限。

 

10

名称:

msync

功能

同步文件到存储器

头文件

#include

函数原形

int msync(void *addr,size_t len,int flags);

参数

addr      指向映射存储区的起始地址

len       映射的字节

prot      flags

返回值:

若成功则返回0,若出错则返回-1

       如果在共享映射区中的页已被修改,那么我们可以调用msync将该页冲洗到映射的文件中。flags参数使我们对如何冲洗存储区有某种程度的控制。我们可以指定MS_ASYNC标志以简被写页的调度。如果我们希望在返回之前等待写操作的完成,则可指定MS_SYNC标志。一定要制定MSASYNCMS_SYNC中的一个。MS_INVALIDATE是一个可选的标志,使用它会通知操作系统丢弃与底层存储器没有同步的任何页。


阅读(826) | 评论(0) | 转发(0) |
0

上一篇:进程间通信IPC

下一篇:dup 与 dup2 的作用

给主人留下些什么吧!~~