JavaScript is required to for searching.
跳过导航链接
退出打印视图
编程接口指南     Oracle Solaris 10 1/13 Information Library (简体中文)
search filter icon
search icon

文档信息

前言

1.  内存和 CPU 管理

2.  用于 Solaris Cluster 的远程共享内存 API

3.  会话描述协议 API

4.  进程调度程序

5.  地址组 API

6.  输入/输出接口

文件和 I/O 接口

基本文件 I/O

高级文件 I/O

文件系统控制

使用文件和记录锁定

选择锁定类型

选择建议性锁定或强制性锁定

关于强制性锁定的注意事项

支持的文件系统

打开文件进行锁定

设置文件锁定

设置和删除记录锁定

获取锁定信息

进程派生和锁定

死锁处理

终端 I/O 函数

7.  进程间通信

8.  套接字接口

9.  使用 XTI 和 TLI 编程

10.  包过滤钩子

11.  传输选择和名称到地址映射

12.  实时编程和管理

13.  Solaris ABI 和 ABI 工具

A.  UNIX 域套接字

索引

使用文件和记录锁定

不需要使用传统的文件 I/O 来锁定文件元素。应针对映射文件使用《多线程编程指南》中介绍的较轻量级同步机制。

锁定文件可防止当多个用户同时尝试更新一个文件时可能会发生的错误。可以锁定文件的一部分。

锁定文件后,将阻止对整个文件进行访问。锁定记录后,将阻止对文件中指定的段进行访问。在 SunOS 中,所有文件都是一系列的数据字节:“记录”这一概念是指使用该文件的程序。

选择锁定类型

强制性锁定将暂停进程,直到所请求的文件段被释放。建议性锁定返回一个表明是否成功获得锁定的结果。进程可以忽略建议性锁定的结果。对于同一文件,不能同时使用强制性和建议性文件锁定。打开文件时所用的模式决定了对文件的锁定是强制性锁定还是建议性锁定。

在两种基本锁定调用中,fcntl(2)lockf(3C) 更容易移植、功能更强大,但更不易用。fcntl(2) 是在 POSIX 1003.1 标准中指定的。提供 lockf(3C) 是为了与较早版本的应用程序兼容。

选择建议性锁定或强制性锁定

对于强制性锁定,文件必须是设置了 set-group-ID 位而未设置组执行权限的常规文件。如果不符合其中任一条件,则所有记录锁定均为建议性锁定。

可按如下方式设置强制性锁定。

#include <sys/types.h>
#include <sys/stat.h>

 int mode;
 struct stat buf;
     ...
     if (stat(filename, &buf) < 0) {
         perror("program");
         exit (2);
     }
     /* get currently set mode */
     mode = buf.st_mode;
     /* remove group execute permission from mode */
     mode &= ~(S_IEXEC>>3);
         /* set 'set group id bit' in mode */
     mode |= S_ISGID;
     if (chmod(filename, mode) < 0) {
         perror("program");
         exit(2);
     }
     ... 

当系统在执行文件时,操作系统会忽略记录锁定。所有带有记录锁定的文件都不应设置执行权限。

chmod(1) 命令还可用于将文件设置为允许强制性锁定。

$ chmod +l file

此命令在文件模式中设置 O20n0 权限位,用来指示对文件进行强制性锁定。如果 n 是偶数,则此位将会解释为启用强制性锁定。如果 n 是奇数,则此位将会解释为“执行时设置组 ID”。

当使用 -l 选项请求长列表格式时,ls(1) 命令显示以下设置:

$ ls -l file

此命令显示以下信息:

-rw---l--- 1 user group size mod_time file

权限中的字母 "l" 指示已设置 set-group-ID 位。由于已设置 set-group-ID 位,因此启用强制性锁定。此外,还启用了设置组 ID (set group ID) 的一般语义。

关于强制性锁定的注意事项

请牢记锁定的以下方面:

支持的文件系统

下表列出的文件系统既支持建议性锁定又支持强制性锁定。

表 6-4 支持的文件系统

文件系统
说明
ufs
基于磁盘的缺省文件系统
fifofs
由命名管道文件组成的伪文件系统,这些管道文件可为进程提供对数据的通用访问权限
namefs
主要由 STREAMS 用来在文件顶部动态挂载文件描述符的伪文件系统
specfs
可用于访问特殊字符设备和块设备的伪文件系统

NFS 仅支持建议性文件锁定。procfd 文件系统不支持文件锁定。

打开文件进行锁定

只能使用有效的打开描述符来请求对文件进行锁定。对于读取锁定,文件必须至少是使用读取权限打开的。对于写入锁定,文件也必须是使用写入权限打开的。在以下示例中,将打开文件以进行读取和写入访问。

...
     filename = argv[1];
     fd = open (filename, O_RDWR);
     if (fd < 0) {
         perror(filename);
         exit(2);
     }
     ...

设置文件锁定

要锁定整个文件,请将偏移量设置为零,并将大小设置为零。

可以通过多种方法设置文件锁定。选择哪种方法取决于该锁定与程序其余部分如何交互以及性能和可移植性。本示例使用与 POSIX 标准兼容的 fcntl(2) 接口。此接口尝试锁定文件,直到发生以下情况之一:

设置和删除记录锁定

锁定记录时,请不要将锁定段的起始点和长度设置为零。否则,此锁定过程与文件锁定相同。

使用记录锁定的原因是存在数据争用。因此,当无法获取所有所需锁定时,应采取相应的失败响应措施:

本示例显示了一个使用 fcntl(2) 锁定的记录。

{
     struct flock lck;
       ...
     lck.l_type = F_WRLCK;    /* setting a write lock */
     lck.l_whence = 0;    /* offset l_start from beginning of file */
     lck.l_start = here;
     lck.l_len = sizeof(struct record);

     /* lock "this" with write lock */
     lck.l_start = this;
     if (fcntl(fd, F_SETLKW, &lck) < 0) {
         /* "this" lock failed. */
         return (-1);
 ...
}

下一示例显示了 lockf(3C) 接口。

#include <unistd.h>

{
 ...
     /* lock "this" */
     (void) lseek(fd, this, SEEK_SET);
     if (lockf(fd, F_LOCK, sizeof(struct record)) < 0) {
         /* Lock on "this" failed. Clear lock on "here". */
         (void) lseek(fd, here, 0);
         (void) lockf(fd, F_ULOCK, sizeof(struct record));
         return (-1);
}
 

可使用与设置锁定相同的方法来删除锁定。只是锁定类型有所不同 (F_ULOCK)。解除锁定不受其他进程的阻止,并且只影响调用进程所设置的锁定。解除锁定只影响在先前锁定调用中指定的文件段。

获取锁定信息

可以确定哪个进程在持有锁定。可以按照前面示例所示设置锁定,并在 fcntl(2) 中使用 F_GETLK

下一示例查找并输出文件中所有锁定段上的标识数据。

示例 6-2 输出文件的锁定段

struct flock lck;

     lck.l_whence = 0;
     lck.l_start = 0L;
     lck.l_len = 0L;
     do {
         lck.l_type = F_WRLCK;
         (void) fcntl(fd, F_GETLK, &lck);
         if (lck.l_type != F_UNLCK) {
             (void) printf("%d %d %c %8ld %8ld\n", lck.l_sysid, lck.l_pid,
            (lck.l_type == F_WRLCK) ? 'W' : 'R', lck.l_start, lck.l_len);
             /* If this lock goes to the end of the address space, no
              * need to look further, so break out. */
             if (lck.l_len == 0) {
             /* else, look for new lock after the one just found. */
                     lck.l_start += lck.l_len;
             }
         }
     } while (lck.l_type != F_UNLCK);

fcntl(2)F_GETLK 命令可以在等待服务器响应时处于休眠状态。如果客户机或服务器出现资源不足的情况,则此命令可能会失败,同时返回 ENOLCK

lockf(3C)F_TEST 命令一起使用来测试进程是否在持有锁定。此接口并不返回有关锁定的位置或拥有权的信息。

示例 6-3 使用 lockf 测试进程

(void) lseek(fd, 0, 0L);
 /* set the size of the test region to zero (0). to test until the
    end of the file address space. */
if (lockf(fd, (off_t)0, SEEK_SET) < 0) {
    switch (errno) {
        case EACCES:
        case EAGAIN:
            (void) printf("file is locked by another process\n");
            break;
        case EBADF:
            /* bad argument passed to lockf */
            perror("lockf");
            break;
        default:
            (void) printf("lockf: unexpected error <%d>\n", errno);
            break;
    }
}

进程派生和锁定

进程派生时,子进程会收到父进程打开的文件描述符的副本。子进程不继承锁定,因为锁定由特定进程拥有。父进程和子进程共享每个文件的公用文件指针。这两个进程都可能会尝试对同一文件中的相同位置设置锁定。使用 lockf(3C)fcntl(2) 都会出现此问题。如果持有记录锁定的程序进行派生,则子进程应该关闭此文件。关闭此文件之后,子进程应该重新打开此文件以设置新的独立文件指针。

死锁处理

UNIX 锁定功能可以检测和避免死锁。仅当系统准备将记录锁定接口置于休眠状态时才会发生死锁。执行搜索来确定两个进程是否处于死锁状态。如果检测到潜在的死锁,则锁定接口将失败,并设置 errno 以指示死锁。使用 F_SETLK 设置锁定的进程不会导致死锁,因为当不能被授予锁定时,这些进程并不会等待。