apue第4章习题

本文解答了APUE章节中的多个习题,包括使用stat函数替代lstat函数的效果、文件权限的变化验证、复制包含空洞文件的方法等。通过实验验证了文件操作行为及系统调用的特性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

4.1 用 stat 函数替换图 4-3 程序中的 lstat函数,如若命令行残数之一是符号链接,会发生什么变化?

stat不支持链接,如果有参数是链接符号,会显示链接后的文件属性。

4.2 如果文件模式创建屏蔽字 777 (八进制),结果会怎样?用shell的umask命令验证该结果

pi@raspberrypi:~/chen_DIR/APUE/chapter_4 $ vim ex4-2.c
pi@raspberrypi:~/chen_DIR/APUE/chapter_4 $ gcc ex4-2.c
pi@raspberrypi:~/chen_DIR/APUE/chapter_4 $ ./a.out
pi@raspberrypi:~/chen_DIR/APUE/chapter_4 $ ls
4-12.c  4-21.c  4-23.c  4-25.c  4-8.c  a.out      ex4-2_bar  ex4-2_foo  myfile  times
4-16.c  4-22.c  4-24.c  4-3.c   4-9.c  changemod  ex4-2.c    foo        tags
pi@raspberrypi:~/chen_DIR/APUE/chapter_4 $ ls -la ex4-2_bar ex4-2_foo
---------- 1 pi pi 0 Dec  1 08:52 ex4-2_bar
-rw-rw-rw- 1 pi pi 0 Dec  1 08:52 ex4-2_foo

会把所有的位都屏蔽掉

4.3 关闭一个你所拥有文件的用户读权限,将导致拒绝你访问自己的文件,对此进行验证。

pi@raspberrypi:~/chen_DIR/APUE/chapter_4 $ chmod u-rw ex4-2_foo
pi@raspberrypi:~/chen_DIR/APUE/chapter_4 $ cat ex4-2_foo
cat: ex4-2_foo: Permission denied

4.4 创建文件foo和bar后,运行图4-9的程序,讲发生什么情况?

pi@raspberrypi:~/chen_DIR/APUE/chapter_4 $ ./a.out
pi@raspberrypi:~/chen_DIR/APUE/chapter_4 $ ls -l foo bar
-rw------- 1 pi pi 0 Dec  1 09:25 bar
-rw-rw-rw- 1 pi pi 0 Dec  1 09:25 foo
pi@raspberrypi:~/chen_DIR/APUE/chapter_4 $ chmod a-r foo bar
pi@raspberrypi:~/chen_DIR/APUE/chapter_4 $ ls -l foo bar
--w------- 1 pi pi 0 Dec  1 09:25 bar
--w--w--w- 1 pi pi 0 Dec  1 09:25 foo
pi@raspberrypi:~/chen_DIR/APUE/chapter_4 $ ./a.out
pi@raspberrypi:~/chen_DIR/APUE/chapter_4 $ ls -l foo bar
--w------- 1 pi pi 0 Dec  1 09:26 bar
--w--w--w- 1 pi pi 0 Dec  1 09:26 foo

文件的权限没有变,但是文件内容什么的修改时间都被改了。

 

4.5 4.12节中讲到一个普通文件的大小可以为0,同时我们又知道st_size字段是为目录或符号链接定义的,那么目录和符号链接的长度是否可以为0?

目录的长度从来不会是0,因为它总是包含 . 和 .. 两项。符号链接的长度指其路径名包含的字符数,由于路径名中至少有一个字符,所以长度也不为0。

4.6 编写一个类似cp(1)的程序,他复制包含空洞的文件,但不将字节0写到输出文件中去。

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>

#define BUF_SIZE 4096

int main(int argc, char *argv[])
{
        char *filePathFrom;
        char *filePathTo;
        int fdFrom, fdTo;
        int have_holes, byte_count, res, current_position;
        char buff[BUF_SIZE];
        struct stat st;


        if(argc !=3 || !argv[1] || !argv[2])
        {
                fprintf(stderr, "Usage: %s <source file path> <target file path>\n",
                                argv[0]);
                exit(1);
        }
        filePathFrom = argv[1];
        filePathTo = argv[2];
        if((fdFrom = open(filePathFrom, O_RDWR)) < 0)
        {
                fprintf(stderr, "open path from error");
                exit(1);
        }

        if(fstat(fdFrom, &st) != 0)
                fprintf(stderr, "stat error");
        else {
                if(S_ISREG(st.st_mode) && st.st_size > 512 * st.st_blocks) {
                        have_holes = 1;
                        printf("%s is a sparse-block file!\n", filePathFrom);
                } else {
                        have_holes = 0;
                        printf("%s is not a sparse-block file!\n", filePathFrom);
                }
        }

        if((fdTo = open(filePathTo, O_RDWR|O_APPEND|O_CREAT|O_TRUNC, 0666)) < 0)
        {
                fprintf(stderr, "open path to error");
                exit(1);
        }

        memset(buff, '\0', BUF_SIZE);
        if((res = read(fdFrom, buff, BUF_SIZE)) < 0)
        {
                fprintf(stderr, "fdFrom read error");
                exit(1);
        }
        if(have_holes)
        {
                byte_count = 0;
                for(current_position = 0;current_position < res; current_position++)
                {
                        if(buff[current_position] != 0)
                        {
                                buff[byte_count] = buff[current_position];
                                byte_count++;
                        }
                }
        } else
                byte_count = res;

        if((res = write(fdTo, buff, byte_count)) < 0)
        {
                fprintf(stderr, "fdTo write error");
                exit(1);
        }

        close(fdFrom);
        close(fdTo);
}
View Code

4.7 在4.12节ls命令的输出中,core和core.copy的访问权限不同,如果创建两个文件时umask没有变,说明为什么会发生这种差别。

当创建新的core文件时,内核对其访问权限有一个默认设置,在本例中是rw-r--r--。这一默认值可能会也可能不会被umask的值修改。shell对创建的重定向的新文件也有一个默认的访问权限,本例中为rw-rw-rw-,这个值总是被当前的umask修改,在本例中umask为02.

4.8 在运行图4-16的程序时,使用了df(1)命令来检查空闲的磁盘空间。为什么不使用du(1)命令?

不能使用du的原因是它需要文件名,如

du tempfile或目录名,如

du .

只有当unlink函数返回时才释放tempfile的目录项,du .命令没有计算仍然被tempfile占用空间。本例中只能使用df命令查看文件系统中实际可用的空闲空间。

4.9 图4-20中显示unlink函数会修改文件状态更改时间,这是怎样发生的?

如果被删除的链接不是该文件的最后一个链接,则不会删除该文件。此时,文件的状态更改时间被更新。但是,如果被删除的链接是最后一个链接,则该文件将物理删除。这时再去更新文件的状态更改时间就没有意义,因为包含文件所有信息的i借点将会随着文件的删除而被释放。

4.10 4.22节中,系统对可打开文件数的限制对myftw函数会产生什么影响?

用opendir打开一个目录后,递归调用函数dopath。假设opendir使用一个文件描述符,并且只有在处理完目录后才调用closedir释放描述符,这就意味着每次降一级就要使用另外一个描述符。所以进程可以打开的最大描述符就限制了我们可以遍历的文件系统数的深度。single Unix sepcification的XSI扩展中说明的ftw允许调用者指定使用的描述符,这隐含着可以关闭描述符并且重用它们。

4.11 在4.22节中的myftw从不改变其目录,对这种处理方法进行改动;每次遇到一个目录就调用chdir,这样每次调用lstat时就可以使用文件名而非路径名,处理完所有的目录项后执行chdir("..")。比较这种版本的程序和书中程序的运行时间。

#include "apue.h"
#include <dirent.h>
#include <limits.h>
#include "../chapter_2/2-16.c"

/* function type that is called for each filename */
typedef int Myfunc(const char *, const struct stat *, int);

static Myfunc myfunc;
static int myftw(char *, Myfunc *);
static int dopath(Myfunc *);
static long nreg, ndir, nblk, nchr, nfifo, nslink, nsock, ntot;

int main(int argc, char *argv[])
{
        int ret;
        if(argc != 2)
                err_quit("usage: ftw <starting-pathname>");
        ret = myftw(argv[1], myfunc);           /* does it all */
        ntot = nreg + ndir + nblk + nchr + nfifo + nslink + nsock;
        if(ntot == 0)
                ntot = 1;
        printf("regular files = %7ld, %5.2f %%\n", nreg, nreg*100.0/ntot);
        printf("directories   = %7ld, %5.2f %%\n", ndir, ndir*100.0/ntot);
        printf("block special = %7ld, %5.2f %%\n", nblk, nblk*100.0/ntot);
        printf("char special  = %7ld, %5.2f %%\n", nchr, nchr*100.0/ntot);
        printf("FIFOs         = %7ld, %5.2f %%\n", nfifo, nfifo*100.0/ntot);
        printf("symbolic links = %7ld, %5.2f %%\n", nslink, nslink*100.0/ntot);
        printf("sockets       = %7ld, %5.2f %%\n", nsock, nsock*100.0/ntot);
        exit(ret);
}


#define FTW_F   1
#define FTW_D   2
#define FTW_DNR 3
#define FTW_NS  4

static char *fullpath;
static char *filename;
static size_t pathlen;

static int myftw(char *pathname, Myfunc *func)
{
        filename = path_alloc(&pathlen);

        if(pathlen <= strlen(pathname)) {
                pathlen = strlen(pathname) * 2;
                if((filename = realloc(filename, pathlen)) == NULL)
                        err_sys("realloc failed.");
        }
        strcpy(filename, pathname);
        return (dopath(func));
}

static int dopath(Myfunc *func)
{
        struct stat statbuf;
        struct dirent *dirp;
        DIR *dp;
        int ret, n;

        if(lstat(filename, &statbuf) < 0)
                return(func(filename, &statbuf, FTW_NS));
        if(S_ISDIR(statbuf.st_mode) == 0)
                return(func(filename, &statbuf, FTW_F));

        if((ret = func(filename, &statbuf, FTW_D)) != 0)
                return(ret);
        n = strlen(filename);
        if(n + NAME_MAX + 2 > pathlen) {
                pathlen *= 2;
                if((filename = realloc(filename, pathlen)) == NULL)
                        err_sys("realloc failed");
        }
        //filename[n++] = '/';
        //filename[n] = 0;
        if((dp = opendir(filename)) == NULL)
                return(func(filename, &statbuf, FTW_DNR));
        if(chdir(filename) < 0)
                err_sys("chdir %s error", filename);
        while((dirp = readdir(dp)) != NULL) {
                if(strcmp(dirp->d_name, ".") == 0 ||
                        strcmp(dirp->d_name, "..") == 0)
                        continue;
                filename = dirp->d_name;
                if((ret = dopath(func)) != 0)
                        break;
        }
        if(chdir("..") < 0)
                err_sys("chdir .. error");
        if(closedir(dp) < 0)
                err_ret("can't close directory %s", filename);
        return(ret);
}

static int myfunc(const char *pathname, const struct stat *statptr, int type)
{
        switch(type) {
        case FTW_F:
                switch(statptr->st_mode & S_IFMT) {
                case S_IFREG: nreg++; break;
                case S_IFBLK: nblk++; break;
                case S_IFCHR: nchr++; break;
                case S_IFIFO: nfifo++; break;
                case S_IFLNK: nslink++; break;
                case S_IFSOCK: nsock++; break;
                case S_IFDIR: err_dump("for S_IFDIR for %s", pathname);
                }
                break;
        case FTW_D:
                ndir++;
                break;
        case FTW_DNR:
                err_ret("can't read directory %s", pathname);
                break;
        case FTW_NS:
                err_ret("stat error for %s", pathname);
                break;
        default:
                err_dump("unknown type %d for pathname %s", type, pathname);
        }
        return(0);
}
View Code

4.12 每个进程都有一个根目录用于解析绝对路径名,可以通过chroot函数改变根目录。在手册中查询次函数。说明这个函数什么时候有用

chroot函数被因特网文件传输协议(Internet File Transfer Protocal,FTP)程序用于辅助安全性。系统中没有账户的用户(也称为匿名FTP)放在一个单独的目录下,利用chroot将此目录当作新的根目录,就可以阻止用户方位此目录以外的文件。

chroot也用于在另一台机器上构造一个文件系统层次结构的副本,然后修改此副本,不会更改原来的文件系统。这可用于测试新软件包的安装。

chroot只能由超级用户执行,一旦更改了一个进程的根,该进程及其后代进程就再也不能回复至原先的根。

转载于:https://www.cnblogs.com/ch122633/p/7940454.html

### APUE 第三 学习笔记 #### 文件 I/O 基础 APUE 的第三主要讨论了 Unix 系统中的文件 I/O 操作基础。这一节涵盖了多个重要的概念和技术细节,对于理解如何高效地操作文件至关重要。 #### 打开和关闭文件 为了打开一个文件,程序通常会使用 `open` 或者 `creat` 函数[^1]。这两个函数都返回一个小于零的整数作为错误指示,而成功的调用则返回一个非负整数表示新创建的文件描述符。当不再需要访问某个特定文件时,应该通过调用 `close` 来关闭它。这不仅释放了与该文件关联的操作系统资源,而且也使得这个文件描述符能够被重新利用。 ```c #include <fcntl.h> /* For O_* constants */ #include <unistd.h> /* For open(), close() */ int fd; fd = open("example.txt", O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); if (fd >= 0) { // File opened successfully. } // Later... close(fd); ``` #### 文件读写 一旦有了有效的文件描述符,就可以对其进行读取 (`read`) 和写入 (`write`) 操作。这些基本的 I/O 操作允许应用程序直接处理底层的数据流而不必关心具体的设备特性[^2]。 ```c char buffer[BUFSIZ]; ssize_t n; n = read(fd, buffer, BUFSIZ - 1); if (n > 0) { buffer[n] = '\0'; // Null terminate the string printf("%s\n", buffer); } const char *msg = "Hello world!"; write(fd, msg, strlen(msg)); ``` #### 文件定位 除了简单的顺序读写外,还可以改变当前文件偏移量来实现随机访问。这是通过 `lseek` 实现的功能之一,它可以向前或向后移动文件指针的位置以便从不同的位置开始读写数据[^3]。 ```c off_t offset; offset = lseek(fd, SEEK_SET, 0); // Move to beginning of file if (offset != -1L) { // Seek succeeded. } ``` #### 特殊文件类型的支持 Unix 系统支持多种特殊类型的文件对象,比如管道、套接字以及终端设备等。本还介绍了针对这些不同类型文件的具体 API 接口和支持机制[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值