到第四章了,不知什么时候才能把这本书看完,耽误的时间太多了。
第四章是在第三章的基础上,主要描述文件系统的其他性质和文件的性质。
4.2 stat、fstat、fstatat、lstat函数
首先来看看这四个函数的原型:
#include <sys/stat.h> ///usr/include/x86_64-linux-gnu/sys/ int stat (const char *__restrict __file, struct stat *__restrict __buf) int fstat (int __fd, struct stat *__buf) int fstatat (int __fd, const char *__restrict __file, struct stat *__restrict __buf, int __flag) int lstat (const char *__restrict __file, struct stat *__restrict __buf)这几个函数的功能概括起来就是:获得stat结构体。区别就在于stat函数通过文件名获得这一结构体;fstat通过文件描述符;lstat返回该符号链接的有关信息,而不是由该符号链接引用的文件的信息;fstatat返回当前文件夹下(由fd指向)某个文件(文件名为file)的stat结构体。
好了,不管上面的几个函数具体功能为何,其核心都是返回stat结构体,stat定义如下:
struct stat { __dev_t st_dev; /* Device. */ #ifndef __x86_64__ unsigned short int __pad1; #endif #if defined __x86_64__ || !defined __USE_FILE_OFFSET64 __ino_t st_ino; /* File serial number. */ #else __ino_t __st_ino; /* 32bit file serial number. */ #endif #ifndef __x86_64__ __mode_t st_mode; /* File mode. */ __nlink_t st_nlink; /* Link count. */ #else __nlink_t st_nlink; /* Link count. */ __mode_t st_mode; /* File mode. */ #endif __uid_t st_uid; /* User ID of the file's owner. */ __gid_t st_gid; /* Group ID of the file's group.*/ #ifdef __x86_64__ int __pad0; #endif __dev_t st_rdev; /* Device number, if device. */ #ifndef __x86_64__ unsigned short int __pad2; #endif #if defined __x86_64__ || !defined __USE_FILE_OFFSET64 __off_t st_size; /* Size of file, in bytes. */ #else __off64_t st_size; /* Size of file, in bytes. */ #endif __blksize_t st_blksize; /* Optimal block size for I/O. */ #if defined __x86_64__ || !defined __USE_FILE_OFFSET64 __blkcnt_t st_blocks; /* Number 512-byte blocks allocated. */ #else __blkcnt64_t st_blocks; /* Number 512-byte blocks allocated. */ #endif #ifdef __USE_XOPEN2K8 /* Nanosecond resolution timestamps are stored in a format equivalent to 'struct timespec'. This is the type used whenever possible but the Unix namespace rules do not allow the identifier 'timespec' to appear in the <sys/stat.h> header. Therefore we have to handle the use of this header in strictly standard-compliant sources special. */ struct timespec st_atim; /* Time of last access. */ struct timespec st_mtim; /* Time of last modification. */ struct timespec st_ctim; /* Time of last status change. */ # define st_atime st_atim.tv_sec /* Backward compatibility. */ # define st_mtime st_mtim.tv_sec # define st_ctime st_ctim.tv_sec #else __time_t st_atime; /* Time of last access. */ __syscall_ulong_t st_atimensec; /* Nscecs of last access. */ __time_t st_mtime; /* Time of last modification. */ __syscall_ulong_t st_mtimensec; /* Nsecs of last modification. */ __time_t st_ctime; /* Time of last status change. */ __syscall_ulong_t st_ctimensec; /* Nsecs of last status change. */ #endif #ifdef __x86_64__ __syscall_slong_t __glibc_reserved[3]; #else # ifndef __USE_FILE_OFFSET64 unsigned long int __glibc_reserved4; unsigned long int __glibc_reserved5; # else __ino64_t st_ino; /* File serial number. */ # endif #endif };
看过了上面的stat结构体,随意感受了一下stat结构体的内容,接下来对其中几个重要的字段进行研究,首先来看:
__mode_t st_mode;
# define __STD_TYPE typedef __STD_TYPE __MODE_T_TYPE __mode_t; #define __MODE_T_TYPE __U32_TYPE ///usr/include/x86_64-linux-gnu/bits/typesize.h #define __U32_TYPE unsigned int
这一大堆,来回来去的。
根据APUE,文件共包括以下几类:
好了,再来看看linux的有关内容,还是位于/usr/include/x86_64-linux-gnu/sys/stat.h中
#define __S_ISTYPE(mode, mask) (((mode) & __S_IFMT) == (mask)) #define S_ISDIR(mode) __S_ISTYPE((mode), __S_IFDIR) #define S_ISCHR(mode) __S_ISTYPE((mode), __S_IFCHR) #define S_ISBLK(mode) __S_ISTYPE((mode), __S_IFBLK) #define S_ISREG(mode) __S_ISTYPE((mode), __S_IFREG) #ifdef __S_IFIFO # define S_ISFIFO(mode) __S_ISTYPE((mode), __S_IFIFO) #endif #ifdef __S_IFLNK # define S_ISLNK(mode) __S_ISTYPE((mode), __S_IFLNK) #endif # define S_ISSOCK(mode) __S_ISTYPE((mode), __S_IFSOCK) #ifdef __USE_POSIX199309 # define S_TYPEISMQ(buf) __S_TYPEISMQ(buf) # define S_TYPEISSEM(buf) __S_TYPEISSEM(buf) # define S_TYPEISSHM(buf) __S_TYPEISSHM(buf) #endif
在上一章中我们留下了一个疑问,通过lseek函数可以设定文件的偏移量,但若文件描述符指向管道、FIFO(命名管道)、网络套接字,则lseek返回-1,并将errno设置为ESPIPE。所以我们得到结论文件描述符0、1、2是管道或命名管道。现在终于可以通过实验验证一下了:
#include <sys/stat.h> #include <stdio.h> #include <unistd.h> int main() { struct stat buf; fstat(STDIN_FILENO,&buf); if(S_ISFIFO(buf.st_mode)); printf("stdin is fifo\n"); fstat(STDOUT_FILENO,&buf); if(S_ISFIFO(buf.st_mode)); printf("stdout is fifo\n"); fstat(STDERR_FILENO,&buf); if(S_ISFIFO(buf.st_mode)); printf("stderr is fifo\n"); return 0; }
./test_stat stdin is fifo stdout is fifo stderr is fifo
通过以上实验也验证了我们的推测——文件描述符0、1、2是管道或命名管道。
4.4 节主要讨论设置用户ID与用户组ID的问题,书中给出了一些分析,不过我感觉说的不是很清楚,所以给大家分享一篇blog:http://blog.chinaunix.net/uid-18905703-id-3843177.html
关于以上概念,结合我看到的一些资料,给大家做一个简单的总结。关于这几个概念的区分,还是从现象出发,一个程序要执行首先要知道程序的“有效用户ID”、“有效组ID”、“附属组ID”,知道了以上三个“ID”后还不够,我们还要检查当前的用户“ID”是否与以上三个“ID”相匹配,而这个当前的用户“ID”就是“实际用户ID”与“实际组ID”,也就是登录系统时所用到的身份验证信息。一般来说,当执行一个程序文件时,进程的有效用户ID通常就是实际用户ID,有效组ID通常是实际组ID。但也有特殊情况就是书中提到的passwd命令的执行,关于这个命令的详细分析大家可以参考上面那篇blog。
4.5 文件访问权限
这一节主要要给大家分享一下在访问文件过程中所用到的隐藏权限,这里隐藏的权限是指除了访问文件所需要的权限外,还需要的额外的权限,具体内容如下:
进程每次打开、创建或删除一个文件时,内核就进行文件访问权限测试,这种测试涉及两方面:一方面是文件的所有者,另一方面是执行操作的进程。对于文件的所有者进行管理主要涉及:st_uid(用户ID)、st_gid(用户组ID),对于进程的权限管理需要三个字段:有效用户ID、有限组ID、附属组ID。
有了标识进程与文件权限的信息位,再来就是对这些信息位进行比较,若相符则代表具有权限,若不符则不代表具有相应的权限。以下是内核进行的具体测试过程:
以上4个步骤顺序执行,其中若执行到一个权限规则相匹配则跳出测试,不再进行接下来的测试步骤。
4.6 新文件和目录的所有权
使用open或create创建新文件时,新文件的用户ID设置为进程的有效用户ID。 如果用open或create创建已经存在的文件,则该文件的访问权限位不变。关于组ID可以有以下两个选项:
对linux操作系统如果设置组ID位被设置则新文件的组ID设置为目录的组ID;否则新文件的组ID设置为进程的有效组ID。
好了,说了这么多有关于权限的问题,让我们回过头来再看看在上一章中留下的问题,当时留下的问题是“先在没有写权限的情况下创建文件,即此时文件仅具有读权限,然后写入数据,根据我的实验若文件不存在则可以成功写入数据。此时文件已经存在,再通过读写权限打开文件则出现无权限错误”。对于后一点错误我们已经可以理解了,确实是没有没有权限,因此后来我们加入了“写权限”,则可以成功写入数据,但对于第一点还不是很明白:“没什么没有写权限却可以写入数据?”。
4.7 access和faccessat函数
先来看看这两个函数的原型:
#include <unistd.h> extern int access (const char *__name, int __type) __THROW __nonnull ((1)); extern int faccessat (int __fd, const char *__file, int __type, int __flag) __THROW __nonnull ((2)) __wur;这两个函数的功能是按照实际用户ID和实际组ID来测试文件的访问能力,注意此处一定要两个权限都测试成功,才会返回成功,否则则返回失败。再来看看我们能测试哪些权限,在<unistd.h>中有如下定义,上述定义用于函数的type参数。
#define R_OK 4 /* Test for read permission. */ //通过注释就可以了解其功能,测试读权限 #define W_OK 2 /* Test for write permission. */ //测试写权限 #define X_OK 1 /* Test for execute permission. */ //测试执行权限 #define F_OK 0 /* Test for existence. */ //测试是否存在
access函数通过其定义可以发现,使用文件名标识被测试的文件。faccessat函数通过fd(指向某个文件夹)与文件名共同确定被测试文件。其中若faccessat函数的__flag参数设定为AT_EACCESS,则访问检查用的是调用进程的有效用户ID和有效组ID(同样按照前面的测试顺序,遇到匹配项就跳出),而不是实际用户ID和实际组ID。
通过书中的示例,对access函数的功能进行简单的测试,源码如下:
#include <unistd.h> #include <stdio.h> #include <fcntl.h> int main(int argc,char* argv[]) { if( access(argv[1],R_OK)<0 ) perror(argv[0]); else printf("read access OK\n"); if( open(argv[1],O_RDONLY)<0 ) perror(argv[0]); else printf("open for reading OK\n"); return 0; }
运行结果如下:
./test_access test_access read access OK open for reading OK
./test_access /etc/shadow ./test_access: Permission denied ./test_access: Permission denied
su //切换到root用户 chown root test_access //切换用户ID为root,通过这一步操作就可以使用open函数打开文件 chmod u+s test_access //打开用户设置用户ID位,设置用户ID位的作用用一句话概括就是“执行者将具有该程序所有者的权限”,在我们的例子中就是普通用户具有root用户的读 权限 ls -l test_access //此时程序的所有者已经变为root用户,同时设置用户ID位也被设置——“s”。 -rwsrwxr-x 1 root 8712 6月 26 17:36 test_access exit //返回普通用户 ./test_access /etc/shadow ./test_access: Permission denied //本来设置了“s”位,普通用户应该具有与root用户相同的权限,即普通用户同样可以读权限,但因为access函数使用实际用户ID与实际组ID进行 测试,所以出现无权限错误。 open for reading OK //同理由于设置了“s”位,而root用户具有读权限,普通用户具有与root用户相同的权限,则可以通过open函数打开。
4.8 umask函数
umask函数为进程设置文件模式创建屏蔽字,并返回之前的值(这是少数几个没有出错返回函数中的一个)。函数原型:
#include <sys/stat.h> extern __mode_t umask (__mode_t __mask) __THROW;其中,参数cmask是由9个常量中的若干个按位“或”构成的。在文件模式创建屏蔽字中为1的位,在文件mode中的相应位一定被关闭。
通过一个实例感受以下函数的功能,源码如下:
#include <fcntl.h> #include <unistd.h> #define RWRWRW (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH) int main(int argc,char* argv[]) { umask(0); if(creat("foo",RWRWRW)<0) perror(argv[0]); umask(S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH); if(creat("bar",RWRWRW)<0) perror(argv[0]); return 0; }
ls -l foo bar -rw------- 1 andywang andywang 0 6月 26 21:16 bar -rw-rw-rw- 1 andywang andywang 0 6月 26 21:16 foo
4.9 chmod、fchmod、fchmodat函数
先来看看函数原型:
#include <sys/stat.h> extern int chmod (const char *__file, __mode_t __mode) __THROW __nonnull ((1)); extern int fchmod (int __fd, __mode_t __mode) __THROW; extern int fchmodat (int __fd, const char *__file, __mode_t __mode, int __flag) __THROW __nonnull ((2)) __wur;
以上三个函数的功能是更改现有文件的访问权限。chmod函数在指定的文件上进行操作,而fchmod函数则对已打开的文件进行操作。fchmodat函数则对已打开的文件进行操作。fchmodat函数与chmod函数在下面两种情况下是相同的:一种是pathname参数为绝对路径,另一种是fd参数取值为AT_FDCWD而pathname参数为相对路径。否则,fchmodat计算相对于打开目录(由fd参数指向)的pathname。flag参数可以用于改变fchmodat的行为,当设置了AT_SYMLINK_NOFOLLOW标志时,fchmodat并不会跟随符号链接。
为了改变一个文件的权限位,进程的有效用户ID必须等于文件所有者ID,或者该进程必须具有超级用户权限。
除了前文中提到的9个权限位外还有6个权限位,全部权限如下:
#define S_ISUID __S_ISUID /* Set user ID on execution. */ #define S_ISGID __S_ISGID /* Set group ID on execution. */ # define S_ISVTX __S_ISVTX #define S_IRUSR __S_IREAD /* Read by owner. */ #define S_IWUSR __S_IWRITE /* Write by owner. */ #define S_IXUSR __S_IEXEC /* Execute by owner. */ #define S_IRWXU (__S_IREAD|__S_IWRITE|__S_IEXEC) #define S_IRGRP (S_IRUSR >> 3) /* Read by group. */ #define S_IWGRP (S_IWUSR >> 3) /* Write by group. */ #define S_IXGRP (S_IXUSR >> 3) /* Execute by group. */ /* Read, write, and execute by group. */ #define S_IRWXG (S_IRWXU >> 3) #define S_IROTH (S_IRGRP >> 3) /* Read by others. */ #define S_IWOTH (S_IWGRP >> 3) /* Write by others. */ #define S_IXOTH (S_IXGRP >> 3) /* Execute by others. */ /* Read, write, and execute by others. */ #define S_IRWXO (S_IRWXG >> 3)
好了,还是结合书中的例子看一下,源码如下:
#include <unistd.h> #include <sys/stat.h> #include <stdio.h> int main(int argc,char* argv[]) { struct stat statbuf; if( stat("foo",&statbuf)<0 ) perror(argv[0]); if( chmod("foo", (statbuf.st_mode & ~S_IXGRP) | S_ISGID) <0 ) perror(argv[0]); if( chmod("bar", S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH ) <0 ) perror(argv[0]); return 0; }
ls -l bar foo -rw------- 1 andywang andywang 0 6月 27 22:31 bar -rw-r--r-- 1 andywang andywang 0 6月 27 22:31 foo
ls -l bar foo -rw-r--r-- 1 andywang andywang 0 6月 27 22:32 bar -rw-rwSrw- 1 andywang andywang 0 6月 27 22:32 foo
再来是foo文件,首先通过stat函数获得已有的文件权限,再来通过操作关闭“组执行”权限,并打开“执行时设置组ID”标志(在ls命令中以“S”体现)。此处要注意的一点是:“S”代表不包括执行位,“s”代表包括执行位。chmod不会影响文件的最后修改时间。
好了,说了这么多让我们来看看文件权限计算到底是怎么进行的,我们已经知道了文件权限标志是如何定义的,让我们来看看具体值:
#define __S_ISUID 04000 /* Set user ID on execution. */ #define __S_ISGID 02000 /* Set group ID on execution. */ #define __S_ISVTX 01000 /* Save swapped text after use (sticky). */ #define __S_IREAD 0400 /* Read by owner. */ #define __S_IWRITE 0200 /* Write by owner. */ #define __S_IEXEC 0100 /* Execute by owner. */
好了,文件权限的实际值就是:
#define S_ISUID __S_ISUID //04000,数字以“0”开头,说明是8进制数字 #define S_ISGID __S_ISGID //02000 # define S_ISVTX __S_ISVTX //01000 #define S_IRUSR __S_IREAD //0400 #define S_IWUSR __S_IWRITE //0200 #define S_IXUSR __S_IEXEC //0100 #define S_IRWXU (__S_IREAD|__S_IWRITE|__S_IEXEC) // 0700 #define S_IRGRP (S_IRUSR >> 3) //左移三位,变为0040 #define S_IWGRP (S_IWUSR >> 3) //左移三位,变为0020 #define S_IXGRP (S_IXUSR >> 3) //左移三位,变为0010 /* Read, write, and execute by group. */ #define S_IRWXG (S_IRWXU >> 3) //左移三位,变为0070 #define S_IROTH (S_IRGRP >> 3) //在0040的基础上再次左移三位,变为0004 #define S_IWOTH (S_IWGRP >> 3) //0002 #define S_IXOTH (S_IXGRP >> 3) //0001 /* Read, write, and execute by others. */ #define S_IRWXO (S_IRWXG >> 3) //0007
通过上面的计算,可以发现权限位的值与“chmod”命令中所使用的值完全相同,不同权限之间使用“|”运算进行计算,这一点没有什么问题。
再来看看关闭一个标志位所进行的计算,通过上面的例子可以看到,关闭一个权限位所使用的是“先取反,再进行与运算”。还是结合上面的代码来看一看,“S_IXGRP”的值为“0010”编程2进制就是“000001000”(首位的0不参与运算),此时代表仅有“组执行”权限开启,取反后变为“111110111”,仅有“组执行”位关闭,此时进行与运算,则“组执行”位一定会被关闭,而其他权限位则保持不变,如此就实现了关闭“组执行”位的功能。
4.10 粘着位
有关于粘着位的功能请见以下blog:http://os.51cto.com/art/201004/194994.htm
当前,粘着位对于普通文件已经没有作用了,但若目录的粘着位被设置,则可以起到保护其中文件的作用。
4.11 chown、fchown、fchownat、lchown函数
已经看过了更改权限的函数,以上几个函数的主要功能就是改变文件的主人,对应的命令就是chown。与改变权限的那几个函数特点类似,在此就不详细分析了。
4.12 文件长度
stat结构成员st_size表示以字节为单位的文件的长度。此字段只对普通文件、目录文件和符号链接有意义。对于目录,文件长度通常是一个数(如16或512)的整倍数。对于符号链接,文件长度是在文件名中的实际字节数。
同时本小节再次讨论了与文件空洞有关的内容:
在第三章中我们通过实验创建了两个文件,其中一个带有文件空洞,可通过du命令查看文件实际占用的磁盘块个数,命令如下:
du -s file.hole 8 file.hole
wc -c file.hole 16394 file.hole
如果使用实用程序复制这个文件,那么所有这些空洞都会被填满,其中所有实际数据字节皆被填写为0,操作如下:
cat file.hole > file.hole.copy du file.hole.copy 20 file.hole.copy //此时有空洞的文件所占用的磁盘块数也是20
#include <unistd.h> #include <sys/stat.h> #include <stdio.h> int main(int argc,char* argv[]) { struct stat statbuf; if( stat("file.hole",&statbuf)<0 ) perror(argv[0]); else printf("%ld\n",statbuf.st_size); if( stat("file.nohole",&statbuf)<0 ) perror(argv[0]); else printf("%ld\n",statbuf.st_size); if( stat("file.hole.copy",&statbuf)<0 ) perror(argv[0]); else printf("%ld\n",statbuf.st_size); return 0; }
./test_st_size 16394 16394 16394
4.13 文件截断
将文件截取为一定length可通过以下函数:
#include <unistd.h> extern int truncate (const char *__file, __off_t __length) __THROW __nonnull ((1)) __wur; extern int ftruncate (int __fd, __off_t __length) __THROW __wur;
以上两个函数定义在unstd.h中,说明上述函数是系统调用,与上面的几个函数具有一定的区别。这里要注意的一点是如果该文件以前的长度大于length,则超过length以外的数据就不再能访问了。如果以前的长度小于length,文件长度将增加,在以前的文件尾端和新的文件尾端之间的数据将读作0。我认为这个函数的功能与lseek类似,都是直接设置当前文件偏移量。
4.14文件系统
本小节主要介绍unix文件系统(UFS)的概念模型。在学习具体的文件系统之前,应对虚拟文件系统有一定的了解,有关于虚拟文件的介绍请见:
http://blog.csdn.net/u012927281/article/details/51793209
虚拟文件系统与实际文件系统之间的关系请见下图。比较尴尬,只找到一张繁体字的图,下图来自于鸟哥的私房菜。
关于文件系统有以下几点与大家简单分享以下:
有关于软链接与硬链接的区别请见:http://www.ibm.com/developerworks/cn/linux/l-cn-hardandsymb-links/
4.15 link、linkat、unlink、unlinkat、remove
创建一个指向现有文件的链接的方法是使用link函数或unlink函数。
extern int link (const char *__from, const char *__to) //若to已经存在则出错。只创建to中的最后一个分量,路径中的其他部分应当已经存在。 __THROW __nonnull ((1, 2)) __wur; extern int linkat (int __fromfd, const char *__from, int __tofd, const char *__to, int __flags) __THROW __nonnull ((2, 4)) __wur;
为了删除一个现有的目录项,可以调用unlink函数。
extern int unlink (const char *__name) __THROW __nonnull ((1)); extern int unlinkat (int __fd, const char *__name, int __flag) __THROW __nonnull ((2));
若一个文件被某个进程打开,则文件的内容不会被删除,也就是inode节点不会被释放。关闭一个文件时,内核首先检查打开该文件的进程个数;如果这一计数达到0,内核再去检查其链接计数;若计数也是0,那么就删除该文件的内容。
unlink的这种特性经常被程序用来确保即使是在程序崩溃时,它所创建的临时文件也不会被遗留下来。进程用open或create创建一个文件,然后立即调用unlink,因为该文件仍旧是打开的,所以不会将其内容删除。只有当进程关闭该文件或终止时,该文件的内容才被删除。
如果name是符号链接,那么unlink删除该符号链接,而不是删除由该链接所引用的文件。给出符号链接名的情况下,没有一个函数能删除由该链接所引用的文件。
本节最后一个函数:
#include <stdio.h> extern int remove (const char *__filename) __THROW;
4.17 符号链接
有关于符号链接的有关内容,之前已经给大家分享过。相较于符号链接,硬链接具有以下限制:
关于能不能以普通用户创建硬链接,可以通过以下实验进行验证,结果如下:
mkdir empty ln empty link ln: "empty": 不允许将硬链接指向目录
再来试试使用超级用户建立指向目录的硬链接,同样不允许,所以此处linux与书中所讲的内容有些许出入。
既然硬链接不行,那就试试软链接:
ln -s empty link
既然可以构建目录的符号链接,那么试试构建指向
4.18节主要介绍创建和读取符号链接的函数,先来看看基本定义。
extern int symlink (const char *__from, const char *__to) __THROW __nonnull ((1, 2)) __wur; extern int symlinkat (const char *__from, int __tofd, const char *__to) __THROW __nonnull ((1, 3)) __wur;
extern ssize_t readlink (const char *__restrict __path, char *__restrict __buf, size_t __len) __THROW __nonnull ((1, 2)) __wur; extern ssize_t readlinkat (int __fd, const char *__restrict __path, char *__restrict __buf, size_t __len) __THROW __nonnull ((2, 3)) __wur;
返回的符号链接的内容不以null字节终止。
4.19与4.20两节主要讨论与文件时间相关的概念,在此先不深入讨论了。
4.21创建与删除目录函数
extern int mkdir (const char *__path, __mode_t __mode) __THROW __nonnull ((1)); extern int mkdirat (int __fd, const char *__path, __mode_t __mode) __THROW __nonnull ((2));
用rmdir函数可以删除一个空目录。空目录是只包含.和..这两项的目录。
extern int rmdir (const char *__path) __THROW __nonnull ((1));
4.22 读目录
有关于DIR、struct dirent等结构的详细分析请见:http://www.liweifan.com/2012/05/13/linux-system-function-files-operation/
关于递归读目录程序的相关解析请见:http://blog.csdn.net/angle_birds/article/details/8503039
有些懒了,就不详细分析了。
4.23 chdir、fchdir、getcwd函数
每个进程都有一个当前工作目录,此目录是搜索所有相对路径名的起点。当前工作目录是进程的一个属性,起始目录则是登录名的一个属性。
进程调用chdir和fchdir函数可以更改当前工作目录。
#include <unistd.h> extern int chdir (const char *__path) __THROW __nonnull ((1)) __wur; extern int fchdir (int __fd) __THROW __wur;因为当前工作目录是进程的一个属性,所以它只影响调用chdir的进程本身,而不影响其他进程。另一方面,由于shell在执行程序时会建立新的进程,因此为了改变shell的当前工作目录可以采取的方法是shell直接调用chdir函数。为此cd命令内建在shell中。
通过以下函数可以获得当前工作目录的完整绝对路径名。
extern char *getcwd (char *__buf, size_t __size) __THROW __wur;