复制文件描述符 、 文件锁 、 文件元数据
1 复制文件描述符
1.1 问题
可以使用dup函数和dup2函数实现对文件描述符的复制。
1.2 步骤
实现此案例需要按照如下步骤进行。
步骤一:dup函数
代码如下所示:
- #include <stdio.h>
- #include <fcntl.h>
- #include <unistd.h>
- #include <string.h>
- #include <stdlib.h>
- int main()
- {
- int fd = open ("dup.txt", O_RDWR | O_CREAT | O_TRUNC, 0666);
- if (fd == -1)
- {
- perror ("open");
- exit (EXIT_FAILURE);
- }
- char buf[1024] = "这是使用原文件描述符存入的内容。";
- if (write (fd, buf, strlen(buf) * sizeof (buf[0])) == -1)
- {
- perror ("write");
- exit (EXIT_FAILURE);
- }
- int newfd = dup(fd);
- strcpy(buf, "这是使用dup复制后的新文件描述符存入的内容。");
- if (write (newfd, buf, strlen(buf) * sizeof (buf[0])) == -1)
- {
- perror ("write");
- exit (EXIT_FAILURE);
- }
- if (close (fd) == -1)
- {
- perror ("close");
- exit (EXIT_FAILURE);
- }
- if (close (newfd) == -1)
- {
- perror ("close");
- exit (EXIT_FAILURE);
- }
- return 0;
- }
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
int main()
{
int fd = open ("dup.txt", O_RDWR | O_CREAT | O_TRUNC, 0666);
if (fd == -1)
{
perror ("open");
exit (EXIT_FAILURE);
}
char buf[1024] = "这是使用原文件描述符存入的内容。";
if (write (fd, buf, strlen(buf) * sizeof (buf[0])) == -1)
{
perror ("write");
exit (EXIT_FAILURE);
}
int newfd = dup(fd);
strcpy(buf, "这是使用dup复制后的新文件描述符存入的内容。");
if (write (newfd, buf, strlen(buf) * sizeof (buf[0])) == -1)
{
perror ("write");
exit (EXIT_FAILURE);
}
if (close (fd) == -1)
{
perror ("close");
exit (EXIT_FAILURE);
}
if (close (newfd) == -1)
{
perror ("close");
exit (EXIT_FAILURE);
}
return 0;
}
上述代码中,以下代码:
- int newfd = dup(fd);
int newfd = dup(fd);
使用dup函数复制文件描述符。dup函数将实参fd所对应的文件描述符表项复制到文件描述符表第一个空闲项中,同时返回该表项所对应的新文件描述符,该新文件描述符一定是调用进程当前未使用的最小文件描述符。dup函数只复制文件描述符表项,不复制文件表项和v节点,因此该函数所返回的新文件描述符可以看做是参数文件描述符fd的副本,它们标识同一个文件表项。dup函数究竟会把fd参数所对应的文件描述符表项,复制到文件描述符表的什么位置,程序员是无法控制的,这完全由调用该函数时文件描述符表的使用情况决定,因此对该函数的返回值做任何约束性假设都是不严谨的
上述代码中,以下代码:
- if (write (newfd, buf, strlen(buf) * sizeof (buf[0])) == -1)
- {
- perror ("write");
- exit (EXIT_FAILURE);
- }
if (write (newfd, buf, strlen(buf) * sizeof (buf[0])) == -1)
{
perror ("write");
exit (EXIT_FAILURE);
}
使用write函数向新的文件描述符写入数据,数据仍将被写入dup.txt文件中。因为由dup函数返回的文件描述符与作为参数传递给该函数的文件描述符标识的是同一个文件表项,而文件读写位置是保存在文件表项而非文件描述符表项中的,因此通过这些文件描述符中的任何一个,对文件进行读写或随机访问,都会影响通过其它文件描述符操作的文件读写位置。
上述代码中,以下代码:
- if (close (fd) == -1)
- {
- perror ("close");
- exit (EXIT_FAILURE);
- }
- if (close (newfd) == -1)
- {
- perror ("close");
- exit (EXIT_FAILURE);
- }
if (close (fd) == -1)
{
perror ("close");
exit (EXIT_FAILURE);
}
if (close (newfd) == -1)
{
perror ("close");
exit (EXIT_FAILURE);
}
当关闭文件时,即使是由dup函数产生的文件描述符副本,也应该通过close函数关闭,因为只有当关联于一个文件表项的所有文件描述符都被关闭了,该文件表项才会被销毁,类似地,也只有当关联于一个v节点的所有文件表项都被销毁了,v节点才会被从内存中删除,因此从资源合理利用的角度讲,凡是明确不再继续使用的文件描述符,都应该尽可能及时地用close函数关闭。
步骤二:dup2函数
代码如下所示:
- #include <stdio.h>
- #include <fcntl.h>
- #include <unistd.h>
- #include <string.h>
- #include <stdlib.h>
- int main()
- {
- int fd = open ("dup2.txt", O_RDWR | O_CREAT | O_TRUNC, 0666);
- if (fd == -1)
- {
- perror ("open");
- exit (EXIT_FAILURE);
- }
- char buf[1024] = "这是使用原文件描述符存入的内容。";
- if (write (fd, buf, strlen(buf) * sizeof (buf[0])) == -1)
- {
- perror ("write");
- exit (EXIT_FAILURE);
- }
- int newfd = dup2(STDOUT_FILENO, fd);
- strcpy(buf, "这是使用dup2复制后的新文件描述符存入的内容, 但这些内容将被输出到控制台。\n");
- if (write (newfd, buf, strlen(buf) * sizeof (buf[0])) == -1)
- {
- perror ("write");
- exit (EXIT_FAILURE);
- }
- if (close (fd) == -1)
- {
- perror ("close");
- exit (EXIT_FAILURE);
- }
- if (close (newfd) == -1)
- {
- perror ("close");
- exit (EXIT_FAILURE);
- }
- return 0;
- }
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
int main()
{
int fd = open ("dup2.txt", O_RDWR | O_CREAT | O_TRUNC, 0666);
if (fd == -1)
{
perror ("open");
exit (EXIT_FAILURE);
}
char buf[1024] = "这是使用原文件描述符存入的内容。";
if (write (fd, buf, strlen(buf) * sizeof (buf[0])) == -1)
{
perror ("write");
exit (EXIT_FAILURE);
}
int newfd = dup2(STDOUT_FILENO, fd);
strcpy(buf, "这是使用dup2复制后的新文件描述符存入的内容, 但这些内容将被输出到控制台。\n");
if (write (newfd, buf, strlen(buf) * sizeof (buf[0])) == -1)
{
perror ("write");
exit (EXIT_FAILURE);
}
if (close (fd) == -1)
{
perror ("close");
exit (EXIT_FAILURE);
}
if (close (newfd) == -1)
{
perror ("close");
exit (EXIT_FAILURE);
}
return 0;
}
上述代码中,以下代码:
- int newfd = dup2(STDOUT_FILENO, fd);
int newfd = dup2(STDOUT_FILENO, fd);
使用dup2函数复制文件描述符表项到指定位置。该函数有两个参数,说明如下:
第一个参数为源文件描述符。
第二个参数为目标文件描述符。
dup2函数的功能与dup函数几乎完全一样,唯一的不同就是允许调用者通过第二个参数指定目标文件描述符,正常情况下该函数的返回值应该与第二个参数的值相等。
上述代码中,以下代码:
- if (write (newfd, buf, strlen(buf) * sizeof (buf[0])) == -1)
- {
- perror ("write");
- exit (EXIT_FAILURE);
- }
if (write (newfd, buf, strlen(buf) * sizeof (buf[0])) == -1)
{
perror ("write");
exit (EXIT_FAILURE);
}
将把字符数组内的内容输出到控制台,因为dup2函数在复制由第一个参数所标识的源文件描述符表项时,会首先检查由第二个参数所标识的目标文件描述符表项是否空闲,若空闲则直接将前者复制给后者,否则会先将第二个参数所标识的目标文件描述符关闭,再行复制。
1.3 完整代码
本案例的完整代码如下所示:
dup函数代码:
- #include <stdio.h>
- #include <fcntl.h>
- #include <unistd.h>
- #include <string.h>
- #include <stdlib.h>
- int main()
- {
- int fd = open ("dup.txt", O_RDWR | O_CREAT | O_TRUNC, 0666);
- if (fd == -1)
- {
- perror ("open");
- exit (EXIT_FAILURE);
- }
- char buf[1024] = "这是使用原文件描述符存入的内容。";
- if (write (fd, buf, strlen(buf) * sizeof (buf[0])) == -1)
- {
- perror ("write");
- exit (EXIT_FAILURE);
- }
- int newfd = dup(fd);
- strcpy(buf, "这是使用dup复制后的新文件描述符存入的内容。");
- if (write (newfd, buf, strlen(buf) * sizeof (buf[0])) == -1)
- {
- perror ("write");
- exit (EXIT_FAILURE);
- }
- if (close (fd) == -1)
- {
- perror ("close");
- exit (EXIT_FAILURE);
- }
- if (close (newfd) == -1)
- {
- perror ("close");
- exit (EXIT_FAILURE);
- }
- return 0;
- }
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
int main()
{
int fd = open ("dup.txt", O_RDWR | O_CREAT | O_TRUNC, 0666);
if (fd == -1)
{
perror ("open");
exit (EXIT_FAILURE);
}
char buf[1024] = "这是使用原文件描述符存入的内容。";
if (write (fd, buf, strlen(buf) * sizeof (buf[0])) == -1)
{
perror ("write");
exit (EXIT_FAILURE);
}
int newfd = dup(fd);
strcpy(buf, "这是使用dup复制后的新文件描述符存入的内容。");
if (write (newfd, buf, strlen(buf) * sizeof (buf[0])) == -1)
{
perror ("write");
exit (EXIT_FAILURE);
}
if (close (fd) == -1)
{
perror ("close");
exit (EXIT_FAILURE);
}
if (close (newfd) == -1)
{
perror ("close");
exit (EXIT_FAILURE);
}
return 0;
}
dup2函数代码:
- #include <stdio.h>
- #include <fcntl.h>
- #include <unistd.h>
- #include <string.h>
- #include <stdlib.h>
- int main()
- {
- int fd = open ("dup2.txt", O_RDWR | O_CREAT | O_TRUNC, 0666);
- if (fd == -1)
- {
- perror ("open");
- exit (EXIT_FAILURE);
- }
- char buf[1024] = "这是使用原文件描述符存入的内容。";
- if (write (fd, buf, strlen(buf) * sizeof (buf[0])) == -1)
- {
- perror ("write");
- exit (EXIT_FAILURE);
- }
- int newfd = dup2(STDOUT_FILENO, fd);
- strcpy(buf, "这是使用dup2复制后的新文件描述符存入的内容, 但这些内容将被输出到控制台。\n");
- if (write (newfd, buf, strlen(buf) * sizeof (buf[0])) == -1)
- {
- perror ("write");
- exit (EXIT_FAILURE);
- }
- if (close (fd) == -1)
- {
- perror ("close");
- exit (EXIT_FAILURE);
- }
- if (close (newfd) == -1)
- {
- perror ("close");
- exit (EXIT_FAILURE);
- }
- return 0;
- }
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
int main()
{
int fd = open ("dup2.txt", O_RDWR | O_CREAT | O_TRUNC, 0666);
if (fd == -1)
{
perror ("open");
exit (EXIT_FAILURE);
}
char buf[1024] = "这是使用原文件描述符存入的内容。";
if (write (fd, buf, strlen(buf) * sizeof (buf[0])) == -1)
{
perror ("write");
exit (EXIT_FAILURE);
}
int newfd = dup2(STDOUT_FILENO, fd);
strcpy(buf, "这是使用dup2复制后的新文件描述符存入的内容, 但这些内容将被输出到控制台。\n");
if (write (newfd, buf, strlen(buf) * sizeof (buf[0])) == -1)
{
perror ("write");
exit (EXIT_FAILURE);
}
if (close (fd) == -1)
{
perror ("close");
exit (EXIT_FAILURE);
}
if (close (newfd) == -1)
{
perror ("close");
exit (EXIT_FAILURE);
}
return 0;
}
2 读写冲突
2.1 问题
如果两个或两个以上的进程同时向一个文件的某个特定区域写入数据,比如进程A要向文件中写入字符串“Hello”,同时进程B也要向文件中写入字符串“world”,当进程A刚写完“He”的时候就被暂停而运行进程B,则文件将接着写“wor”,然后进程B又被暂停而运行进程A,进程A继续写入“llo”,写完后进程A退出,进程B继续运行,写入“ld”,写完后进程B退出。这样文件中实际写入的是“Heworllold”,不是希望的“Helloworld” ,这是因为写操作的交错而产生的混乱。
如果一个进程写而其它进程同时在读一个文件的某个特定区域,比如进程A要向文件中写入字符串“Hello”,同时进程B要从文件中读出数据,当进程A刚写完“He”的时候就被暂停而运行进程B,进程B读文件,只能读出“He”,而不是希望的“Hello”。这是因为读写操作的交错而不完整。
多个进程同时读一个文件的某个特定区域,不会有任何问题,它们只是各自把文件中的数据拷贝到各自的缓冲区中,并不会改变文件的内容,相互之间也就不会冲突。
由此可以得出结论,为了避免在读写同一个文件的同一个区域时发生冲突,进程之间应该遵循以下规则:如果一个进程正在写,那么其它进程既不能写也不能读;如果一个进程正在读,那么其它进程不能写但是可以读。
3 加锁和解锁
3.1 问题
Unix系统允许多个进程同时对一个文件进行读写,内核在两个读写操作之间并没有加以同步,因此当一个进程多次调用read来读文件时,其它进程有可能在两次read之间改变该文件,造成文件数据的随机性冲突。为解决此类并发进程对共享文件的访问控制问题,Unix系统设计了文件锁技术。
3.2 步骤
实现此案例需要按照如下步骤进行。
步骤一:锁结构
代码如下所示:
- struct flock
- {
- short int l_type;
- short int l_whence;
- off_t l_start;
- off_t l_len;
- pid_t l_pid;
- };
struct flock
{
short int l_type;
short int l_whence;
off_t l_start;
off_t l_len;
pid_t l_pid;
};
上述代码中,以下代码:
- struct flock
struct flock
结构体flock用于描述锁操作的具体细节。
上述代码中,以下代码:
- short int l_type;
short int l_type;
锁操作的类型,取值为F_RDLCK时,代表加读锁;取值为F_WRLCK时,代表加写锁;取值为F_UNLCK时,代表解锁。
上述代码中,以下代码:
- short int l_whence;
short int l_whence;
锁区偏移起点,取值为SEEK_SET时,代表文件头;取值为SEEK_CUR时,代表文件当前位置;取值为SEEK_END时,代表文件尾。
上述代码中,以下代码:
- off_t l_start;
off_t l_start;
锁区从l_whence开始计算的偏移字节。
上述代码中,以下代码:
- off_t l_len;
off_t l_len;
锁区字节长度:0表示锁到文件尾。
上述代码中,以下代码:
- pid_t l_pid;
pid_t l_pid;
加锁进程标识:-1表示自动设置。
步骤二:从文件头10字节开始的20字节以阻塞模式加读锁
代码如下所示:
- #include <stdio.h>
- #include <fcntl.h>
- #include <unistd.h>
- #include <stdlib.h>
- int main()
- {
- int fd = open ("data.txt", O_RDWR | O_CREAT | O_TRUNC, 0666);
- if (fd == -1)
- {
- perror ("open");
- exit (EXIT_FAILURE);
- }
- struct flock lock;
- lock.l_type = F_RDLCK;
- lock.l_whence = SEEK_SET;
- lock.l_start = 10;
- lock.l_len = 20;
- lock.l_pid = -1;
- if (fcntl (fd, F_SETLKW, &lock) == -1)
- {
- perror ("fcntl");
- exit (EXIT_FAILURE);
- }
- if (close (fd) == -1)
- {
- perror ("close");
- exit (EXIT_FAILURE);
- }
- return 0;
- }
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
int fd = open ("data.txt", O_RDWR | O_CREAT | O_TRUNC, 0666);
if (fd == -1)
{
perror ("open");
exit (EXIT_FAILURE);
}
struct flock lock;
lock.l_type = F_RDLCK;
lock.l_whence = SEEK_SET;
lock.l_start = 10;
lock.l_len = 20;
lock.l_pid = -1;
if (fcntl (fd, F_SETLKW, &lock) == -1)
{
perror ("fcntl");
exit (EXIT_FAILURE);
}
if (close (fd) == -1)
{
perror ("close");
exit (EXIT_FAILURE);
}
return 0;
}
上述代码中,以下代码:
- lock.l_type = F_RDLCK;
lock.l_type = F_RDLCK;
定义锁操作的类型为加读锁。
上述代码中,以下代码:
- lock.l_whence = SEEK_SET;
lock.l_whence = SEEK_SET;
定义锁区偏移起点为文件头。
上述代码中,以下代码:
- lock.l_start = 10;
lock.l_start = 10;
定义锁区从文件头开始计算的偏移10个字节。
上述代码中,以下代码:
- lock.l_len = 20;
lock.l_len = 20;
定义锁区字节长度为20个字节,即只对文件中这20个字节进行区域加锁。
上述代码中,以下代码:
- lock.l_pid = -1;
lock.l_pid = -1;
定义加锁进程标识为自动设置。
上述代码中,以下代码:
- if (fcntl (fd, F_SETLKW, &lock) == -1)
- {
- perror ("fcntl");
- exit (EXIT_FAILURE);
- }
if (fcntl (fd, F_SETLKW, &lock) == -1)
{
perror ("fcntl");
exit (EXIT_FAILURE);
}
使用fcntl函数,对给定文件fd的特定区域加锁。该函数有三个参数,说明如下:
第一个参数为文件描述符,即定义要给哪一个文件加锁。
第二个参数为控制命令,可取以下值:
F_SETLKW - 阻塞模式,是指进程遇锁,将被阻塞直到锁被释放。
F_SETLK - 非阻塞模式,是指进程遇锁,立即以错误返回,并设错误码为EAGAIN。
第三个参数为锁结构体变量。
上述代码中,以下代码:
- if (close (fd) == -1)
- {
- perror ("close");
- exit (EXIT_FAILURE);
- }
if (close (fd) == -1)
{
perror ("close");
exit (EXIT_FAILURE);
}
当通过close函数关闭文件描述符时,调用进程在该文件描述符上所加一切锁将被自动解除。当进程终止时,该进程在所有文件描述符上所加的一切锁将被自动解除。
步骤三:从当前位置10字节开始到文件尾以非阻塞模式加写锁
代码如下所示:
- #include <stdio.h>
- #include <fcntl.h>
- #include <unistd.h>
- #include <stdlib.h>
- #include <errno.h>
- int main()
- {
- int fd = open ("data.txt", O_RDWR | O_CREAT | O_TRUNC, 0666);
- if (fd == -1)
- {
- perror ("open");
- exit (EXIT_FAILURE);
- }
- struct flock lock;
- lock.l_type = F_WRLCK;
- lock.l_whence = SEEK_CUR;
- lock.l_start = 10;
- lock.l_len = 0;
- lock.l_pid = -1;
- if (fcntl (fd, F_SETLK, &lock) == -1)
- {
- if (errno != EAGAIN)
- {
- perror ("fcntl");
- exit (EXIT_FAILURE);
- }
- printf ("暂时不能加锁,稍后再试...\n");
- }
- if (close (fd) == -1)
- {
- perror ("close");
- exit (EXIT_FAILURE);
- }
- return 0;
- }
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
int main()
{
int fd = open ("data.txt", O_RDWR | O_CREAT | O_TRUNC, 0666);
if (fd == -1)
{
perror ("open");
exit (EXIT_FAILURE);
}
struct flock lock;
lock.l_type = F_WRLCK;
lock.l_whence = SEEK_CUR;
lock.l_start = 10;
lock.l_len = 0;
lock.l_pid = -1;
if (fcntl (fd, F_SETLK, &lock) == -1)
{
if (errno != EAGAIN)
{
perror ("fcntl");
exit (EXIT_FAILURE);
}
printf ("暂时不能加锁,稍后再试...\n");
}
if (close (fd) == -1)
{
perror ("close");
exit (EXIT_FAILURE);
}
return 0;
}
上述代码中,以下代码:
- lock.l_type = F_WRLCK;
lock.l_type = F_WRLCK;
定义锁操作的类型为加写锁。
上述代码中,以下代码:
- lock.l_whence = SEEK_CUR;
lock.l_whence = SEEK_CUR;
定义锁区偏移起点为文件当前位置。
上述代码中,以下代码:
- lock.l_start = 10;
lock.l_start = 10;
定义锁区从文件头开始计算的偏移10个字节。
上述代码中,以下代码:
- lock.l_len = 0;
lock.l_len = 0;
定义锁区字节长度到文件结尾,即仅文件开头的10个字节不加锁。
上述代码中,以下代码:
- lock.l_pid = -1;
lock.l_pid = -1;
定义加锁进程标识为自动设置。
上述代码中,以下代码:
- if (fcntl (fd, F_SETLK, &lock) == -1)
- {
- if (errno != EAGAIN)
- {
- perror ("fcntl");
- exit (EXIT_FAILURE);
- }
- printf ("暂时不能加锁,稍后再试...\n");
- }
if (fcntl (fd, F_SETLK, &lock) == -1)
{
if (errno != EAGAIN)
{
perror ("fcntl");
exit (EXIT_FAILURE);
}
printf ("暂时不能加锁,稍后再试...\n");
}
使用fcntl函数,对给定文件fd的特定区域加锁。该函数有三个参数,说明如下:
第一个参数为文件描述符,即定义要给哪一个文件加锁。
第二个参数为控制命令,可取以下值:
F_SETLKW - 阻塞模式,是指进程遇锁,将被阻塞直到锁被释放。
F_SETLK - 非阻塞模式,是指进程遇锁,立即以错误返回,并设错误码为EAGAIN。
第三个参数为锁结构体变量。
上述代码中,以下代码:
- if (close (fd) == -1)
- {
- perror ("close");
- exit (EXIT_FAILURE);
- }
if (close (fd) == -1)
{
perror ("close");
exit (EXIT_FAILURE);
}
当通过close函数关闭文件描述符时,调用进程在该文件描述符上所加一切锁将被自动解除。当进程终止时,该进程在所有文件描述符上所加的一切锁将被自动解除。
步骤四:对整个文件解锁
代码如下所示:
- #include <stdio.h>
- #include <fcntl.h>
- #include <unistd.h>
- #include <stdlib.h>
- int main()
- {
- int fd = open ("data.txt", O_RDWR | O_CREAT | O_TRUNC, 0666);
- if (fd == -1)
- {
- perror ("open");
- exit (EXIT_FAILURE);
- }
- struct flock lock;
- lock.l_type = F_UNLCK;
- lock.l_whence = SEEK_SET;
- lock.l_start = 0;
- lock.l_len = 0;
- lock.l_pid = -1;
- if (fcntl (fd, F_SETLKW, &lock) == -1)
- {
- perror ("fcntl");
- exit (EXIT_FAILURE);
- }
- if (close (fd) == -1)
- {
- perror ("close");
- exit (EXIT_FAILURE);
- }
- return 0;
- }
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
int fd = open ("data.txt", O_RDWR | O_CREAT | O_TRUNC, 0666);
if (fd == -1)
{
perror ("open");
exit (EXIT_FAILURE);
}
struct flock lock;
lock.l_type = F_UNLCK;
lock.l_whence = SEEK_SET;
lock.l_start = 0;
lock.l_len = 0;
lock.l_pid = -1;
if (fcntl (fd, F_SETLKW, &lock) == -1)
{
perror ("fcntl");
exit (EXIT_FAILURE);
}
if (close (fd) == -1)
{
perror ("close");
exit (EXIT_FAILURE);
}
return 0;
}
上述代码中,以下代码:
- lock.l_type = F_UNLCK;
lock.l_type = F_UNLCK;
定义锁操作的类型为解锁。
上述代码中,以下代码:
- lock.l_whence = SEEK_SET;
lock.l_whence = SEEK_SET;
定义锁区偏移起点为文件头。
上述代码中,以下代码:
- lock.l_start = 0;
lock.l_start = 0;
定义锁区从文件头开始计算。
上述代码中,以下代码:
- lock.l_len = 0;
lock.l_len = 0;
定义锁区字节长度到文件结尾,即整个文件。
上述代码中,以下代码:
- lock.l_pid = -1;
lock.l_pid = -1;
定义加锁进程标识为自动设置。
上述代码中,以下代码:
- if (fcntl (fd, F_SETLKW, &lock) == -1)
- {
- perror ("fcntl");
- exit (EXIT_FAILURE);
- }
if (fcntl (fd, F_SETLKW, &lock) == -1)
{
perror ("fcntl");
exit (EXIT_FAILURE);
}
使用fcntl函数,对给定文件fd的特定区域解锁。该函数有三个参数,说明如下:
第一个参数为文件描述符,即定义要给哪一个文件解锁。
第二个参数为控制命令,可取以下值:
F_SETLKW - 阻塞模式,是指进程遇锁,将被阻塞直到锁被释放。
F_SETLK - 非阻塞模式,是指进程遇锁,立即以错误返回,并设错误码为EAGAIN。
第三个参数为锁结构体变量。
3.3 完整代码
本案例中的完整代码如下所示:
从文件头10字节开始的20字节以阻塞模式加读锁,代码如下所示:
- #include <stdio.h>
- #include <fcntl.h>
- #include <unistd.h>
- #include <stdlib.h>
- int main()
- {
- int fd = open ("data.txt", O_RDWR | O_CREAT | O_TRUNC, 0666);
- if (fd == -1)
- {
- perror ("open");
- exit (EXIT_FAILURE);
- }
- struct flock lock;
- lock.l_type = F_RDLCK;
- lock.l_whence = SEEK_SET;
- lock.l_start = 10;
- lock.l_len = 20;
- lock.l_pid = -1;
- if (fcntl (fd, F_SETLKW, &lock) == -1)
- {
- perror ("fcntl");
- exit (EXIT_FAILURE);
- }
- if (close (fd) == -1)
- {
- perror ("close");
- exit (EXIT_FAILURE);
- }
- return 0;
- }
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
int fd = open ("data.txt", O_RDWR | O_CREAT | O_TRUNC, 0666);
if (fd == -1)
{
perror ("open");
exit (EXIT_FAILURE);
}
struct flock lock;
lock.l_type = F_RDLCK;
lock.l_whence = SEEK_SET;
lock.l_start = 10;
lock.l_len = 20;
lock.l_pid = -1;
if (fcntl (fd, F_SETLKW, &lock) == -1)
{
perror ("fcntl");
exit (EXIT_FAILURE);
}
if (close (fd) == -1)
{
perror ("close");
exit (EXIT_FAILURE);
}
return 0;
}
从当前位置10字节开始到文件尾以非阻塞模式加写锁,代码如下所示:
- #include <stdio.h>
- #include <fcntl.h>
- #include <unistd.h>
- #include <stdlib.h>
- #include <errno.h>
- int main()
- {
- int fd = open ("data.txt", O_RDWR | O_CREAT | O_TRUNC, 0666);
- if (fd == -1)
- {
- perror ("open");
- exit (EXIT_FAILURE);
- }
- struct flock lock;
- lock.l_type = F_WRLCK;
- lock.l_whence = SEEK_CUR;
- lock.l_start = 10;
- lock.l_len = 0;
- lock.l_pid = -1;
- if (fcntl (fd, F_SETLK, &lock) == -1)
- {
- if (errno != EAGAIN)
- {
- perror ("fcntl");
- exit (EXIT_FAILURE);
- }
- printf ("暂时不能加锁,稍后再试...\n");
- }
- if (close (fd) == -1)
- {
- perror ("close");
- exit (EXIT_FAILURE);
- }
- return 0;
- }
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
int main()
{
int fd = open ("data.txt", O_RDWR | O_CREAT | O_TRUNC, 0666);
if (fd == -1)
{
perror ("open");
exit (EXIT_FAILURE);
}
struct flock lock;
lock.l_type = F_WRLCK;
lock.l_whence = SEEK_CUR;
lock.l_start = 10;
lock.l_len = 0;
lock.l_pid = -1;
if (fcntl (fd, F_SETLK, &lock) == -1)
{
if (errno != EAGAIN)
{
perror ("fcntl");
exit (EXIT_FAILURE);
}
printf ("暂时不能加锁,稍后再试...\n");
}
if (close (fd) == -1)
{
perror ("close");
exit (EXIT_FAILURE);
}
return 0;
}
对整个文件解锁,代码如下所示:
- #include <stdio.h>
- #include <fcntl.h>
- #include <unistd.h>
- #include <stdlib.h>
- int main()
- {
- int fd = open ("data.txt", O_RDWR | O_CREAT | O_TRUNC, 0666);
- if (fd == -1)
- {
- perror ("open");
- exit (EXIT_FAILURE);
- }
- struct flock lock;
- lock.l_type = F_UNLCK;
- lock.l_whence = SEEK_SET;
- lock.l_start = 0;
- lock.l_len = 0;
- lock.l_pid = -1;
- if (fcntl (fd, F_SETLKW, &lock) == -1)
- {
- perror ("fcntl");
- exit (EXIT_FAILURE);
- }
- if (close (fd) == -1)
- {
- perror ("close");
- exit (EXIT_FAILURE);
- }
- return 0;
- }
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
int fd = open ("data.txt", O_RDWR | O_CREAT | O_TRUNC, 0666);
if (fd == -1)
{
perror ("open");
exit (EXIT_FAILURE);
}
struct flock lock;
lock.l_type = F_UNLCK;
lock.l_whence = SEEK_SET;
lock.l_start = 0;
lock.l_len = 0;
lock.l_pid = -1;
if (fcntl (fd, F_SETLKW, &lock) == -1)
{
perror ("fcntl");
exit (EXIT_FAILURE);
}
if (close (fd) == -1)
{
perror ("close");
exit (EXIT_FAILURE);
}
return 0;
}
4 文件元数据
4.1 问题
文件的元数据是指文件的属性信息。本案例讲解如何获取文件元数据,元数据的数据结构,及文件类型和权限说明。
4.2 步骤
实现此案例需要按照如下步骤进行。
步骤一:获取文件元数据
代码如下所示:
- #include <stdio.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- #include <unistd.h>
- #include <stdlib.h>
- int main()
- {
- int fd = open ("data.txt", O_RDWR, 0666);
- if (fd == -1)
- {
- perror ("open");
- exit (EXIT_FAILURE);
- }
- struct stat statdata;
- fstat(fd, &statdata);
- if (close (fd) == -1)
- {
- perror ("close");
- exit (EXIT_FAILURE);
- }
- return 0;
- }
#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
int fd = open ("data.txt", O_RDWR, 0666);
if (fd == -1)
{
perror ("open");
exit (EXIT_FAILURE);
}
struct stat statdata;
fstat(fd, &statdata);
if (close (fd) == -1)
{
perror ("close");
exit (EXIT_FAILURE);
}
return 0;
}
上述代码中,以下代码:
- fstat(fd, &statdata);
fstat(fd, &statdata);
使用函数fstat提取文件fd的元数据。该函数有两个参数,说明如下:
第一个参数为文件描述符,即哪个文件。
第二个参数为文件元数据结构地址。
步骤二:文件元数据结构
stat结构体说明如下:
- struct stat
- {
- dev_t st_dev;
- ino_t st_ino;
- mode_t st_mode;
- nlink_t st_nlink;
- uid_t st_uid;
- gid_t st_gid;
- dev_t st_rdev;
- off_t st_size;
- blksize_t st_blksize;
- blkcnt_t st_blocks;
- time_t st_atime;
- time_t st_mtime;
- time_t st_ctime;
- };
struct stat
{
dev_t st_dev;
ino_t st_ino;
mode_t st_mode;
nlink_t st_nlink;
uid_t st_uid;
gid_t st_gid;
dev_t st_rdev;
off_t st_size;
blksize_t st_blksize;
blkcnt_t st_blocks;
time_t st_atime;
time_t st_mtime;
time_t st_ctime;
};
上述代码中,以下代码:
- dev_t st_dev;
dev_t st_dev;
保存设备的ID号。
上述代码中,以下代码:
- ino_t st_ino;
ino_t st_ino;
保存i节点的节点号。
上述代码中,以下代码:
- mode_t st_mode;
mode_t st_mode;
保存文件类型和文件权限。
上述代码中,以下代码:
- nlink_t st_nlink;
nlink_t st_nlink;
保存硬链接数。
上述代码中,以下代码:
- uid_t st_uid;
uid_t st_uid;
保存用户的ID号。
上述代码中,以下代码:
- gid_t st_gid;
gid_t st_gid;
保存组的ID号。
上述代码中,以下代码:
- dev_t st_rdev;
dev_t st_rdev;
保存特殊设备的ID号。
上述代码中,以下代码:
- off_t st_size;
off_t st_size;
保存文件的总字节数。
上述代码中,以下代码:
- blksize_t st_blksize;
blksize_t st_blksize;
保存I/O块的字节数。
上述代码中,以下代码:
- blkcnt_t st_blocks;
blkcnt_t st_blocks;
保存占用块数,每块512字节。
上述代码中,以下代码:
- time_t st_atime;
time_t st_atime;
保存文件的最后访问时间。
上述代码中,以下代码:
- time_t st_mtime;
time_t st_mtime;
保存文件的最后修改时间。
上述代码中,以下代码:
- time_t st_ctime;
time_t st_ctime;
保存文件的最后状态修改时间。
步骤三:文件类型和权限
stat结构的st_mode成员表示文件的类型和权限,该成员在stat结构中被声明为mode_t类型,其原始类型在32位系统中被定义为unsigned int,即32位无符号整数,但到目前为止,只有其中的低18位有意义。如下图所示:
图 -1
代码如下所示:
- #include <stdio.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- #include <unistd.h>
- #include <stdlib.h>
- #include <string.h>
- const char* mtos (mode_t m)
- {
- static char s[11];
- if(S_ISDIR (m))
- strcpy (s, "d");
- else if (S_ISSOCK (m))
- strcpy (s, "s");
- else if (S_ISCHR (m))
- strcpy (s, "c");
- else if (S_ISBLK (m))
- strcpy (s, "b");
- else if (S_ISLNK (m))
- strcpy (s, "l");
- else if (S_ISFIFO (m))
- strcpy (s, "p");
- else
- strcpy (s, "-");
- strcat (s, m & S_IRUSR ? "r" : "-");
- strcat (s, m & S_IWUSR ? "w" : "-");
- strcat (s, m & S_IXUSR ? "x" : "-");
- strcat (s, m & S_IRGRP ? "r" : "-");
- strcat (s, m & S_IWGRP ? "w" : "-");
- strcat (s, m & S_IXGRP ? "x" : "-");
- strcat (s, m & S_IROTH ? "r" : "-");
- strcat (s, m & S_IWOTH ? "w" : "-");
- strcat (s, m & S_IXOTH ? "x" : "-");
- if (m & S_ISUID)
- s[3] = (s[3] == 'x' ? 's' : 'S');
- if (m & S_ISGID)
- s[6] = (s[6] == 'x' ? 's' : 'S');
- if (m & S_ISVTX)
- s[9] = (s[9] == 'x' ? 't' : 'T');
- return s;
- }
- int main()
- {
- int fd = open ("data.txt", O_RDWR | O_CREAT | O_TRUNC, 0666);
- if (fd == -1)
- {
- perror ("open");
- exit (EXIT_FAILURE);
- }
- struct stat statdata;
- fstat(fd, &statdata);
- printf("%s\n", mtos(statdata.st_mode));
- if (close (fd) == -1)
- {
- perror ("close");
- exit (EXIT_FAILURE);
- }
- return 0;
- }
#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
const char* mtos (mode_t m)
{
static char s[11];
if(S_ISDIR (m))
strcpy (s, "d");
else if (S_ISSOCK (m))
strcpy (s, "s");
else if (S_ISCHR (m))
strcpy (s, "c");
else if (S_ISBLK (m))
strcpy (s, "b");
else if (S_ISLNK (m))
strcpy (s, "l");
else if (S_ISFIFO (m))
strcpy (s, "p");
else
strcpy (s, "-");
strcat (s, m & S_IRUSR ? "r" : "-");
strcat (s, m & S_IWUSR ? "w" : "-");
strcat (s, m & S_IXUSR ? "x" : "-");
strcat (s, m & S_IRGRP ? "r" : "-");
strcat (s, m & S_IWGRP ? "w" : "-");
strcat (s, m & S_IXGRP ? "x" : "-");
strcat (s, m & S_IROTH ? "r" : "-");
strcat (s, m & S_IWOTH ? "w" : "-");
strcat (s, m & S_IXOTH ? "x" : "-");
if (m & S_ISUID)
s[3] = (s[3] == 'x' ? 's' : 'S');
if (m & S_ISGID)
s[6] = (s[6] == 'x' ? 's' : 'S');
if (m & S_ISVTX)
s[9] = (s[9] == 'x' ? 't' : 'T');
return s;
}
int main()
{
int fd = open ("data.txt", O_RDWR | O_CREAT | O_TRUNC, 0666);
if (fd == -1)
{
perror ("open");
exit (EXIT_FAILURE);
}
struct stat statdata;
fstat(fd, &statdata);
printf("%s\n", mtos(statdata.st_mode));
if (close (fd) == -1)
{
perror ("close");
exit (EXIT_FAILURE);
}
return 0;
}
上述代码中,以下代码:
- if(S_ISDIR (m))
- strcpy (s, "d");
if(S_ISDIR (m))
strcpy (s, "d");
判断该文件是否是目录。
上述代码中,以下代码:
- else if (S_ISSOCK (m))
- strcpy (s, "s");
else if (S_ISSOCK (m))
strcpy (s, "s");
判断该文件是否是本地套接字。
上述代码中,以下代码:
- else if (S_ISCHR (m))
- strcpy (s, "c");
else if (S_ISCHR (m))
strcpy (s, "c");
判断该文件是否是字符设备。
上述代码中,以下代码:
- else if (S_ISBLK (m))
- strcpy (s, "b");
else if (S_ISBLK (m))
strcpy (s, "b");
判断该文件是否是块设备。
上述代码中,以下代码:
- else if (S_ISLNK (m))
- strcpy (s, "l");
else if (S_ISLNK (m))
strcpy (s, "l");
判断该文件是否是符号链接。
上述代码中,以下代码:
- else if (S_ISFIFO (m))
- strcpy (s, "p");
else if (S_ISFIFO (m))
strcpy (s, "p");
判断该文件是否是有名管道。
上述代码中,以下代码:
- else
- strcpy (s, "-");
else
strcpy (s, "-");
该文件是普通文件。
上述代码中,以下代码:
- strcat (s, m & S_IRUSR ? "r" : "-");
strcat (s, m & S_IRUSR ? "r" : "-");
该文件允许用户读。
上述代码中,以下代码:
- strcat (s, m & S_IWUSR ? "w" : "-");
strcat (s, m & S_IWUSR ? "w" : "-");
该文件允许用户写。
上述代码中,以下代码:
- strcat (s, m & S_IXUSR ? "x" : "-");
strcat (s, m & S_IXUSR ? "x" : "-");
该文件允许用户执行。
上述代码中,以下代码:
- strcat (s, m & S_IRGRP ? "r" : "-");
strcat (s, m & S_IRGRP ? "r" : "-");
该文件允许组读。
上述代码中,以下代码:
- strcat (s, m & S_IWGRP ? "w" : "-");
strcat (s, m & S_IWGRP ? "w" : "-");
该文件允许组写。
上述代码中,以下代码:
- strcat (s, m & S_IXGRP ? "x" : "-");
strcat (s, m & S_IXGRP ? "x" : "-");
该文件允许组执行。
上述代码中,以下代码:
- strcat (s, m & S_IROTH ? "r" : "-");
strcat (s, m & S_IROTH ? "r" : "-");
该文件允许其它读。
上述代码中,以下代码:
- strcat (s, m & S_IWOTH ? "w" : "-");
strcat (s, m & S_IWOTH ? "w" : "-");
该文件允许其它写。
上述代码中,以下代码:
- strcat (s, m & S_IXOTH ? "x" : "-");
strcat (s, m & S_IXOTH ? "x" : "-");
该文件允许其它执行。
上述代码中,以下代码:
- if (m & S_ISUID)
- s[3] = (s[3] == 'x' ? 's' : 'S');
if (m & S_ISUID)
s[3] = (s[3] == 'x' ? 's' : 'S');
判断是否设置用户ID位。
上述代码中,以下代码:
- if (m & S_ISGID)
- s[6] = (s[6] == 'x' ? 's' : 'S');
if (m & S_ISGID)
s[6] = (s[6] == 'x' ? 's' : 'S');
判断是否设置组ID位。
上述代码中,以下代码:
- if (m & S_ISVTX)
- s[9] = (s[9] == 'x' ? 't' : 'T');
if (m & S_ISVTX)
s[9] = (s[9] == 'x' ? 't' : 'T');
判断是否设置粘滞位。
4.3 完整代码
本案例中的完整代码如下所示:
- #include <stdio.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- #include <unistd.h>
- #include <stdlib.h>
- #include <string.h>
- const char* mtos (mode_t m)
- {
- static char s[11];
- if(S_ISDIR (m))
- strcpy (s, "d");
- else if (S_ISSOCK (m))
- strcpy (s, "s");
- else if (S_ISCHR (m))
- strcpy (s, "c");
- else if (S_ISBLK (m))
- strcpy (s, "b");
- else if (S_ISLNK (m))
- strcpy (s, "l");
- else if (S_ISFIFO (m))
- strcpy (s, "p");
- else
- strcpy (s, "-");
- strcat (s, m & S_IRUSR ? "r" : "-");
- strcat (s, m & S_IWUSR ? "w" : "-");
- strcat (s, m & S_IXUSR ? "x" : "-");
- strcat (s, m & S_IRGRP ? "r" : "-");
- strcat (s, m & S_IWGRP ? "w" : "-");
- strcat (s, m & S_IXGRP ? "x" : "-");
- strcat (s, m & S_IROTH ? "r" : "-");
- strcat (s, m & S_IWOTH ? "w" : "-");
- strcat (s, m & S_IXOTH ? "x" : "-");
- if (m & S_ISUID)
- s[3] = (s[3] == 'x' ? 's' : 'S');
- if (m & S_ISGID)
- s[6] = (s[6] == 'x' ? 's' : 'S');
- if (m & S_ISVTX)
- s[9] = (s[9] == 'x' ? 't' : 'T');
- return s;
- }
- int main()
- {
- int fd = open ("data.txt", O_RDWR | O_CREAT | O_TRUNC, 0666);
- if (fd == -1)
- {
- perror ("open");
- exit (EXIT_FAILURE);
- }
- struct stat statdata;
- fstat(fd, &statdata);
- printf("%s\n", mtos(statdata.st_mode));
- if (close (fd) == -1)
- {
- perror ("close");
- exit (EXIT_FAILURE);
- }
- return 0;
- }

浙公网安备 33010602011771号