您的位置:首页 > 其它

uc笔记05---sync/fsync/fdatasync,fcntl,文件锁,stat/fstat/lstat

2015-09-30 16:58 746 查看
1. sync/fsync/fdatasync

1)大多数磁盘 I/O 都通过缓冲进行,写入文件其实只是写入缓冲区,

直到缓冲区满,才将其排入写队列。

2)延迟写降低了写操作的次数,提高了写操作的效率,

但可能导致磁盘文件与缓冲区数据不同步。

3)sync/fsync/fdatasync 用于强制磁盘文件与缓冲区同步。

4)sync 将所有被修改过的缓冲区排入写队列即返回,不等待写磁盘操作完成。

5)fsync 只针对一个文件,且直到写磁盘操作完成才返回。

6)fdatasync 只同步文件数据,不同步文件属性。

#include <unistd.h>

void sync (void);

int fsync (int fd);

成功返回 0,失败返回 -1。

int fdatasync (int fd);

成功返回 0,失败返回 -1。

+-fwrite-> 标准库缓冲 -fflush---+ sync

应用程序内存 -+ +-> 内核缓冲 -fdatasync-> 磁盘(缓冲)

+------------write------------+ fsync

2. fcntl

#include <fcntl.h>

int fcntl (

int fd, // 文件描述符

int cmd, // 操作指令

... // 可变参数,因操作指令而异

);

对 fd 文件执行 cmd 操作,某些操作需要提供参数。

1)常用形式

#include <fcntl.h>

int fcntl (int fd, int cmd);

int fcntl (int fd, int cmd, long arg);

成功返回值因 cmd 而异,失败返回 -1。

cmd 取值:

F_DUPFD - 复制 fd 为不小于 arg 的文件描述符。

若 arg 文件描述符已用,该函数会选择比 arg 大的最小未用值,

而非如 dup2 函数那样关闭之。

F_GETFD - 获取文件描述符标志。

目前仅定义了一个文件描述符标志位 FD_CLOEXEC;

0 - 在通过 execve() 函数所创建的进程中,该文件描述符依然保持打开。

1 - 在通过 execve() 函数所创建的进程中,该文件描述符将被关闭。

F_SETFD - 设置文件描述符标志。

arg 只能取 FD_CLOEXEC (调用 exec 族函数后关闭该 fd)。

F_GETFL - 获取文件模式;不能获取 O_CREAT、O_EXCL 和 O_TRUNC。

F_SETFL - 追加文件模式;arg 只能取 O_APPEND 和 O_NONBLOCK。

范例:dup.c

#include <stdio.h>

#include <string.h>

#include <fcntl.h>

int main (void) {

int fd1 = open ("dup1.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);

if (fd1 == -1) {

perror ("open");

return -1;

}

printf ("fd1 = %d\n", fd1);

int fd2 = open ("dup2.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);

if (fd2 == -1) {

perror ("open");

return -1;

}

printf ("fd2 = %d\n", fd2);

/* fd1 复制给 fd2,若 fd2 已经打开,则关闭,返回 fd2

int fd3 = dup2 (fd1, fd2); // fd1, fd2, fd3 分别为:3,4,4(对应同一个文件表)

if (fd3 == -1) {

perror ("dup2");

return -1;

}

*/

// fd1 复制给 fd2,由于 fd2 已经打开,所以选择一个新的:

int fd3 = fcntl (fd1, F_DUPFD, fd2); // fd1, fd2, fd3 分别为:3,4,5(3 和 5 对应同一个文件表)

if (fd3 == -1) {

perror ("fcntl");

return -1;

}

printf ("fd3 = %d\n", fd3);

const char* text = "123";

if (write (fd1, text, strlen (text) * sizeof (text[0])) == -1) {

perror ("write");

return -1;

}

text = "456";

if (write (fd2, text, strlen (text) * sizeof (text[0])) == -1) {

perror ("write");

return -1;

}

text = "789";

if (write (fd3, text, strlen (text) * sizeof (text[0])) == -1) {

perror ("write");

return -1;

}

close (fd3);

close (fd2);

close (fd1);

return 0;

}

分析:

int fd3 = dup2 (fd1, fd2); fd2 复制给 fd1,如果 fd2 已经打开,则先关闭 fd2,

然后再进行复制,那么 fd2 和 fd3 的文件描述符一样;且 fd1 fd2 fd3 三个文件的内容一样;

上例中,fd1 fd2 fd3 内容都为:123456789

int fd3 = fcntl (fd1, F_DUPFD, fd2); 同样把 fd2 复制给 fd1,如果发现 fd2 打开,

则另外再找一个最小文件描述符给 fd3,所以 fd1 fd2 fd3 文件描述符分别为:3 4 5;

同时,fd1 和 fd3 的内容一样,为:123789,fd2 内容为:456;

范例:flags.c

#include <stdio.h>

#include <fcntl.h>

void pflags (int flags) {

printf ("文件模式(%08X):", flags); // %08x 十六进制

struct {

int flag;

const char* desc;

} flist[] = {

O_RDONLY, "O_RDONLY",

O_WRONLY, "O_WRONLY",

O_RDWR, "O_RDWR",

O_APPEND, "O_APPEND",

O_CREAT, "O_CREAT",

O_EXCL, "O_EXCL",

O_TRUNC, "O_TRUNC",

O_NOCTTY, "O_NOCTTY",

O_NONBLOCK, "O_NONBLOCK",

O_SYNC, "O_SYNC",

O_DSYNC, "O_DSYNC",

O_RSYNC, "O_RSYNC",

O_ASYNC, "O_ASYNC"

};

size_t i;

int first = 1;

for (i = 0; i < sizeof (flist) / sizeof (flist[0]); i++)

if (flags & flist[i].flag) {

printf ("%s%s", first ? "" : " | ", flist[i].desc); // 输出类似格式:O_WRONLY | O_ASYNC

first = 0;

}

printf ("\n");

}

int main (void) {

int fd = open ("flags.txt", O_WRONLY | O_CREAT | O_ASYNC, 0644); // 没有即创建且异步

if (fd == -1) {

perror ("open");

return -1;

}

int flags = fcntl (fd, F_GETFL);

if (flags == -1) {

perror ("fcntl");

return -1;

}

pflags (flags); // O_CREATE/O_EXCL/O_TRUNC 不能被 F_GETFL 获取

if (fcntl (fd, F_SETFL, O_RDWR | O_APPEND | O_NONBLOCK) == -1) {

perror ("fcntl");

return -1;

}

if ((flags = fcntl (fd, F_GETFL)) == -1) {

perror ("fcntl");

return -1;

}

pflags (flags); // 只有 O_APPEND/O_NONBLOCK 可以被 F_SETFL 追加

close (fd);

return 0;

}

3. 文件锁

#include <fcntl.h>

int fcntl (int fd, int cmd, struct flock* lock);

其中:

struct flock {

short int l_type; // 锁的类型:F_RDLCK/F_WRLCK/F_UNLCK (读锁/写锁/解锁)

short int l_whence; // 偏移起点:SEEK_SET/SEEK_CUR/SEEK_END (文件头/当前位置/文件尾)

off_t l_start; // 锁区偏移,从 l_whence 开始

off_t l_len; // 锁区长度,0 表示锁到文件尾

pid_t l_pid; // 加锁进程,-1 表示自动设置

};

cmd取值:

F_GETLK - 测试 lock 所表示的锁是否可加。

若可加则将 lock.l_type 置为 F_UNLCK,

否则通过 lock 返回当前锁的信息。

F_SETLK - 设置锁定状态为 lock.l_type,(非阻塞模式)

成功返回 0,失败返回 -1。

若因其它进程持有锁而导致失败,则 errno 为 EACCES 或 EAGAIN。

F_SETLKW - 设置锁定状态为 lock.l_type,(阻塞模式:等待)

成功返回 0,否则一直等待,除非被信号打断返回 -1。

1) 既可以锁定整个文件,也可以锁定特定区域。

2) 读锁 (共享锁)、写锁 (独占锁/排它锁)、解锁。

图示:rwlock.bmp、flock.bmp

3) 文件描述符被关闭 (进程结束) 时,自动解锁。

4) 劝谏锁 (协议锁)、强制锁。

举例:两个进程对同一个文件操作 nolock.c

#include <stdio.h>

#include <fcntl.h>

#include <string.h>

int main(int argc, char* argv[]) {

// 从命令行输入要打印的字符串

if(argc < 2)

goto usage;

// 字符串写入这个文件中

int fd= open("nolock.txt", O_WRONLY | O_CREAT | O_APPEND, 0644);

if(-1 == fd) {

perror("open");

return -1;

}

// 打印文件描述符

printf("fd = %d\n", fd);

// 逐个字符地写入,每写入一个休息一秒,方便我们作测试;

size_t i, len= strlen(argv[1]);

for(i=0; i< len; ++i) {

if(-1 == write(fd, &argv[1][i], sizeof(argv[1][i]))) {

perror("write");

return -1;

}

sleep(1);

}

getchar();

close(1);

return 0;

usage:

fprintf(stderr, "%s<string>\n", argv[0]);

return -1;

}

测试:打开一个进程,同时在另一个端口,再打开这个进程,例如两个端口命令如下:

$ a.out hello

$ a.out word

那么输入 nolock.txt 的不是 hello word,而是两个单词打乱顺序后的组合;

注意:首先需要明白,此程序运行后,各自的文件描述符都是 3,但是这两个 3 不是同一个文件描述符,

只是他们对应的磁盘文件是同一个(可以理解为文件描述符是进程级的,每个程序间互不影响);

本程序 open 是以追加方式打开,一个进程切换到另一个进程的时候,都是从文件尾开始写入,所以出现追加效果;

如果 open 后面不设置 O_APPEND 效果,那么会出现覆盖效果。

这里是两个独立进程,打开两个 open,也就打开了两个文件表;

如果是父子进程,则只打开一个文件表。

范例:lock1.c

#include <stdio.h>

#include <string.h>

#include <fcntl.h>

// 加读锁

int rlock (int fd, off_t start, off_t len, int wait) {

struct flock lock;

lock.l_type = F_RDLCK; // 不同点

lock.l_whence = SEEK_SET;

lock.l_start = start;

lock.l_len = len;

lock.l_pid = -1;

return fcntl (fd, wait ? F_SETLKW : F_SETLK, &lock);

}

// 加写锁

int wlock (int fd, off_t start, off_t len, int wait) {

struct flock lock;

lock.l_type = F_WRLCK; // 不同点

lock.l_whence = SEEK_SET;

lock.l_start = start;

lock.l_len = len;

lock.l_pid = -1;

return fcntl (fd, wait ? F_SETLKW : F_SETLK, &lock);

}

// 解锁

int ulock (int fd, off_t start, off_t len) {

struct flock lock;

lock.l_type = F_UNLCK; // 不同点

lock.l_whence = SEEK_SET;

lock.l_start = start;

lock.l_len = len;

lock.l_pid = -1;

return fcntl (fd, F_SETLK, &lock);

}

int main (void) {

printf ("进程标识(PID):%u\n", getpid ());

int fd = open ("lock.txt", O_RDWR | O_CREAT | O_TRUNC, 0644);

if (fd == -1) {

perror ("open");

return -1;

}

const char* text = "ABCDEFGHIJKLMNOPQR";

if (write (fd, text, strlen (text) * sizeof (text[0])) == -1) {

perror ("write");

return -1;

}

// 对 EFGH 加读锁(起始位置为 0)

printf ("对EFGH加读锁");

if (rlock (fd, 4, 4, 0) == -1) {

printf ("失败:%m\n");

return -1;

}

printf ("成功!\n");

// 对 MNOP 加写锁

printf ("对MNOP加写锁");

if (wlock (fd, 12, 4, 0) == -1) {

printf ("失败:%m\n");

return -1;

}

printf ("成功!\n");

printf ("按<回车>,解锁MN...");

getchar (); // 用于等待,等待我们开另一个进程;

// 解锁 MN

ulock (fd, 12, 2);

printf ("按<回车>,解锁EFGH...");

getchar ();

// 解锁 EFGH

ulock (fd, 4, 4);

close (fd);

return 0;

}

范例:lock2.c

#include <stdio.h>

#include <fcntl.h>

// 打印锁信息

void plock (struct flock lock) {

if (lock.l_type == F_UNLCK)

printf ("没有锁。\n");

else {

printf ("%d进程", lock.l_pid);

switch (lock.l_whence) {

case SEEK_SET:

printf ("在距文件头");

break;

case SEEK_CUR:

printf ("在距当前位置");

break;

case SEEK_END:

printf ("在距文件尾");

break;

}

printf ("%ld字节处,为%ld字节加了", lock.l_start, lock.l_len);

switch (lock.l_type) {

case F_RDLCK:

printf ("读锁。\n");

break;

case F_WRLCK:

printf ("写锁。\n");

break;

}

}

}

// 读锁测试

int rtest (int fd, off_t start, off_t len) {

struct flock lock;

lock.l_type = F_RDLCK;

lock.l_whence = SEEK_SET;

lock.l_start = start;

lock.l_len = len;

lock.l_pid = -1;

if (fcntl (fd, F_GETLK, &lock) == -1)

return -1;

plock (lock);

return 0;

}

// 写锁测试

int wtest (int fd, off_t start, off_t len) {

struct flock lock;

lock.l_type = F_WRLCK;

lock.l_whence = SEEK_SET;

lock.l_start = start;

lock.l_len = len;

lock.l_pid = -1;

if (fcntl (fd, F_GETLK, &lock) == -1)

return -1;

plock (lock);

return 0;

}

// 加读锁

int rlock (int fd, off_t start, off_t len, int wait) {

struct flock lock;

lock.l_type = F_RDLCK;

lock.l_whence = SEEK_SET;

lock.l_start = start;

lock.l_len = len;

lock.l_pid = -1;

return fcntl (fd, wait ? F_SETLKW : F_SETLK, &lock);

}

// 加写锁

int wlock (int fd, off_t start, off_t len, int wait) {

struct flock lock;

lock.l_type = F_WRLCK;

lock.l_whence = SEEK_SET;

lock.l_start = start;

lock.l_len = len;

lock.l_pid = -1;

return fcntl (fd, wait ? F_SETLKW : F_SETLK, &lock);

}

// 解锁

int ulock (int fd, off_t start, off_t len) {

struct flock lock;

lock.l_type = F_UNLCK;

lock.l_whence = SEEK_SET;

lock.l_start = start;

lock.l_len = len;

lock.l_pid = -1;

return fcntl (fd, F_SETLK, &lock);

}

int main (void) {

int fd = open ("lock.txt", O_RDWR);

if (fd == -1) {

perror ("open");

return -1;

}

// 对 CDEF 做读锁测试

printf ("对 CDEF 做读锁测试。");

if (rtest (fd, 2, 4) == -1) {

printf ("失败:%m\n");

return -1;

}

// 对 CDEF 加读锁(结果成功)

printf ("对 CDEF 加读锁");

if (rlock (fd, 2, 4, 0) == -1)

printf ("失败:%m\n");

else {

printf ("成功!\n");

ulock (fd, 2, 4);

}

// 对 CDEF 做写锁测试

printf ("对 CDEF 做写锁测试。");

if (wtest (fd, 2, 4) == -1) {

printf ("失败:%m\n");

return -1;

}

// 对 CDEF 加写锁(结果失败)

printf ("对 CDEF 加写锁");

if (wlock (fd, 2, 4, 0) == -1)

printf ("失败:%m\n");

else {

printf ("成功!\n");

ulock (fd, 2, 4);

}

// 对 KLMN 做读锁测试

printf ("对 KLMN 做读锁测试。");

if (rtest (fd, 10, 4) == -1) {

printf ("失败:%m\n");

return -1;

}

// 对 KLMN 加读锁(结果失败)

printf ("对 KLMN 加读锁");

if (rlock (fd, 10, 4, 0) == -1)

printf ("失败:%m\n");

else {

printf ("成功!\n");

ulock (fd, 10, 4);

}

// 对 KLMN 做写锁测试

printf ("对 KLMN 做写锁测试。");

if (wtest (fd, 10, 4) == -1) {

printf ("失败:%m\n");

return -1;

}

// 对 KLMN 加写锁(结果失败)

printf ("对 KLMN 加写锁");

if (wlock (fd, 10, 4, 0) == -1)

printf ("失败:%m\n");

else {

printf ("成功!\n");

ulock (fd, 10, 4);

}

printf ("等待 KLMN 上的写锁被解除 . . . \n");

// 用等待模式对 KLMN 加写锁(结果成功)

printf ("对 KLMN 加写锁");

if (wlock (fd, 10, 4, 1) == -1)

printf ("失败:%m\n");

else {

printf ("成功!\n");

ulock (fd, 10, 4);

}

close (fd);

return 0;

}

5) 文件锁仅在不同进程间起作用,位于 V 节点表里(内存里)。

6) 通过锁同步多个进程对同一个文件的读写访问。

范例:wlock.c

#include <stdio.h>

#include <string.h>

#include <fcntl.h>

#include <errno.h>

// 写锁测试

// 返回值:1 - 可加写锁,0 - 不可加写锁,-1 - 系统错误

int wtest (int fd, off_t start, off_t len) {

struct flock lock;

lock.l_type = F_WRLCK;

lock.l_whence = SEEK_SET;

lock.l_start = start;

lock.l_len = len;

lock.l_pid = -1;

if (fcntl (fd, F_GETLK, &lock) == -1)

return -1;

if (lock.l_type == F_UNLCK)

return 1;

return 0;

}

// 加写锁

int wlock (int fd, off_t start, off_t len, int wait) {

struct flock lock;

lock.l_type = F_WRLCK;

lock.l_whence = SEEK_SET;

lock.l_start = start;

lock.l_len = len;

lock.l_pid = -1;

return fcntl (fd, wait ? F_SETLKW : F_SETLK, &lock);

}

// 解锁

int ulock (int fd, off_t start, off_t len) {

struct flock lock;

lock.l_type = F_UNLCK;

lock.l_whence = SEEK_SET;

lock.l_start = start;

lock.l_len = len;

lock.l_pid = -1;

return fcntl (fd, F_SETLK, &lock);

}

// 对于主函数,应该先测试,后锁定

int main (int argc, char* argv[]) {

if (argc < 2)

goto usage;

int lock = 0;

if (argc > 2)

if (! strcmp (argv[2], "-l"))

lock = 1;

else

goto usage;

int fd = open ("wlock.txt", O_WRONLY | O_CREAT | O_APPEND, 0644);

if (fd == -1) {

perror ("open");

return -1;

}

if (lock) {

/* 基于锁测试的非阻塞模式

int unlock = 0;

do {

if ((unlock = wtest (fd, 0, 0)) == -1) { // 系统错误

perror ("wtest");

return -1;

}

if (! unlock) { // 返回 0,则无法加锁

printf ("该文件已被锁定,稍后再试 . . . \n");

// 空闲处理 . . .

}

} while (! unlock);

if (wlock (fd, 0, 0, 0) == -1) {

perror ("wlock");

return -1;

}

*/

/* 基于锁失败的非阻塞模式

while (wlock (fd, 0, 0, 0) == -1) {

if (errno != EACCES && errno != EAGAIN) {

perror ("wlock");

return -1;

}

printf ("该文件已被锁定,稍后再试 . . . \n");

// 空闲处理 . . .

}

*/

// 阻塞模式(无空闲处理机制)

if (wlock (fd, 0, 0, 1) == -1) {

perror ("wlock");

return -1;

}

}

size_t i, len = strlen (argv[1]);

for (i = 0; i < len; i++) {

if (write (fd, &argv[1][i], sizeof (argv[1][i])) == -1) {

perror ("write");

return -1;

}

sleep (1);

}

if (lock)

if (ulock (fd, 0, 0) == -1) {

perror ("ulock");

return -1;

}

close (fd);

return 0;

usage:

fprintf (stderr, "用法:%s <字符串> [-l]\n", argv[0]);

return -1;

}

测试结果如下:

# wlock 达内科技 | # wlock 有限公司

wlock.txt

<乱码>

# wlock 达内科技 -l | # wlock 有限公司 -l

wlock.txt

达内科技有限公司

范例:rlock.c

#include <stdio.h>

#include <unistd.h>

#include <fcntl.h>

#include <errno.h>

// 读锁测试

// 返回值:1 - 可加读锁,0 - 不可加读锁,-1 - 系统错误

int rtest (int fd, off_t start, off_t len) {

struct flock lock;

lock.l_type = F_RDLCK;

lock.l_whence = SEEK_SET;

lock.l_start = start;

lock.l_len = len;

lock.l_pid = -1;

if (fcntl (fd, F_GETLK, &lock) == -1)

return -1;

if (lock.l_type == F_UNLCK)

return 1;

return 0;

}

// 加读锁

int rlock (int fd, off_t start, off_t len, int wait) {

struct flock lock;

lock.l_type = F_RDLCK;

lock.l_whence = SEEK_SET;

lock.l_start = start;

lock.l_len = len;

lock.l_pid = -1;

return fcntl (fd, wait ? F_SETLKW : F_SETLK, &lock);

}

// 解锁

int ulock (int fd, off_t start, off_t len) {

struct flock lock;

lock.l_type = F_UNLCK;

lock.l_whence = SEEK_SET;

lock.l_start = start;

lock.l_len = len;

lock.l_pid = -1;

return fcntl (fd, F_SETLK, &lock);

}

int main (int argc, char* argv[]) {

int lock = 0;

if (argc > 1)

if (! strcmp (argv[1], "-l"))

lock = 1;

else

goto usage;

int fd = open ("wlock.txt", O_RDONLY);

if (fd == -1) {

perror ("open");

return -1;

}

if (lock) {

/* 基于锁测试的非阻塞模式

int unlock = 0;

do {

if ((unlock = rtest (fd, 0, 0)) == -1) {

perror ("rtest");

return -1;

}

if (! unlock) {

printf ("该文件已被锁定,稍后再试 . . . \n");

// 空闲处理 . . .

}

} while (! unlock);

if (rlock (fd, 0, 0, 0) == -1) {

perror ("rlock");

return -1;

}

*/

/* 基于锁失败的非阻塞模式

while (rlock (fd, 0, 0, 0) == -1) {

if (errno != EACCES && errno != EAGAIN) {

perror ("rlock");

return -1;

}

printf ("该文件已被锁定,稍后再试 . . . \n");

// 空闲处理 . . .

}

*/

// 阻塞模式

if (rlock (fd, 0, 0, 1) == -1) {

perror ("rlock");

return -1;

}

}

char buf[1024];

ssize_t readed;

while ((readed = read (fd, buf, sizeof (buf))) > 0)

write (STDOUT_FILENO, buf, readed);

printf ("\n");

if (readed == -1) {

perror ("read");

return -1;

}

if (lock)

if (ulock (fd, 0, 0) == -1) {

perror ("ulock");

return -1;

}

close (fd);

return 0;

usage:

fprintf (stderr, "用法:%s [-l]\n", argv[0]);

return -1;

}

测试结果如下:

# wlock 达内科技有限公司 | # rlock

<乱码>

# wlock 达内科技有限公司 -l | # rlock -l

达内科技有限公司

4. stat/fstat/lstat 获取文件属性

#include <sys/stat.h>

int stat (

const char* path, // 文件路径

struct stat* buf // 文件属性

);

int fstat (

int fd, // 文件描述符

struct stat* buf // 文件属性

);

int lstat (

const char* path, // 文件路径

struct stat* buf // 文件属性

);

成功返回 0,失败返回 -1。

stat 函数跟踪软链接,lstat 函数不跟踪软链接。

struct stat {

dev_t st_dev; // 设备 ID

ino_t st_ino; // i 节点号

mode_t st_mode; // 文件类型和权限

nlink_t st_nlink; // 硬链接数

uid_t st_uid; // 属主 ID

gid_t st_gid; // 属组 ID

dev_t st_rdev; // 特殊设备 ID

off_t st_size; // 总字节数

blksize_t st_blksize; // I/O 块字节数

blkcnt_t st_blocks; // 占用块 (512字节) 数

time_t st_atime; // 最后访问时间

time_t st_mtime; // 最后修改时间

time_t st_ctime; // 最后状态改变时间

};

st_mode(0TTSUGO) 为以下值的位或:(6 位八进制,等于 18 位二进制)

S_IFDIR - 目录 \

S_IFREG - 普通文件 |

S_IFLNK - 软链接 |

S_IFBLK - 块设备 > TT (S_IFMT)

S_IFCHR - 字符设备 |

S_IFSOCK - Unix域套接字 |

S_IFIFO - 有名管道 /

--------------------------------

S_ISUID - 设置用户ID \

S_ISGID - 设置组ID > S

S_ISVTX - 粘滞 /

--------------------------------

S_IRUSR(S_IREAD) - 属主可读 \

S_IWUSR(S_IWRITE) - 属主可写 > U (S_IRWXU)

S_IXUSR(S_IEXEC) - 属主可执行 /

--------------------------------

S_IRGRP - 属组可读 \

S_IWGRP - 属组可写 > G (S_IRWXG)

S_IXGRP - 属组可执行 /

--------------------------------

S_IROTH - 其它可读 \

S_IWOTH - 其它可写 > O (S_IRWXO)

S_IXOTH - 其它可执行 /

1)有关 S_ISUID/S_ISGID/S_ISVTX 的说明

A. 具有 S_ISUID/S_ISGID 位的可执行文件,其有效用户ID/有效组 ID,

并不取自由其父进程 (比如登录 shell) 所决定的,实际用户ID/实际组ID,

而是取自该可执行文件的属主ID/属组ID;如:/usr/bin/passwd

B. 具有 S_ISUID 位的目录,其中的文件或目录除 root 外,只有其属主可以删除。

C. 具有 S_ISGID 位的目录,在该目录下所创建的文件,继承该目录的属组 ID。

D.具有 S_ISVTX 位的可执行文件,在其首次执行并结束后,

其代码区将被连续地保存在磁盘交换区中,而一般磁盘文件中的数据块是离散存放的。

因此,下次执行该程序可以获得较快的载入速度。

现代 Unix 系统大都采用快速文件系统,已不再需要这种技术。

E.具有 S_ISVTX 位的目录,只有对该目录具有写权限的用户,

在满足下列条件之一的情况下,才能删除或更名该目录下的文件或目录:

a. 拥有此文件;

b. 拥有此目录;

c. 是超级用户。

如:/tmp

任何用户都可在该目录下创建文件,任何用户对该目录都享有读/写/执行权限,

但除 root 以外的任何用户在目录下,都只能删除或更名属于自己的文件。

2)常用以下宏辅助分析 st_mode:

S_ISDIR() - 是否目录

S_ISREG() - 是否普通文件

S_ISLNK() - 是否软链接

S_ISBLK() - 是否块设备

S_ISCHR() - 是否字符设备

S_ISSOCK() - 是否Unix域套接字

S_ISFIFO() - 是否有名管道

范例:stat.c(实现类似于 ls -l 功能)

#include <stdio.h>

#include <string.h>

#include <sys/stat.h>

#include <time.h>

// 格式化输出文件类型属性权限

const char* mtos (mode_t m) {

static char s[11];

// 通过预定义宏判断文件类型

if (S_ISDIR (m))

strcpy (s, "d"); // 目录

else if (S_ISLNK (m))

strcpy (s, "l"); // 软连接

else if (S_ISBLK (m))

strcpy (s, "b"); // 块设备文件

else if (S_ISCHR (m))

strcpy (s, "c"); // 字符设备文件

else if (S_ISSOCK (m))

strcpy (s, "s"); // 套接字文件

else if (S_ISFIFO (m))

strcpy (s, "p"); // 有名管道文件

else

strcpy (s, "-"); // 普通文件

// 属主权限 U

strcat (s, m & S_IRUSR ? "r" : "-"); // 可读

strcat (s, m & S_IWUSR ? "w" : "-"); // 可写

strcat (s, m & S_IXUSR ? "x" : "-"); // 可执行

// 属组权限 G

strcat (s, m & S_IRGRP ? "r" : "-");

strcat (s, m & S_IWGRP ? "w" : "-");

strcat (s, m & S_IXGRP ? "x" : "-");

// 其他用户权限 O

strcat (s, m & S_IROTH ? "r" : "-");

strcat (s, m & S_IWOTH ? "w" : "-");

strcat (s, m & S_IXOTH ? "x" : "-");

// S

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;

}

// 格式化输出时间

const char* ttos (time_t t) {

static char s[20];

struct tm* lt = localtime (&t);

sprintf (s, "%04d-%02d-%02d %02d:%02d:%02d",

lt->tm_year + 1900, lt->tm_mon + 1, // 年从 1900 开始,月从 0 开始,其他正常开始;

lt->tm_mday, lt->tm_hour, lt->tm_min, lt->tm_sec);

return s;

}

int main (int argc, char* argv[]) {

if (argc < 2)

goto usage; // 打印出错信息

struct stat st;

if (argc < 3) { // 跟踪软连接

if (stat (argv[1], &st) == -1) {

perror ("stat");

return -1;

}

}

else if (! strcmp (argv[2], "-l")) { // 不跟踪软连接

if (lstat (argv[1], &st) == -1) {

perror ("lstat");

return -1;

}

}

else

goto usage;

printf (" 设备ID:%u\n", st.st_dev);

printf (" i节点号:%u\n", st.st_ino);

printf (" 模式:%s\n", mtos (st.st_mode));

// printf (" 模式:%#o\n", st.st_mode);

printf (" 硬链接数:%u\n", st.st_nlink);

printf (" 属主ID:%u\n", st.st_uid);

printf (" 属组ID:%u\n", st.st_gid);

printf (" 特殊设备ID:%u\n", st.st_rdev);

printf (" 总字节数:%u\n", st.st_size);

printf (" I/O块字节数:%u\n", st.st_blksize);

printf ("占用块(512字节)数:%u\n", st.st_blocks);

printf (" 最后访问时间:%s\n", ttos (st.st_atime));

printf (" 最后修改时间:%s\n", ttos (st.st_mtime));

printf ("最后状态改变时间:%s\n", ttos (st.st_ctime));

return 0;

usage:

fprintf (stderr, "用法:%s <文件> [-l]\n", argv[0]);

return -1;

}

5. access

#include <unistd.h>

int access (

const char* pathname, // 文件路径

int mode // 访问模式

);

1)成功返回 0,失败返回 -1。

2)按实际用户 ID 和实际组 ID(而非有效用户 ID 和有效组 ID),进行访问模式测试。

3)mode 取 R_OK/W_OK/X_OK 的位或,测试执行调用进程的用户对该文件,

是否可读/可写/可执行,或者取 F_OK,测试该文件是否存在。

范例:access.c

#include <stdio.h>

#include <unistd.h>

int main (int argc, char* argv[]) {

if (argc < 2) {

fprintf (stderr, "用法:%s <文件>\n", argv[0]);

return -1;

}

printf ("文件%s", argv[1]);

if (access (argv[1], F_OK) == -1)

printf ("不存在(%m)。\n");

else {

if (access (argv[1], R_OK) == -1)

printf ("不可读(%m),");

else

printf ("可读,");

if (access (argv[1], W_OK) == -1)

printf ("不可写(%m),");

else

printf ("可写,");

if (access (argv[1], X_OK) == -1)

printf ("不可执行(%m)。\n");

else

printf ("可执行。\n");

}

return 0;

}

6. umask 查看/修改当前 shell 的文件权限屏蔽字:

查看文件权限屏蔽字:

# umask

0022

更改文件权限屏蔽字:

# umask 0033

# umask

0033

#include <sys/stat.h>

mode_t umask (

mode_t cmask // 屏蔽字

);

1)为进程设置文件权限屏蔽字,并返回以前的值,此函数永远成功。

2)cmask 由 9 个权限宏位或组成 (直接写八进制整数形式亦可,

如 022 - 屏蔽属组和其它用户的写权限):

S_IRUSR(S_IREAD) - 属主可读

S_IWUSR(S_IWRITE) - 属主可写

S_IXUSR(S_IEXEC) - 属主可执行

------------------------------

S_IRGRP - 属组可读

S_IWGRP - 属组可写

S_IXGRP - 属组可执行

------------------------------

S_IROTH - 其它可读

S_IWOTH - 其它可写

S_IXOTH - 其它可执行

3)设上屏蔽字以后,此进程所创建的文件,

都不会有屏蔽字所包含的权限。

范例:umask.c

#include <stdio.h>

#include <fcntl.h>

#include <sys/stat.h>

int main (void) {

// mode_t old = umask (0333); 返回以前的值

mode_t old = umask (

S_IWUSR | S_IXUSR |

S_IWGRP | S_IXGRP |

S_IWOTH | S_IXOTH);

int fd = open ("umask.txt", O_RDWR | O_CREAT | O_TRUNC, 0777);

if (fd == -1) {

perror ("open");

return -1;

}

close (fd);

// umask (old); 恢复为原来值

return 0;

}

执行后,umask.txt 的执行权限变为:-r--r--r--

可以通过 umask(old); 恢复默认权限;
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: