文件IO

文件描述符表

每启动一个进程,系统会在内核空间中为该进程创建一个 struct task_struct 结构体,用于存储和管理进程的各种信息。在这其中,有一项名为文件描述符表的数据结构。文件描述符表是内核中一个关键的数据结构,用于跟踪进程打开的文件或其他 I/O 对象。它通常是一个简单的数组,每个文件描述符是数组中的一个索引。通过文件描述符和系统提供的API,可以间接访问文件指针,从而间接访问和修改文件的各种信息。如下图所示:

上图中,每一个文件描述符都对应一个 struct file 指针,该指针指向 struct file 结构体,该结构体记录了文件的各种属性。例如文件路径、文件打开时的标志、文件读写位置、文件访问权限等。

文件的创建、打开和关闭

打开文件-open()函数

在 Linux 中,open()函数用于打开文件或者创建一个新文件。它的原型如下:

//需要包含的头文件
#include <sys/types.h>    //用于定义一些基本类型,如 mode_t
#include <sys/stat.h>     //用于定义文件权限
#include <fcntl.h>        //包含 open()函数声明和一些常量,如 O_RDONLY

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

实际上只需要包含 fcntl.h 文件即可。

参数说明:

  • pathname要打开或创建的文件的路径名。
  • flags文件打开的标志,可以是下列标志的组合:
    • O_RDONLY只读方式打开文件。
    • O_WRONLY只写方式打开文件。
    • O_RDWR读写方式打开文件。
    • O_CREAT如果文件不存在,创建一个新的文件。
    • O_APPEND在文件末尾追加数据。
    • O_TRUNC如果文件存在,将文件截断为空文件。
    • O_DIRECTORY:如果参数 pathname 不是一个目录,那么 open 将失败。
    • O_EXCL如果使用了这个标志,则使用 O_CREAT 标志来打开一个文件时,如果文件已经存在,open 将返回失败。
    • O_NOFOLLOW:要求参数 pathname 所指的文件不能是符号链接。 
    • O_NONBLOCK:打开文件后,对这个文件描述符的所有的操作都以非阻塞方式进行。
    • O_NDELAY O_NONBLOCK 完全一样。
    • O_SYNC当把数据写入到文件描述符时,强制立即输出到物理设备。
  • mode文件的权限,只有在使用 O_CREAT 标志时才需要指定。

补充:必须指定标志 O_RDONLYO_WRONLY O_RDWR 中的一个,其他参数为可选项。

返回值:

open()函数返回一个文件描述符,用于后续对文件的操作。如果打开或创建文件失败,将返回-1。

错误值处理:

可以通过全局变量 errno 获取错误值,使用 perror 函数和 strerror 函数打印错误信息。

perror 函数和 strerror 函数定义如下:

void perror(const char *s); 
char* strerror(int errnum);

例子:

#include<stdio.h>
#include<string.h>      //strerror 函数
#include<fcntl.h>       //open 函数
#include<errno.h>       //perror 函数

int main(int argc, char* argv[])
{
    int fd = open("log", O_RDWR | O_CREAT, 0764);
    if(fd == -1)
    {
        printf("open failed errno = %d\n", errno);
        perror("open");
        printf("open failed: %s\n", strerror(errno));
        return -1;
    }

    printf("open success fd = %d\n", fd);
    return 0;
}

补充:可以在 /usr/include/asm‐generic/errno.h 以及 /usr/include/asm‐generic/errno‐base.h 中去查看 errno 的定义值。

open 函数常见错误值如下:

  • EACCESS:无对应权限(以读权限打开,但当前用户无读访问权限)。
  • EEXIST使用了 O_CREAT O_EXCL 标志,但文件已经存在。
  • EISDIR参数 pathname 是一个目录,而又涉及到写操作。
  • ELOOP:符号链接多级链接或者指定了 O_NOFOLLOW 标识但 pathname 是一个符号链接。
  • EMFILE:当前程序打开的文件数已经达到最大值了。
  • ENAMETOOLONG文件名太长。
  • ENFILE打开的总文件数已经达到上限了。
  • ENOSPC文件将要被创建,但是设备存储没有空间了。
  • ENOTDIR参数 pathname 不是一个目录。 

文件描述符为什么从 3 开始:

如果文件打开成功,fd 的返回值为 3,这是因为一个进程会默认打开 3 个文件描述符,它们是标准输入、标准输出、标准错误。

STDIN_FILENO   0   标准输入文件描述符 
STDOUT_FILENO  1   标准输出文件描述符 
STDERR_FILENO  2   标准错误文件描述符

后续打开的新文件,会从 3 开始编号。

进程的最大文件个数:

那么,在 Linux 中,一个进程默认可以打开的最大文件个数是多少呢,参照下面案例:

 1 #include<stdio.h>
 2 #include<fcntl.h>
 3 
 4 int main(int argc, char** argv)
 5 {
 6     char pBuf[32] = "\0";
 7     int nCount = 0;
 8     while(1)
 9     {
10         sprintf(pBuf, "file%d", nCount++);
11         int fd = open(pBuf, O_RDONLY | O_CREAT, 0764);
12         if(fd == -1)
13         {
14             perror("open");
15             break;
16         }
17 
18         printf("open : %s\n", pBuf);
19     }
20 
21     return 0;
22 }

输出:

open : file0
...
open : file1019
open : file1020
open: Too many open files

结果显示最多只能打开 1021 个,加上进程默认开启的 3 个文件描述符,总共只能打开 1024 个。实际上,这是由系统默认设置的。我们可以通过输入如下命令查看:

ulimit -a

输出如下:

core file size          (blocks, -c) 0
...
open files              (-n) 1024
...

可以看到,open files 被设置为 1024。这个配置项是可以手动修改的,可以在终端中输入:

ulimit -n 2048

上述方法只是临时生效,如果想永久生效,可以在 /etc/security/limits.conf 文件中添加:

 *    soft nofile 2048
 *    hard nofile 2048

其中:

  • *:代表所有用户都生效。
  • soft代表软限制,如果超过该值,系统会发出警告。
  • hard代表硬限制,表示系统所能允许的最大文件数量上限, 超过这个限制时进程将无法再打开更多文件。
  • nofile表示进程能打开的最大文件个数。

补充:如果 Ubuntu 版本大于 16.04,设置可能不会生效,还需修改 /etc/systemd/user.conf /etc/systemd/system.conf 中的这两个配置项:

DefaultLimitNOFILE=2048              //user.conf

DefaultLimitNOFILE=2048:524288      //system.conf

注意:需要去掉配置文件中的“#”,并设置相应的值。

修改完成后重新执行上述代码,输出如下:

open : file0
...
open : file2043
open : file2044
open: Too many open files

O_CREAT 与文件权限:

在调用 open()函数时如果指定了 O_CREAT 标识,当文件不存在时,系统会默认为我们创建该文件。如果不提供第三个参数,则创建出来的文件权限将会出现异常,例如:

 1 #include<stdio.h>
 2 #include<fcntl.h>
 3 int main(int argc, char** argv)
 4 {
 5     int fd = open("log", O_RDONLY | O_CREAT);  //no set permission
 6     if(fd == -1)
 7     {
 8         perror("open");
 9         return -1;
10     }
11     printf("open success fd = %d\n", fd);
12     return 0;
13 }

log 文件详细信息如下:

--wsrwx--- 1 test test 0 1029 15:46 log*

发现权限异常,因此,在使用 O_CREAT 标志时,必须指定权限值。

给 open 函数指定权限,代码如下:

int fd = open("log", O_RDONLY | O_CREAT, 0777);

log 文件详细信息如下:

-rwxrwxr-x 1 test test 0 1029 15:57 log*

发现 other 组缺少 w(写权限),这是由 umask 掩码位所引起的,这里不做详细介绍。

创建并打开文件-creat()函数

在 Linux 中,creat()函数用于创建一个新文件,并设置该文件的访问权限。该函数定义如下:

#include <fcntl.h>

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

参数说明:

  • pathname要创建的文件的路径和文件名。
  • mode要为新文件设置的访问权限。可以用数字表示法,也可以用下列标识:
    • S_IRUSR文件拥有者读权限
    • S_IWUSR文件拥有者写权限
    • S_IXUSR文件拥有者执行权限
    • S_IRGRP组用户读权限
    • S_IWGRP组用户写权限
    • S_IXGRP组用户执行权限
    • S_IROTH其他用户读权限
    • S_IWOTH其他用户写权限
    • S_IXOTH其他用户执行权限

返回值:

creat()函数会创建一个新文件,并返回新文件的文件描述符。如果文件创建成功,文件描述符就是一个非负整数。如果文件创建失败,则返回-1,并设置相应的错误码 errno,表示失败的原因。

例子:

 1 #include<stdio.h>
 2 #include<fcntl.h>
 3 //#include<sys/stat.h>
 4 int main(int argc, char** argv)
 5 {
 6     int fd1 = creat("log1", 0764);
 7     int fd2 = creat("log2", S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP 
 8                   | S_IWGRP | S_IROTH);
 9             
10     if(fd1 == -1)
11     {
12         perror("creat");
13         return -1;
14     }
15 
16     if(fd2 == -1)
17     {
18         perror("creat");
19         return -1;
20     }
21 
22     printf("creat success : fd1 = %d\n", fd1);
23     printf("creat success : fd2 = %d\n", fd2);
24     return 0;
25 }

执行 ll 命令后文件信息如下:

-rwxrw-r-- 1 test test 0 1029 17:08 log1*
-rwxrw-r-- 1 test test 0 1029 17:08 log2*

复制文件描述符-dup()、dup2()函数

在一个进程中,使用2次open()函数打开同一个文件,会在内核中创建2个 struct file 结构体,两个文件描述符记录各自的结构体指针。

例如,下面代码打开同一文件两次,移动其中一个文件的读写位置,观察是否会影响另一个文件:

 1 #include<stdio.h>
 2 #include<unistd.h>
 3 #include<fcntl.h>
 4 
 5 int main()
 6 {
 7     int fd0 = open("log", O_RDONLY);
 8     if(fd0 == -1)
 9     {
10         perror("open");
11         return -1;
12     }
13 
14     int fd1 = open("log", O_RDONLY);
15     if(fd1 == -1)
16     {
17         perror("open");
18         return -1;
19     }
20 
21     lseek(fd0, 10, SEEK_CUR);
22 
23     printf("fd = %d, cur pos = %ld\n", fd0, lseek(fd0, 0, SEEK_CUR));
24     printf("fd = %d, cur pos = %ld\n", fd1, lseek(fd1, 0, SEEK_CUR));
25 }

输出:

fd = 3, cur pos = 10
fd = 4, cur pos = 0

dup()函数

dup()函数用于复制文件描述符,创建一个新的文件描述符,该文件描述符与原始的文件描述符指向相同的文件(struct file 结构体)。该函数定义如下:

#include <unistd.h>

int dup(int oldfd);

参数说明:

  • oldfd待复制的文件描述符。

返回值:

如果函数调用成功,则返回新的文件描述符,该描述符是当前可用的最小非负整数。如果函数调用失败,则返回-1,并设置相应的错误码 errno

例子:

 1 #include<stdio.h>
 2 #include<unistd.h>
 3 #include<fcntl.h>
 4 
 5 int main(int argc, char** argv)
 6 {
 7     int fd = open("log", O_RDONLY);
 8     if(fd == -1)
 9     {
10         perror("open");
11         return -1;
12     }
13 
14     int fd2 = dup(fd);
15     if(fd2 == -1)
16     {
17         perror("dup");
18         return -1;
19     }
20 
21     lseek(fd, 10, SEEK_CUR);
22 
23     printf("fd = %d, cur pos = %ld\n", fd, lseek(fd, 0, SEEK_CUR));
24     printf("fd = %d, cur pos = %ld\n", fd2, lseek(fd2, 0, SEEK_CUR));
25     close(fd);
26     close(fd2);
27 }

输出:

fd = 3, cur pos = 10
fd = 4, cur pos = 10

dup2()函数

dup2()函数同样用于复制文件描述符,与dup()函数不同的是,dup2()函数可以由我们自己指定新的文件描述符。该函数定义如下:

#include <unistd.h>

int dup2(int oldfd, int newfd); 

参数说明:

  • oldfd待复制的文件描述符。
  • newfd新的文件描述符。

返回值:

如果函数调用成功,则返回新的文件描述符。如果函数调用失败,则返回-1,并设置相应的错误码 errno

注意项:

  1. 如果newfd等于oldfd,则dup2()函数不做任何操作,直接返回newfd
  2. 如果newfd已经打开,则先关闭它,再将其复制为oldfd。(可以进行重定向)

例子,将标准输出重定向到log文件中:

 1 #include<stdio.h>
 2 #include<fcntl.h>
 3 #include<unistd.h>
 4 
 5 int main(int argc, char** argv)
 6 {
 7     int fd = open("log", O_RDWR);
 8     if(fd == -1)
 9     {
10         perror("open");
11         return -1;
12     }
13 
14     int nRet = dup2(fd, STDOUT_FILENO);    //重定向
15     if(nRet == -1)
16     {
17         perror("dup2");
18         return -1;
19     }
20 
21     puts("Hello World!");      //将内容输出到log文件中
22     close(fd);
23     return 0;
24 }

关闭文件-close()函数

close()函数用于关闭打开的文件描述符。当文件描述符不再需要时,应该显式地关闭它。关闭文件描述符会释放相关的系统资源,包括文件表项和文件锁等,并确保操作系统不再跟踪该文件描述符。该函数定义如下:

#include <unistd.h>

int close(int fd);

参数说明:

  • fd要关闭的文件描述符。

返回值:

如果关闭文件描述符成功,返回值为 0。如果关闭文件描述符失败,返回值为 -1,并设置相应的错误码 errno

常用的错误码如下:

  • EBADF无效的文件描述符。
  • EINTR关闭操作被阻塞,并且被一个信号中断。
  • EIOI/O 错误发生。

文件读写

读-read()函数

read()函数用于从已打开的文件描述符中读取数据,该函数定义如下:

#include <unistd.h>

ssize_t read(int fd, void *buf, size_t count);

参数说明:

  • fd待读取的文件描述符。
  • buf存储读取数据的缓冲区。
  • count要读取的字节数。

返回值:

返回实际读取的字节数。如果返回值为 0,表示已到达文件结尾(EOF)或没有可读取的数据。如果返回值为 -1,表示出现了错误,可以通过 errno 来获取具体的错误信息。常见的错误值如下:

  • EAGAIN使用 O_NONBLOCK 标志指定了非阻塞输入输出,但当前没有数据可读。
  • EBADFfd 不是一个合法的文件描述符。
  • EINTR在读取到数据以前被信号中断。
  • EISDIRfd 指向一个目录。

EINTR 标识:

read 函数在读取文件数据时,可能遇到被信号中断的情况。例如,假设需要读取 10 个字节的数据,会有如下几种情况:

  • 一个字节都没读到就被信号打断,read()函数返回-1,errno 被设置为 EINTR。
  • 读到 5 个字节后被信号打断,read()函数返回 5,errno 被设置为 EINTR。
  • 文件中只要 5 个字节,当前全部读完,read()函数返回 5,不发生错误,不设置 errno。

为了避免中断导致数据读取个数错误,需要对中断进行处理,确保能读到我们想要的长度。代码如下:

 1 int readn(int fd, void* pBuf, size_t n)
 2 {    
 3     size_t nLeft = n;        //剩余待读字节数
 4     char* pTmpBuf = (char*)pBuf;
 5 
 6     while (nLeft > 0)
 7     {
 8         int nRead = read(fd, pTmpBuf, nLeft);
 9         if (nRead == -1)
10         {
11             if (errno == EINTR)
12                 continue;      //中断错误,继续执行循环
13             else
14                 return -1;    //其他错误
15         }
16         else if(nRead == 0)
17             break;            //读到文件末尾 EOF
18         else
19         {
20             //读到数据
21             nLeft -= nRead;
22             pTmpBuf += nRead;
23         }
24     }
25     return n - nLeft;
26 }

read()函数是一个阻塞函数:

当没有可读数据时,调用 read() 函数会导致程序阻塞,直到有数据可读或者出错。例如,如果读取的对象是标准输入,那么,程序会阻塞中断并等待用户输入:

 1 #include<stdio.h>
 2 #include<fcntl.h>
 3 #include<unistd.h>
 4 
 5 int main(int argc, char** argv)
 6 {
 7     char pBuf[32];
 8     int nRet = read(STDIN_FILENO, pBuf, sizeof(pBuf) - 1);
 9     if(nRet == -1)
10     {
11         perror("read");
12         return -1;
13     }
14     pBuf[nRet] = '\0';
15     printf("buf : %s, bytes : %d\n", pBuf, nRet);
16     return 0;
17 }

运行上述代码后,会发现中断被阻塞等待输入数据:

dhajkhd
buf : dhajkhd
, bytes : 8

写-write()函数

在 Linux 中,write()函数用于向一个文件描述符写入指定字节数的数据,该函数定义如下:

#include <unistd.h>

ssize_t write(int fd, const void *buf, size_t count);

参数说明:

  • fd待写入的文件描述符。
  • buf数据缓冲区。
  • count待写入的字节数。

返回值:

如果成功写入数据,则返回正整数,表示写入的字节数。如果发生错误,则返回 -1,可以通过 errno 来获取具体的错误信息。常见的错误值如下:

  • EBADFfd 不是一个合法的文件描述符,或者不是以写方式打开文件。
  • EINTR在读取到数据以前被信号中断。
  • ENOSPCfd 指向的文件所在的设备无可用空间。

EINTR 标识:

和 read()函数一样,需要对中断错误进行封装,确保能正确写入指定字节的数据,代码如下:

 1 ssize_t writeN(int fd, void* pUsrBuf, size_t n)
 2 {
 3     size_t nLeft = n;          //剩余待写字节数
 4     char* pBuf = pUsrBuf;
 5 
 6     while(nLeft > 0)
 7     {
 8         int nWrite = write(fd, pBuf, nLeft);
 9         if(nWrite == -1)
10         {
11             if(errno == EINTR)
12                 continue;        //中断出错,再写一次
13             else
14                 return -1;         //其他错误,直接退出
15         }
16         else
17         {
18             //写入成功,移动指针
19             nLeft -= nWrite;
20             pBuf += nWrite;
21         }
22     }
23     
24     return n - nLeft;
25 }

案例:

 1 #include<stdio.h>
 2 #include<errno.h>
 3 #include<fcntl.h>
 4 #include<unistd.h>
 5 #include<string.h>
 6 
 7 int main(int argc, char** argv)
 8 {
 9     if(argc != 2)
10     {
11         printf("Usage: %s [string]\n", argv[0]);
12         return -1;
13     }
14 
15     char pBuf[1024] = {0};
16     sprintf(pBuf, "StdOut:%s\n", argv[1]);
17     int nRet = write(STDOUT_FILENO, pBuf, strlen(pBuf));
18 
19     sprintf(pBuf, "StdErr:%s\n", argv[1]);
20     nRet = write(STDOUT_FILENO, pBuf, strlen(pBuf));
21     return 0;
22 }

输出:

$./a.out "Hello World!"
StdOut:Hello World!
StdErr:Hello World!

文件读写位置

每一个被打开的文件,都有一个文件指针表明当前的存取位置。一般文件被新建或者打开的时候,文件指针都 位于文件头,除非在打开的时候指定了 O_APPEND 标志。文件的读写操作都是从文件指针位置开始的,每次文件的 读取和写入,文件指针都会根据读写的字节数向后移动,直到文件结尾。

在 Linux 中,可以使用 lseek()函数来移动文件指针的位置,该函数定义如下:

#include <unistd.h>

off_t lseek(int fd, off_t offset, int whence);

参数说明:

  • fd文件描述符,用于表示要在其上进行操作的文件。
  • buf要移动的字节数,可以是正数、负数或者 0。offset 如果是正数,就等于将文件指针向文件结束方向移动;如果是负数,就等于将文件指针向文件开始位置方向移动;如果是 0,则文件指针保持不变。
  • whence表示相对于何处定位偏移量。该参数可以取下列值:
    • SEEK_SET从文件开始位置开始偏移 offset 字节,offset 不能小于 0,否则将出错。
    • SEEK_CUR从文件当前位置开始偏移 offset 字节,如果 offset 是负数,则向前移动;如果 offset 是正数,则向后移动。
    • SEEK_END从文件结束位置开始偏移 offset 字节,如果 offset 是负数,则向前移动;如果 offset 是正数,则向后移动(可以用此方法产生空洞文件)。

返回值:

函数执行成功后,返回新位置距开始位置的偏移量。如果发生错误,则返回-1,可以通过 errno 来获取具体的错误信息。常见的错误值如下:

  • EBADF文件描述符无效。
  • EINVALwhence 参数值无效。
  • EOVERFLOW无法表示所求位置。例如,在 32 位系统中尝试使用过大的偏移量。

例子:

 1 #include<stdio.h>
 2 #include<fcntl.h>
 3 #include<unistd.h>
 4 
 5 int main(int argc, char** argv)
 6 {
 7     if(argc != 2)
 8     {
 9         printf("Usage : %s [filename]\n", argv[0]);
10         return -1;
11     }
12 
13     int fd = open(argv[1], O_RDONLY);
14     char pBuf[32] = {0};
15 
16     int nRead = read(fd, pBuf, 10);
17     int nPos = lseek(fd, 0, SEEK_CUR);
18     printf("pBuf : %s, readCount : %d, curPos : %d\n", pBuf, nRead, nPos);
19 
20     nPos = lseek(fd, -5, SEEK_CUR);
21     nRead = read(fd, pBuf, 10);
22     printf("pBuf : %s, readCount : %d, curPos : %d\n", pBuf, nRead, nPos);
23 
24     return 0;
25 }

输出:

$cat log
0123456789ABCDEFG
$./a.out log
pBuf : 0123456789, readCount : 10, curPos : 10
pBuf : 56789ABCDE, readCount : 10, curPos : 5

利用 lseek 获取文件大小:

通过将文件指针移动到文件末尾,并偏移 0 个位置,lseek()将返回文件指针的位置,这个指针偏移量的就是文件大小。 

int file_size = lseek(fd_lseek, 0, SEEK_END);

利用 lseek 产生空洞文件:

可以利用上述特性,产生空洞文件,在下载文件时,会先计算文件所需空间,然后先产生一个相同大小空洞文件,之后将文件的内容下载下来,填入该文件中。下面利用代码实现一个空洞文件:

 1 #include<stdio.h>
 2 #include<unistd.h>
 3 #include<fcntl.h>
 4 
 5 int main(int argc, char** argv)
 6 {
 7     int fd = open("log", O_WRONLY | O_CREAT | O_TRUNC, 0666);
 8     if(fd == -1)
 9     {
10         perror("open");
11         return -1;
12     }
13 
14     off_t offset = lseek(fd, 1024 * 1024 * 1024 - 1, SEEK_END);
15     printf("offset : %ld\n", offset);
16     write(fd, "\0", 1);
17     close(fd);
18     return 0;
19 }

在终端中输出 ls -lh log 命令:

$ ls -lh log
-rw-rw-r-- 1 test test 1.0G 10月  30 17:28 log

如果要在32位机器上产生一个大于2G的文件,需要在源文件中添加如下宏:

#ifndef _FILE_OFFSET_BITS
#define _FILE_OFFSET_BITS 64
#endif 

同步缓冲区-fsync()函数

fsync()函数用于将文件数据从内核缓冲区刷新到磁盘上的存储介质上,确保数据持久化存储。它会阻塞调用进程,直到数据成功刷新到磁盘为止。该函数定义如下:

#include <unistd.h>

int fsync(int fd);

参数说明:

  • fd需要同步到磁盘的文件描述符。

返回值:

如果刷新操作成功,返回值为 0;如果刷新操作失败,返回值为 -1,并设置相应的错误码 errno

常用的错误码如下:

  • EBADF无效的文件描述符。
  • EIOI/O 错误发生。

文件访问权限

判断文件权限-access()函数

access()函数用于检查指定的文件或目录是否存在,是否具有可读、可写、可执行权限。该函数定义如下:

#include <unistd.h>

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

参数说明:

  • path:待检查的文件或目录的路径。
  • mode:待检查的权限,可以取如下值或是它们的组合:
    • R_OK判断文件是否有读权限。
    • W_OK判断文件是否有写权限 。
    • X_OK判断文件是否有可执行权限。
    • F_OK判断文件是否存在。

返回值:

 函数的返回值为 0 表示指定的文件拥有指定权限或权限组。如果返回值为-1,表示发生错误,常见错误如下:

  • EACCES文件权限不满足mode中一项或多项。
  • ENOENT文件或目录不存在。
  • ENAMETOOLONG路径名太长。

例子:

 1 #include<stdio.h>
 2 #include<unistd.h>
 3 #include<fcntl.h>
 4 #include<errno.h>
 5 
 6 int main(int argc, char** argv)
 7 {
 8     if(argc != 2)
 9     {
10         printf("Usage : %s [filename]", argv[0]);
11         return -1;
12     }
13 
14     if(access(argv[1], F_OK | R_OK | W_OK | X_OK) == 0)
15         puts("F_OK | R_OK | W_OK | X_OK\n");
16     else if(errno == EACCES)            //权限不匹配
17         puts("EACCES\n");
18     else if(errno == ENOENT)            //给定路径错误或文件不存在
19         puts("ENOENT\n");
20     else if(errno == ENAMETOOLONG)      //文件名太长
21         puts("ENAMETOOLONG\n");
22 
23     return 0;
24 }

修改文件属性-fcntl()函数

fcntl()函数用于对打开的文件描述进行各种控制操作,它可以实现文件锁定、获取文件状态、修改文件标识等功能,该函数定义如下:

#include <fcntl.h>

int fcntl(int fd, int cmd, ...);

参数说明:

  • fd表示要操作的文件描述符。
  • cmd表示要执行的操作命令。
    • F_DUPFD复制文件描述符,返回一个新的文件描述符。
    • F_GETFD F_SETFD:获取和设置文件描述符的标志。
    • F_GETFL  F_SETFL:获取和设置文件状态标志。
    • F_GETOWN F_SETOWN获取和设置文件异步 I/O 的拥有者。
    • F_SETLK F_SETLKW对文件进行锁定或解锁。
    • F_GETLK获取文件锁的状态。
  • 参数3根据不同的命令,指定不同参数。

返回值:

 fcntl()函数的返回值取决于所执行的操作。

F_GETFL 与 F_SETFL标志:

这两个标识用于获取和设置文件状态,该状态是指调用open()函数时指定的附加状态标识。可以更改的标志有:O_APPENDO_ASYNCO_DIRECTO_NOATIME O_NONBLOCK

对于 F_GETFL 标识,如果函数执行成功,返回当前文件的状态标识。如果发生错误,返回-1,并设置相应的错误码 errno

对于 F_SETFL 标识,如果函数执行成功,返回0。如果发生错误,返回-1,并设置相应的错误码 errno

下面例子说明无法设置文件访问标识 O_RDONLYO_WRONLY O_RDWR

 1 #include<stdio.h>
 2 #include<fcntl.h>
 3 #include<unistd.h>
 4 #include<string.h>
 5 
 6 int main(int argc, char** argv)
 7 {
 8     int fd = open("log", O_RDONLY);
 9     if(fd == -1)
10     {
11         perror("open");
12         return -1;
13     }
14     char pBuf[32] = {0};
15     int nRead = read(fd, pBuf, 10);
16     if(nRead == -1)
17     {
18         perror("read");
19         return -1;
20     }
21     printf("pBuf : %s, read : %d\n", pBuf, nRead);
22     int nRet = fcntl(fd, F_SETFL, O_WRONLY);
23     printf("fcntl = %d\n", nRet);
24     if(nRet == -1)
25     {
26         perror("fcntl");
27         return -1;
28     }
29 
30     const char* pBuf2 = "C++ is best language!";
31     int nWrite = write(fd, pBuf2, strlen(pBuf2));
32     if(nWrite == -1)
33     {
34         printf("fd = %d\n", fd);
35         perror("write");
36         return -1;
37     }
38     
39     return 0;
40 }

输出:

$ ./a.out 
pBuf : HelloWorld, read : 10
fcntl = 0
fd = 3
write: Bad file descriptor

设置标准输入为非阻塞:

 1 #include<stdio.h>
 2 #include<errno.h>
 3 #include<fcntl.h>
 4 #include<unistd.h>
 5 
 6 int main(int argc, char** argv)
 7 {
 8     char pBuf[32];
 9     int nRet = fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK);
10     if(nRet == -1)
11     {
12         perror("fcntl");
13         return -1;
14     }
15 
16 AGAIN:
17     nRet = read(STDIN_FILENO, pBuf, sizeof(pBuf));
18     if(nRet == -1)
19     {
20         perror("read");
21         if(errno == EAGAIN)
22         {
23             sleep(1);
24             goto AGAIN;
25         }
26         else
27         {
28             return -1;
29         }
30     }
31 
32     pBuf[nRet] = '\0';
33     printf("pBuf : %s\n", pBuf);
34 
35     return 0;
36 }

输出:

$ ./a.out 
read: Resource temporarily unavailable
read: Resource temporarily unavailable
read: Resource temporarily unavailable
dhajkdaread: Resource temporarily unavailable

pBuf : dhajkda

增加或移除标识:

 1 int set_file_flag(int fd, int nflags, int on)
 2 {
 3     int fl = fcntl(fd, F_GETFL, 0);
 4     if(fl == -1)
 5     {
 6         perror("fcntl");
 7         return -1;
 8     }
 9 
10     if(!!on)
11         fl |= nflags;
12     else
13         fl &= ~nflags;
14 
15     if(fcntl(fd, F_SETFL, fl) == -1)
16     {
17         perror("fcntl");
18         return -1;
19     }
20     return 0;
21 }

文件属性

stat()、lstat()、fstat()函数

stat()、lstat()、fstat()这几个函数用于获取一个文件或目录的详细信息,包括文件大小、文件类型、权限、所有者、所属组、最后访问、修改和状态改变的时间等。

对于一个符号链接文件,stat()会返回它所指向的文件的属性;lstat返回的就是这个符号链接本身的属性;fstat可以通过文件描述符表获取该文件的属性,如果给定文件描述符是一个符号文件,该函数返回它所指向的文件的属性。

该函数定义如下:

#include <sys/stat.h>

int stat(const char *path, struct stat *struct_stat);
int lstat(const char *path,struct stat *struct_stat);
int fstat(int fdp, struct stat *struct_stat); 

参数说明:

  • path:表示要获取属性信息的文件路径。
  • struct_stat:表示存储属性信息的结构体。
  • fd表示要获取属性的文件描述符。

返回值:

如果函数执行成功,返回值为0;如果函数执行失败,返回值为-1,并设置相应的错误码 errno

struct stat结构体:

struct stat结构体用于表示文件和目录的元数据信息,定义如下:

struct stat {
    dev_t st_dev;         // 文件所在设备 ID
    ino_t st_ino;         // inode 号
    mode_t st_mode;       // 文件类型和访问权限
    nlink_t st_nlink;     // 硬链接数
    uid_t st_uid;         // 文件拥有者的用户 ID
    gid_t st_gid;         // 文件所属组的组 ID
    dev_t st_rdev;        // 对于特殊文件,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;      // 最后一个状态更改时间
};

其中,st_mode 有以下定义:

S_IFMT      0170000     文件类型的位遮罩
S_IFLNK     0120000     符号连接
S_IFREG     0100000     一般文件
S_IFBLK     0060000     区块装置
S_IFDIR     0040000     目录
S_IFCHR     0020000     字符装置
S_IFIFO     0010000     先进先出
S_ISUID     04000       文件的(set user‐id on execution)位
S_ISGID     02000       文件的(set group‐id on execution)位
S_ISVTX     01000       文件的sticky位(粘住位)
S_IRUSR     00400       文件所有者具可读取权限
S_IWUSR     00200       文件所有者具可写入权限
S_IXUSR     00100       文件所有者具可执行权限
S_IRGRP     00040       用户组具可读取权限
S_IWGRP     00020       用户组具可写入权限
S_IXGRP     00010       用户组具可执行权限
S_IROTH     00004       其他用户具可读取权限
S_IWOTH     00002       其他用户具可写入权限
S_IXOTH     00001       其他用户具可执行权限

在POSIX中定义了检查这些类型的宏定义:

S_ISLNK (st_mode)       判断是否为符号连接
S_ISREG (st_mode)       是否为一般文件
S_ISDIR (st_mode)       是否为目录
S_ISBLK (st_mode)       是否是块设备
S_ISCHR (st_mode)       是否为字符设备文件
S_ISSOCK (st_mode)      是否为socket

粘住位(S_ISVTX):

粘住位(Sticky Bit)是一种文件权限设置的特殊标记。它可以应用于目录,用于控制内部文件的删除操作。当目录被设置了粘住位后,只有文件的所有者、目录的所有者或超级用户才能够删除该目录中的文件。其他用户即使有文件的写权限,也无法删除不属于自己的文件。

在命令行中,可以通过 chmod 命令来设置目录的粘住位。例如:

chmod 1777 目录名        //使用数字形式设置粘住位

chmod +t 目录名          //使用符号形式设置粘住位

可以使用 ls -ld 目录名 查看目录是否具备粘住位:

$ls -ld ./

drwxrwxrwt 2 test test 4096 10月  30 17:22 ./

如果目录具有粘住位,第一列中的最后一个字符将显示t,代表该目录具备粘住位和执行权限;如果显示T,则该目录只具备粘住位。

SUID/SGID位:

SUID位:当文件被设置了SUID位后,当普通用户执行该文件时,其执行用户变为该文件的所有者,即拥有该文件的特权用户,从而使普通用户可以获得该文件的特权执行权限。SUID位通常应用于需要执行一些特权操作,但无需完全使用特权用户权限的程序,比如passwd命令。

SGID位:当目录被设置了SGID位后,该目录内新建的文件或目录的所属组会被自动设置为该目录的所属组,而非当前用户的默认组。

 SUID/SGID位的设置方法如下:

chmod u+s file          //设置SUID位

chmod g+s directory     //设置SGID位

ls -ld 目录名 查看SUID/SGID位时,通过在对于位置上用s或S表示。

改变文件属性-chmod()函数

在linux中,改变文件或目录的权限可以使用chmod()函数,该函数定义如下:

#include <sys/stat.h>

int chmod(const char *path, mode_t mode);
int fchmod(int fd, mode_t mode);

参数说明:

  • path:表示要修改权限的文件或目录的路径。
  • mode:表示要设置的权限。有如下取值或组合:
    • S_ISUID文件的SUID位。
    • S_ISGID文件的SGID位。
    • S_ISVTX文件的粘住位(sticky)位。
    • S_IRWXU文件所有者具读、写、执行权限。
    • S_IRUSR文件所有者具可读取权限。
    • S_IWUSR文件所有者具可写入权限。
    • S_IXUSR文件所有者具可执行权限。
    • S_IRWXG用户组具读、写、执行权限。
    • S_IRGRP用户组具可读取权限 。
    • S_IWGRP用户组具可写入权限 。
    • S_IXGRP用户组具可执行权限。
    • S_IRWXO其他用户具读、写、执行权限。
    • S_IROTH其他用户具可读取权限。
    • S_IWOTH其他用户具可写入权限。
    • S_IXOTH其他用户具可执行权限。
  • fd表示要修改权限的文件或目录的的文件描述符。

返回值:

如果函数执行成功,返回值为0;如果函数执行失败,返回值为-1,并设置相应的错误码 errno

例子:

 1 #include<stdio.h>
 2 #include<unistd.h>
 3 #include<sys/stat.h>
 4 
 5 int main(int argc, char** argv)
 6 {
 7     if(argc != 3)
 8     {
 9         printf("Usage: %s [mode] [filename]", argv[0]);
10         return -1;
11     }
12 
13     mode_t mode;
14     sscanf(argv[1], "%o", &mode);
15     int nchmod = chmod(argv[2], mode);
16     if(nchmod == -1)
17     {
18         perror("chmod");
19         return -1;
20     }
21 
22     return 0;
23 }

输出:

$ ./a.out 777 chmod.c 
$ ls -l chmod.c
-rwxrwxrwx 1 test test 580 10月  30 20:07 chmod.c

改变文件所属用户-chown()函数

chown()函数用于修改文件或目录的所有者(owner)和所属组(group),该函数定义如下:

#include <unistd.h>

int chown(const char *path, uid_t owner, gid_t group);
int fchown(int fd, uid_t owner, gid_t group);
int lchown(const char *path, uid_t owner, gid_t group);

参数说明:

  • path:表示要修改所有者和所属组的文件或目录的路径。
  • fd:文件描述符。
  • owner:表示要设置的所有者的用户 ID,如果参数为 -1,则不会更改所有者。
  • group要设置的所属组的组 ID,如果参数为 -1,则不会更改所属组。

返回值:

如果函数执行成功,返回值为0;如果函数执行失败,返回值为-1,并设置相应的错误码 errno

修改文件访问时间-utime()函数

在Linux中,一个文件拥有下列三种时间:

  • Access Time 访问时间:文件最近一次被读取的时间。当一个文件被打开并读取时,访问时间会被更新。
  • Modify Time 修改时间:文件最近一次被修改的时间。任何对文件内容的修改,包括写入和修改文件的操作,都会更新修改时间。
  • Change Time 变更时间:文件元数据 (metadata) 最近一次被修改的时间。元数据包括文件的权限、所有者、所属组等信息。上面两种时间的修改都会导致该时间被修改。

utime()函数用于修改一个文件的访问时间(Access Time)和修改时间(Modify Time),该函数定义如下:

#include <utime.h>

int utime(const char *filename, const struct utimbuf *times);

参数说明:

  • filename要修改时间的文件名。
  • timesstruct utimbuf结构体指针,用于指定新的访问时间和修改时间。

返回值:

如果函数执行成功,返回值为0;如果函数执行失败,返回值为-1,并设置相应的错误码 errno

struct utimbuf结构体:

struct utimbuf结构体用于指定新的访问时间和修改时间,该结构体定义如下:

struct utimbuf {
    time_t actime;  /* 新的访问时间 */
    time_t modtime; /* 新的修改时间 */
};

文件操作

截断文件-truncate()函数

truncate()函数用于修改文件的大小。它可以将指定文件截断为指定的长度,同时也可以用于扩展文件的大小。该函数定义如下:

#include <unistd.h>

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

参数说明:

  • path要操作的目标文件路径(包括文件名)。
  • fd要操作的目标文件的文件描述符。
  • length目标文件所要截断的长度。

返回值:

如果函数执行成功,返回值为0;如果函数执行失败,返回值为-1,并设置相应的错误码 errno

创建硬链接-link()函数

link()函数用于创建一个硬链接,新链接与源文件共享相同的inode和数据块,也就是说它们实际上是同一个文件的不同名称。当删除文件时,只是删除了目录下的记录项和把inode硬链接计数减1,当硬链接计数减为0时,才会真正的删除文件。

该函数定义如下:

#include <unistd.h>

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

参数说明:

  • oldpath源文件的路径名。
  • newpath要创建的硬链接的路径名。

返回值:

如果函数执行成功,返回值为0;如果函数执行失败,返回值为-1,并设置相应的错误码 errno

注意项:

  1. 硬链接通常要求位于同一文件系统中,POSIX允许跨文件系统。
  2. 符号链接没有文件系统限制。
  3. 通常不允许创建目录的硬链接,某些unix系统下超级用户可以创建目录的硬链接。
  4. 创建目录项以及增加硬链接计数应当是一个原子操作。

创建符号链接-symlink()函数

symlink()函数数用于创建一个符号链接(软链接)。符号链接是一种特殊的文件类型,它不包含源文件的实际内容,而只包含源文件的路径名。读取软链接文件时,实际上就是读取其指向的源文件。另外,删除软链接并不会影响源文件,只是删除了指向该文件的链接文件。

该函数定义如下:

#include<unistd.h>

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

参数说明:

  • oldpath源文件的路径名。
  • newpath要创建的符号链接的路径名。

返回值:

如果函数执行成功,返回值为0;如果函数执行失败,返回值为-1,并设置相应的错误码 errno

读取符号链接指向的文件-readlink()函数

readlink()函数于读取符号链接(软链接)的目标路径名。该函数定义如下:

#include<unistd.h>

int readlink(const char * path ,char * buf, size_t bufsize);

参数说明:

  • path指向符号链接(软链接)的路径名。
  • buf用于存储目标路径名的缓冲区。
  • bufsize缓冲区的大小。

返回值:

readlink()函数将符号链接的目标路径名读取到 buf 缓冲区中,并返回实际读取的字节数。如果目标路径名的长度超过了 bufsiz-1 ,则只会读取 bufsiz-1 长度的内容,并在 buf 缓冲区的最后添加一个 null 字符终止字符串。

如果函数执行成功,返回读取的字节数;如果函数执行失败,返回值为-1,并设置相应的错误码 errno

删除文件- unlink()函数

unlink()函数用于删除一个文件。该函数定义如下:

#include<unistd.h>

int unlink(const char * pathname);

参数说明:

  • pathname指向符号链接(软链接)的路径名。

返回值:

如果函数执行成功,返回值为0;如果函数执行失败,返回值为-1,并设置相应的错误码 errno

注意项:

  1. 只有对待删除文件所在的目录拥有写权限的用户才能成功删除文件。
  2. unlink()函数不适用于删除目录,只用于删除常规文件。如果要删除目录,可以使用 rmdir()函数。
  3. 当文件被成功删除后,该文件所占用的磁盘空间将被释放。但是,如果该文件正被其他进程打开或正在使用,则只有当所有的打开文件描述符关闭后,文件的磁盘空间才会被释放。
  4. 如果是符号链接,删除符号链接文件。 如果是硬链接,硬链接数减1,当减为0时,释放数据块和inode

移动、重命名文件-rename()函数

rename()函数用于重命名文件或将文件移动到另一个目录。该函数定义如下:

#include<stdio.h>

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

参数说明:

  • oldpath旧的文件路径名。
  • newpath:新的文件路径名。

返回值:

如果函数执行成功,返回值为0;如果函数执行失败,返回值为-1,并设置相应的错误码 errno

注意项:

  1. oldpathnewpath 可以指向不同的目录。如果指定的路径名在不同的目录下,rename()函数将移动文件到目标目录下并重命名。
  2. 如果newpath指向一个已存在的文件,rename()函数会直接覆盖该文件。
  3. 对于文件和目录的重命名,需要具有对目标目录的写权限。

文件夹操作

创建文件夹-mkdir()函数

mkdir()函数用于创建一个目录。该函数定义如下:

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

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

参数说明:

  • pathname要创建的目录的路径名。
  • mode设置目录的权限。与chmod()函数用法相同。

返回值:

如果函数调用成功,则返回0。如果函数调用失败,则返回-1,并设置相应的错误码 errno

案例-递归创建目录:

 1 #include<stdio.h>
 2 #include<fcntl.h>
 3 #include<unistd.h>
 4 #include<string.h>
 5 #include<errno.h>
 6 #include <sys/stat.h>
 7 
 8 int main(int argc, char** argv)
 9 {
10     if(argc != 2)
11     {
12         printf("Usage : %s [path]\n", argv[0]);
13         return -1;
14     }
15 
16     char pBuf[1024];
17     char* ptr = strtok(argv[1], "/");
18     if(ptr != NULL)
19         strcpy(pBuf, ptr);
20 
21     while(ptr)
22     {
23         int nRet = mkdir(pBuf, 0777);
24         if(nRet == -1)
25         {
26             if(errno != EEXIST)
27             {
28                 perror("mkdir");
29                 return -1;
30             }
31         }
32         
33         if(ptr = strtok(NULL, "/"))
34         {
35             strcat(pBuf, "/");
36             strcat(pBuf, ptr);
37             break;
38         }
39     }
40 
41     return 0;
42 }

删除文件夹- rmdir()函数

rmdir()函数用于删除空目录。该函数定义如下:

#include <unistd.h>

int rmdir(const char *pathname);

参数说明:

  • pathname要删除的目录的路径名。

返回值:

如果函数调用成功,则返回0。如果函数调用失败,则返回-1,并设置相应的错误码 errno

打开文件夹-opendir()函数

opendir()函数用于打开目录,并返回一个指向目录流的指针。通过这个指针,可以遍历目录中的文件和子目录。该函数定义如下:

#include <dirent.h>

DIR *opendir(const char *name);

参数说明:

  • name要打开的目录的路径名。

返回值:

若成功打开目录,则返回一个指向类型为 DIR 的目录流的指针。若发生错误,则返回 NULL并设置相应的错误码 errno

关闭文件夹-closedir()函数

closedir()函数用于关闭通过 opendir() 函数打开的目录流,并释放与之相关的资源。该函数定义如下:

#include <dirent.h>

int closedir(DIR *dirp);

参数说明:

  • dirp指向要关闭的目录流的指针。

返回值:

若成功关闭目录流,则返回0。若出现错误,则返回-1,并设置相应的错误码 errno

读取文件夹-readdir()函数

readdir()函数用于读取打开的目录流中的下一个目录项。该函数定义如下:

#include <dirent.h>

struct dirent *readdir(DIR *dirp);

参数说明:

  • dirp指向已打开的目录流的指针。

返回值:

若成功读取到下一个目录项,则返回指向 struct dirent 结构体的指针。若已经到达目录的末尾或发生错误,则返回 NULL

struct dirent结构体:

struct dirent结构体用于表示目录中的一个文件(或子目录)的信息。该结构体定义如下:

struct dirent {
    ino_t          d_ino;       // inode 号
    off_t          d_off;       // 文件偏移量
    unsigned short d_reclen;    // dirent 结构体的长度
    unsigned char  d_type;      // 文件类型
    char           d_name[];    // 文件名
};

字段含义:

  • nd_ino文件的 inode 号。
  • d_off目录项在目录中的偏移量。由于d_name长度不固定,因此需要通过此偏移量来定位目录项。
  • d_reclen表示 struct dirent 结构体的长度,以字节为单位。
  • d_type表示文件类型。
    • DT_BLK:块设备文件
    • DT_CHR:字符设备文件
    • DT_DIR:目录文件
    • DT_FIFO:管道文件
    • DT_LNK:符号链接文件
    • DT_REG:普通文件
    • DT_SOCK:套接字文件
    • DT_UNKNOWN:未知文件类型
  • d_name文件名,是一个以‘\0’结尾的字符数组。

重置读取位置-rewinddir()函数

rewinddir()函数用于将当前目录流的位置重置为目录的起始位置,以便重新开始遍历目录的文件项。该函数定义如下:

#include <dirent.h>

void rewinddir(DIR *dirp);

参数说明:

  • dirp指向已打开的目录流的指针。

获取目录流读取位置-telldir()函数

telldir()函数用于获取目录流的当前读取位置。函数定义如下:

#include <dirent.h>

long telldir(DIR *dirp);

参数说明:

  • dirp指向已打开的目录流的指针。

返回值:

返回当前目录流的位置,如果发生错误,则返回 -1,并设置相应的错误码 errno

设置目录流读取位置-seekdir()函数

seekdir()函数用于将目录流定位到指定位置,该函数定义如下:

#include <dirent.h>

void seekdir(DIR *dirp, long loc);

参数说明:

  • dirp指向已打开的目录流的指针。
  • loc目录流的位置,通常是通过调用 telldir() 函数获取的位置值。

返回值:

无返回值。

案例1-几个函数简单用法:

 1 #include<stdio.h>
 2 #include<unistd.h>
 3 #include<fcntl.h>
 4 #include<dirent.h>
 5 
 6 int main(int argc, char** argv)
 7 {
 8     if(argc != 2)
 9     {
10         printf("Usage : %s [directory]\n", argv[0]);
11         return -1;
12     }
13 
14     DIR* dirp = opendir(argv[1]);
15     if(!dirp)        
16     {
17         perror("opendir");
18         return -1;
19     }
20     
21     int nCount = 0;
22     long offset = 0;
23     struct dirent* _ptr = NULL;
24     while(_ptr = readdir(dirp))
25     {
26         if(nCount++ == 2)
27             offset = telldir(dirp);
28         printf("name : %s\n", _ptr->d_name);
29         
30     }
31 
32     puts("==========seekdir============\n");
33 
34     seekdir(dirp, offset);
35     while(_ptr = readdir(dirp))
36     {
37         if(nCount++ == 2)
38             offset = telldir(dirp);
39         printf("name : %s\n", _ptr->d_name);
40     }
41 
42     closedir(dirp);
43     return 0;
44 }

其他

获取当前工作路径-getcwd()函数

getcwd()函数用于获取当前工作目录的绝对路径。该函数定义如下:

#include <unistd.h>

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

参数说明:

  • buf一个字符数组,用于接收当前工作目录的绝对路径,会在末尾添加‘\0’。
  • sizebuf数组的大小(字节数)。

返回值:

如果函数调用成功,则返回指向buf的指针,即当前工作目录的绝对路径。如果函数调用失败,则返回NULL并设置相应的错误码 errno

案例:

 1 #include<stdio.h>
 2 #include<unistd.h>
 3 #include<string.h>
 4 
 5 int main(int argc, char** argv)
 6 {
 7     char sPath[4096];
 8     memset(sPath, 0, sizeof(sPath));
 9     if(getcwd(sPath, sizeof(sPath)) != NULL)
10     {
11         printf("cur path : %s\n", sPath);
12     }
13     else
14     {
15         perror("getcwd");
16         return -1;
17     }
18 
19     return 0;
20 }

改变当前的工作目录-chdir()函数

chdir()函数用于改变当前工作目录,即将当前进程的工作目录切换到指定的目录。该函数定义如下:

#include<unistd.h>

int chdir(const char* path);
int fchdir(int fd);

参数说明:

  • path要切换的目标目录的路径。
  • fd:以文件描述符表示的目录,使用open()函数打开目录,然后将得到的文件描述符传递给fchdir()函数。

返回值:

如果函数调用成功,则返回0。如果函数调用失败,则返回-1,并设置相应的错误码 errno

 

posted @ 2024-02-11 20:05  西兰花战士  阅读(11)  评论(0编辑  收藏  举报