php unlink加入chomod_第四章-文件和目录

引言

本章内容覆盖

文件的所有属性

修改这些属性的各个函数

unix文件系统的结构以及符号链接

对目录进行操作的各个函数

函数stat、fstat、fstatat和lstat

#include

#include

#include

int stat(const char *pathname, struct stat *buf); //返回命名文件有关的信息结构

int fstat(int fd, struct stat *buf);//返回fd上打开文件的有关信息

int lstat(const char *pathname, struct stat *buf);//该符号链接的有关信息

#include /* Definition of AT_* constants */

#include

int fstatat(int dirfd, const char *pathname, struct stat *buf,

int flags);//相对于当前打开目录的路径名返回文件统计信息,dirfd

文件类型

普通文件,unix内核不区分文件内容是文本还是二进制,交给处理该文件的应用程序去解释

目录文件,只有内核可以直接写,它包含了其他文件名及指向与这些文件有关信息的指针

块设备文件,对设备(如磁盘)带缓冲的访问

字符特殊文件,对设备不带缓冲的访问

FIFO,命名管道,用于进程间通信

套接字(socket),用于进程间的网络通信

符号链接(symbolic link),指向另一个文件

以下示例程序实现“针对每一个命令行参数打印其文件类型”#include "apue.h"

int

main(int argc, char *argv[])

{

int i;

struct stat buf;

char *ptr;

for (i = 1; i < argc; i++) {

printf("%s: ", argv[i]);

if (lstat(argv[i], &buf) < 0) {

err_ret("lstat error");

continue;

}

if (S_ISREG(buf.st_mode))

ptr = "regular";

else if (S_ISDIR(buf.st_mode))

ptr = "directory";

else if (S_ISCHR(buf.st_mode))

ptr = "character special";

else if (S_ISBLK(buf.st_mode))

ptr = "block special";

else if (S_ISFIFO(buf.st_mode))

ptr = "fifo";

else if (S_ISLNK(buf.st_mode))

ptr = "symbolic link";

else if (S_ISSOCK(buf.st_mode))

ptr = "socket";

else

ptr = "** unknown mode **";

printf("%s\n", ptr);

}

exit(0);

}

设置用户ID和设置组ID

与每个进程相关联的用户ID和组ID

通常有效用户ID就是实际用户ID,有效组ID就是实际组ID,st_mode

中设置特殊标志,使进程有效用户ID用文件所有者用户ID(st_uid),有效组ID用文件的组所有者ID(st_gid)

文件访问权限

st_mode包含了对文件的访问权限位,对于所有文件类型,总共9个访问权限位:

chomod命令修改这9个访问权限位,

目录执行权限使能打开该目录下的文件

目录读权限使能获得该目录下的所有文件名的列表

文件读权限使能打开和读该文件

文件写权限使能打开和写该文件

文件所在目录的写权限和执行权限使能删除该文件

进程打开、创建或删除一个文件操作会触发内核进行文件访问权限测试(所有者ID sh是文件属性,有限ID和附属组ID是进程属性)

下图是内核的大致测试流程

J1:进程的有效用户ID是0(超级用户)?

J2:进程的有效用户ID等于文件的所有者ID(进程拥有此文件)?

J3:所有者适当的访问权限位被设置?

J4:进程的有效组ID或进程的附属组ID之一等于文件的组ID(进程属于某个组)?

J5:组适当的访问权限位被设置?

J6:其他用户适当的访问权限位被设置?

函数access和facessat

#include

int access(const char *pathname, int mode);

#include /* Definition of AT_* constants */

#include

int faccessat(int dirfd, const char *pathname, int mode, int flags);

这两个函数按实际用户ID和实际组ID进行访问权限测试,看下面的示例

int

main(int argc, char *argv[])

{

if (argc != 2)

err_quit("usage: a.out ");

if (access(argv[1], R_OK) < 0)//测试读权限位

err_ret("access error for %s", argv[1]);

else

printf("read access OK\n");

if (open(argv[1], O_RDONLY) < 0)

err_ret("open error for %s", argv[1]);

else

printf("open for reading OK\n");

exit(0);

}

接着看下面该程序运行的测试:

xiangke@xiangke-virtual-machine:/home/apue.3e/filedir$ ls -l access.c

-rwxrw-rw- 1 nobody nogroup 367 4月 27 2013 access.c

xiangke@xiangke-virtual-machine:/home/apue.3e/filedir$ ./access access.c

read access OK

open for reading OK //test1:用户xiangke可以读打开的文件“access.c ”

xiangke@xiangke-virtual-machine:/home/apue.3e/filedir$ ls -l /etc/shadow

-rw-r----- 1 root shadow 1274 11月 8 2018 /etc/shadow

xiangke@xiangke-virtual-machine:/home/apue.3e/filedir$ ./access /etc/shadow

access error for /etc/shadow: Permission denied//test2:但是用户xiangke没有访问文件“/etc/shadow”权限

open error for /etc/shadow: Permission denied

xiangke@xiangke-virtual-machine:/home/apue.3e/filedir$ sudo -i

[sudo] xiangke 的密码:

root@xiangke-virtual-machine:~# cd /home/apue.3e/filedir/

root@xiangke-virtual-machine:/home/apue.3e/filedir# chown root access

root@xiangke-virtual-machine:/home/apue.3e/filedir# chmod u+s access

root@xiangke-virtual-machine:/home/apue.3e/filedir# ls -l access

-rwsr-xr-x 1 root root 13688 1月 4 16:45 access //accss文件的用户ID改为了root,已打开设置用户ID位

root@xiangke-virtual-machine:/home/apue.3e/filedir# exit

注销

xiangke@xiangke-virtual-machine:/home/apue.3e/filedir$ ./access /etc/shadow

access error for /etc/shadow: Permission denied //test4:用户xiangke能打开该文件了,因为设置了用户ID,但仍然不能正常读该文件

open for reading OK

xiangke@xiangke-virtual-machine:/home/apue.3e/filedir$

函数umask

这里说明与每个进程相关联的文件模式创建屏蔽字,文件模式创建屏蔽字为1的位,在文件mode中的相应位一定被关闭。

#include

#include

mode_t umask(mode_t mask);

看下面这个实例

#include "apue.h"

#include

#define RWRWRW (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)

int

main(void)

{

umask(0);

if (creat("foo", RWRWRW) < 0)

err_sys("creat error for foo");

umask(S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);

if (creat("bar", RWRWRW) < 0)

err_sys("creat error for bar");

exit(0);

}

运行此程序,如下

xiangke@xiangke-virtual-machine:/home/apue.3e/filedir$ umask //查看当前文件模式创建屏蔽字

0002

xiangke@xiangke-virtual-machine:/home/apue.3e/filedir$ ./umask

xiangke@xiangke-virtual-machine:/home/apue.3e/filedir$ ls -l foo bar

-rw------- 1 xiangke xiangke 0 1月 9 13:41 bar

-rw-rw-rw- 1 xiangke xiangke 0 1月 9 13:41 foo

xiangke@xiangke-virtual-machine:/home/apue.3e/filedir$

从程序运行结果来看可知umask函数禁止了所有组和其他用户的访问权限

函数chmod、fchmod和fchomodat

更改现有文件的访问权限

#include

int chmod(const char *pathname, mode_t mode);

int fchmod(int fd, mode_t mode);

#include /* Definition of AT_* constants */

#include

int fchmodat(int dirfd, const char *pathname, mode_t mode, int flags);

参数mode是下图中所示常量的按位与

以下实例修改了文件模式

#include "apue.h"

int

main(void)

{

struct stat statbuf;

/* turn on set-group-ID and turn off group-execute */

if (stat("foo", &statbuf) < 0)

err_sys("stat error for foo");

if (chmod("foo", (statbuf.st_mode & ~S_IXGRP) | S_ISGID) < 0)

err_sys("chmod error for foo");

/* set absolute mode to "rw-r--r--" */

if (chmod("bar", S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) < 0)

err_sys("chmod error for bar");

exit(0);

}

xiangke@xiangke-virtual-machine:/home/apue.3e/filedir$ ls -l bar foo

-rw------- 1 xiangke xiangke 0 1月 9 13:41 bar

-rw-rw-rw- 1 xiangke xiangke 0 1月 9 13:41 foo

xiangke@xiangke-virtual-machine:/home/apue.3e/filedir$ ./changemod

//对文件foo关闭了组执行权限,打开了设置组ID位;对文件bar,其权限设置为一个绝对值

xiangke@xiangke-virtual-machine:/home/apue.3e/filedir$ ls -l bar foo

-rw-r--r-- 1 xiangke xiangke 0 1月 9 13:41 bar

-rw-rwSrw- 1 xiangke xiangke 0 1月 9 13:41 foo

xiangke@xiangke-virtual-machine:/home/apue.3e/filedir$

函数chown、fchown、fchownat和lchown

AT_SYMLINK_NOFOLLOW 被设置意味着是操作符号链接本身而不是它所指向的文件

这些函数用于更改文件的用户ID和组ID

#include

int chown(const char *pathname, uid_t owner, gid_t group);

int fchown(int fd, uid_t owner, gid_t group);//操作fd参数指向的打开文件

int lchown(const char *pathname, uid_t owner, gid_t group);

#include /* Definition of AT_* constants */

#include

int fchownat(int dirfd, const char *pathname,

uid_t owner, gid_t group, int flags);

以下是一个更改文件所有者的实例

void test_chown()

{

int iret = 0;

struct stat buf;

if (-1 == stat(FILE_NAME, &buf))

{

perror("stat errors!");

exit(0);

}

printf("user ID group ID\n"

"%-7d %-8d\n", buf.st_uid, buf.st_gid);

iret = chown(FILE_NAME, 0, 0);

if (-1 == iret)

{

perror("chown errors!");

exit(0);

}

if (-1 == stat(FILE_NAME, &buf))

{

perror("stat errors!");

exit(0);

}

printf("after chown:\n"

"user ID group ID\n"

"%-7d %-8d\n", buf.st_uid, buf.st_gid);

}

int main(void)

{

test_chown();

}

以下是该实例的执行效果

xiangke@xiangke-virtual-machine:/home/apue.3e/fileio$ whoami && id

xiangke //查看用户ID和组ID

uid=1000(xiangke) gid=1000(xiangke) 组=1000(xiangke),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),113(lpadmin),128(sambashare)

xiangke@xiangke-virtual-machine:/home/apue.3e/fileio$

xiangke@xiangke-virtual-machine:/home/apue.3e/fileio$ ./hole

xiangke@xiangke-virtual-machine:/home/apue.3e/fileio$ ls -l file.hole

-rw-r--r-- 1 xiangke xiangke 16394 1月 9 14:40 file.hole //新建的文件file.hole 文件用户ID和组ID均为1000

xiangke@xiangke-virtual-machine:/home/apue.3e/fileio$

xiangke@xiangke-virtual-machine:/home/apue.3e/fileio$ ./itest

user ID group ID

1000 1000

chown errors!: Operation not permitted //普通用户更改文件"file.hole的所有者的操作被禁止

xiangke@xiangke-virtual-machine:/home/apue.3e/fileio$

xiangke@xiangke-virtual-machine:/home/apue.3e/fileio$ sudo -i//切换到超级用户

root@xiangke-virtual-machine:/home/apue.3e/fileio# ./itest

user ID group ID

1000 1000

after chown

user ID group ID

0 0 //超级用户成功的更改了该文件的所有者

root@xiangke-virtual-machine:/home/apue.3e/fileio#

文件长度

符号链接文件长度是在文件名中的实际字节数root@xiangke-virtual-machine:/home/apue.3e/fileio# ln -s /usr/lib/ ilib

root@xiangke-virtual-machine:/home/apue.3e/fileio# ls -l ilib

lrwxrwxrwx 1 root root 9 1月 9 15:15 ilib -> /usr/lib/ //刚好9个字节

普通文件长度可以是0

目录文件长度一般是一个数(16或512)的整数倍

文件中的空洞root@xiangke-virtual-machine:/home/apue.3e/fileio# ls -l file.hole

-rw-r--r-- 1 root root 16394 1月 9 14:40 file.hole

root@xiangke-virtual-machine:/home/apue.3e/fileio# du -s file.hole

8 file.hole //该文件所用的磁盘空间总量是8x1024=8192字节,远小于实际文件的长度,所以该文件中有空洞,

root@xiangke-virtual-machine:/home/apue.3e/fileio# wc -c file.hole

16394 file.hole

root@xiangke-virtual-machine:/home/apue.3e/fileio# cat file.hole > filehole.copy //空洞都会用0填满

root@xiangke-virtual-machine:/home/apue.3e/fileio# ls -l file.hole filehole.copy

-rw-r--r-- 1 root root 16394 1月 9 14:40 file.hole

-rw-r--r-- 1 root root 16394 1月 9 15:24 filehole.copy

root@xiangke-virtual-machine:/home/apue.3e/fileio# du -s file.hole filehole.copy

8 file.hole

20 filehole.copy //需要额外的块存储指向实际数据块的各个指针

文件截断

打开文件时使用标志O_TRUNC

以下函数将一个现有文件长度截断为length

#include

#include

int truncate(const char *path, off_t length);

int ftruncate(int fd, off_t length);

文件系统

以下讨论unxi文件系统UFS(以Berkeley快速文件系统为基础)

大小写敏感

磁盘\分区\文件系统关系

关于柱面组的i节点和数据块部分,有i节点链接计数其值是指向该i节点的目录项数, LINK_MAX指定了一个文件链接数的最大值

i节点编号和文件名存放在目录项中

i节点包含了文件有关的所有信息

一个目录项不能指向另一个文件系统中的i节点,所以ln(1)命令(对于硬链接)不能跨越文件系统

mv命令重命名文件的通用操作是构造一个指向i节点的新目录项,并删除老的目录项

函数link、linkat、unlink、unlinkat和remove

创建新目录项和增加链接计数应当是一个原子操作

创建一个指向现有文件链接的方法

#include

int link(const char *oldpath, const char *newpath);

#include /* Definition of AT_* constants */

#include

int linkat(int olddirfd, const char *oldpath,

int newdirfd, const char *newpath, int flags);

删除一个现有的目录项

#include

int unlink(const char *pathname);

#include /* Definition of AT_* constants */

#include

int unlinkat(int dirfd, const char *pathname, int flags);

解除对一个文件或目录的链接

#include

int remove(const char *pathname);//对于文件,功能同unlink函数;对于目录,功能同rmdir函数

看以下示例:

#include "apue.h"

#include

int

main(void)

{

if (open("tempfile", O_RDWR) < 0) //打开一个文件

err_sys("open error");

if (unlink("tempfile") < 0)

err_sys("unlink error");

printf("file unlinked\n");

sleep(15);

printf("done\n");

exit(0);

}

按照如下操作运行该程序,

xiangke@xiangke-virtual-machine:/home/apue.3e/filedir$ ls -l tempfile

-rw-r----- 1 xiangke xiangke 1940248 1月 9 16:23 tempfile

xiangke@xiangke-virtual-machine:/home/apue.3e/filedir$ df ./

文件系统 1K-块 已用 可用 已用% 挂载点

/dev/sda1 19478204 5504084 12961640 30% /

xiangke@xiangke-virtual-machine:/home/apue.3e/filedir$ ./unlink &

[1] 18606

xiangke@xiangke-virtual-machine:/home/apue.3e/filedir$ file unlinked

xiangke@xiangke-virtual-machine:/home/apue.3e/filedir$ ls -l tempfile

ls: 无法访问'tempfile': 没有那个文件或目录

xiangke@xiangke-virtual-machine:/home/apue.3e/filedir$ df ./

文件系统 1K-块 已用 可用 已用% 挂载点

/dev/sda1 19478204 5504084 12961640 30% /

xiangke@xiangke-virtual-machine:/home/apue.3e/filedir$ df done

./

文件系统 1K-块 已用 可用 已用% 挂载点

/dev/sda1 19478204 5502188 12963536 30% /

[1]+ 已完成 ./unlink

xiangke@xiangke-virtual-machine:/home/apue.3e/filedir$

从上述运行结果来看,解除文件链接后在该程序进程结束之前,文件tempfile已经删除了但磁盘空间并没有变化,说明文件内容还被保留,进程退出后,该文件内容才被删除,所以有更多的可用磁盘空间.

函数rename和renameat

这些函数对文件或目录进行重命名

#include

int rename(const char *oldpath, const char *newpath);

#include /* Definition of AT_* constants */

#include

int renameat(int olddirfd, const char *oldpath,

int newdirfd, const char *newpath);

int renameat2(int olddirfd, const char *oldpath,

int newdirfd, const char *newpath, unsigned int flags);

oldpath是目录,newpath不能是其子目录,newpath已存在则它必须是空目录。

不能对.和…重命名

oldpath引用符号链接,则处理的是符号链接本身

oldpath是文件或符号链接,newpath必须是一个文件而不是目录

调用进程需要堆包含oldpath和newpath的目录具有写和执行权限,对newname具有写权限。

创建和读取符号链接

符号链接是对一个文件的间接指针,不同于硬链接只能超级用户才能创建,任何用户都可创建,也没有任何文件系统限制

以下函数创建符号链接

/*1.don't care if target is existent*/

/*2.linkpath and target don't neet to be located in the same filesystem*/

#include

int symlink(const char *target, const char *linkpath);

#include /* Definition of AT_* constants */

#include

int symlinkat(const char *target, int newdirfd, const char *linkpath);

以下函数读符号链接的值

/*1.the content in buf don't terminate by NULL*/

#include

ssize_t readlink(const char *pathname, char *buf, size_t bufsiz);

#include /* Definition of AT_* constants */

#include

ssize_t readlinkat(int dirfd, const char *pathname,

char *buf, size_t bufsiz);

文件的时间

对每个文件维护3个时间字段,如下

以下列出了各种函数对访问、修改和状态更改时间的作用,特别强调状态更改时间是i节点最后一次被修改的时间。

函数futimens、utimensat和utimes

以下函数更改一个文件的访问时间和修改时间

/*1.times[0]包含访问时间,times[1]包含修改时间,时间值是日历时间*/

#include /* Definition of AT_* constants */

#include

int utimensat(int dirfd, const char *pathname,

const struct timespec times[2], int flags);

int futimens(int fd, const struct timespec times[2]);

这里涉及以下宏

flags = AT_SYMLINK_NOFOLLOW, file’s time don’t follow the syslink’s

dirfd=AT_FDCWD means that using the current work directory of the calling process

times=NULL ,current time is set

以下是一个示例#include "apue.h"

#include

int

main(int argc, char *argv[])

{

int i, fd;

struct stat statbuf;

struct timespec times[2];

for (i = 1; i < argc; i++) {

if (stat(argv[i], &statbuf) < 0) { /* fetch current times */

err_ret("%s: stat error", argv[i]);

continue;

}

if ((fd = open(argv[i], O_RDWR | O_TRUNC)) < 0) { /* truncate */

err_ret("%s: open error", argv[i]);

continue;

}

times[0] = statbuf.st_atim;

times[1] = statbuf.st_mtim;

if (futimens(fd, times) < 0) /* reset times */

err_ret("%s: futimens error", argv[i]);

close(fd);

}

exit(0);

}

open将文件长度截断为0,未改变该文件的访问时间和修改时间,按如下操作步骤执行测试

xiangke@xiangke-virtual-machine:/home/apue.3e/filedir$ ls -l changemod bar

-rw-r--r-- 1 xiangke xiangke 0 1月 9 13:41 bar

-rwxr-xr-x 1 root root 13712 1月 4 16:45 changemod

xiangke@xiangke-virtual-machine:/home/apue.3e/filedir$ ls -l changemod unlink

-rwxr-xr-x 1 root root 13712 1月 4 16:45 changemod

-rwxr-xr-x 1 root root 13736 1月 4 16:45 unlink

xiangke@xiangke-virtual-machine:/home/apue.3e/filedir$ ls -lu changemod unlink

-rwxr-xr-x 1 root root 13712 1月 9 14:48 changemod

-rwxr-xr-x 1 root root 13736 1月 9 16:15 unlink

xiangke@xiangke-virtual-machine:/home/apue.3e/filedir$ date

2020年 01月 10日 星期五 15:38:14 CST

xiangke@xiangke-virtual-machine:/home/apue.3e/filedir$ ./zap changemod unlink

changemod: open error: Permission denied

unlink: open error: Permission denied

xiangke@xiangke-virtual-machine:/home/apue.3e/filedir$ sudo ./zap changemod unlink

[sudo] xiangke 的密码:

xiangke@xiangke-virtual-machine:/home/apue.3e/filedir$ ls -lu changemod unlink

-rwxr-xr-x 1 root root 0 1月 9 14:48 changemod

-rwxr-xr-x 1 root root 0 1月 9 16:15 unlink //这里表明文件最后修改时间和访问时间未变.

xiangke@xiangke-virtual-machine:/home/apue.3e/filedir$ ls -lc changemod unlink

-rwxr-xr-x 1 root root 0 1月 10 15:38 changemod

-rwxr-xr-x 1 root root 0 1月 10 15:38 unlink //这里表明文件状态更改时间改为了程序运行时的时间

xiangke@xiangke-virtual-machine:/home/apue.3e/filedir$

函数mkdir、mkdirat和rmdir

函数mkdir、mkdirat创建目录

#include

#include

int mkdir(const char *pathname, mode_t mode);

#include /* Definition of AT_* constants */

#include

int mkdirat(int dirfd, const char *pathname, mode_t mode);

mode指定文件访问权限,对于目录至少要设置一个执行权限位

dirfd是一个打开文件,相对路径名根据此打开目录进行计算

函数rmdir删除目录

#include

int rmdir(const char *pathname);

必须是一个空目录

读目录

只有内核才能写目录,以下是与目录有关的例程

#include

#include

DIR *opendir(const char *name);//初始化结构DIR

DIR *fdopendir(int fd);

struct dirent *readdir(DIR *dirp);//返回目录中的第一个目录项

int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result);

void rewinddir(DIR *dirp)//复位目录

int closedir(DIR *dirp);//关闭目录

long telldir(DIR *dirp);

void seekdir(DIR *dirp, long loc);

DIR结构保存当前正在被读的目录的有关信息

遍历文件层次结构

以下两个函数实现遍历一个文件层次结构

/*1.dirpath规定起始路径名*/

/*2.nopenf限制遍历目录时同时打开的文件数以避免永光调用进程的文件描述符,当然,它的值应该要大于等于1*/

/*3.函数fn会自然的操作目录下的每一个条目,fpath是当前条目路径名(相对路径或绝对路径,这取决于dirpath),flags(FTW_F,FTW_D,FTW_DNR,FTW_DP,FTW_NS,FTW_SL,FTW_SLN)*/

#include

int nftw(const char *dirpath,

int (*fn) (const char *fpath, const struct stat *sb,

int typeflag, struct FTW *ftwbuf),

int nopenfd, int flags);

#include

int ftw(const char *dirpath,

int (*fn) (const char *fpath, const struct stat *sb,

int typeflag),

int nopenfd);

ftw函数调用stat使程序跟随符号链接。

nftw提供给调用函数fn的第四个参数如下struct FTW

{

int base;

int level;

};

base是在fpath中给定的路径名中的文件名(basename)的偏移量。level是fpath在目录树中相对于根节点的深度(dirpath的深度为0)

返回0:遍历完整个树

返回-1: 错误

返回非0值:让树的遍历停止

函数chdir、fchdir和getcwd

#include

//以下两个函数更改调用进程的当前工作目录,而不影响其他进程的工作目录

int chdir(const char *path);

int fchdir(int fd); //fd指定新的当前工作目录

//函数获得调用进程的当前工作目录

char *getcwd(char *buf, size_t size);

看下面这个实例

#include "apue.h"

int

main(void)

{

if (chdir("/tmp") < 0)

err_sys("chdir failed");

printf("chdir to /tmp succeeded\n");

exit(0);

}

xiangke@xiangke-virtual-machine:/home/apue.3e/filedir$ ./mycd

chdir to /tmp succeeded

xiangke@xiangke-virtual-machine:/home/apue.3e/filedir$ pwd

/home/apue.3e/filedir

xiangke@xiangke-virtual-machine:/home/apue.3e/filedir$

从该实例运行结果来看,shell的当前工作目录并没有改变。

getcwd函数常用于文件系统返回到它工作的出发点。

设备特殊文件

st_dev表示文件系统的设备号,通过使用宏major,minor分别获得主设备号(它标识设备驱动程序),次设备号(标识特定的子设备

st_rdev表示实际字符特殊设备或块特殊设备设备号。‘

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值