《Unix环境高级编程》课后习题(1-6章)
来源:互联网 发布:佛山房地产数据 编辑:程序博客网 时间:2024/06/01 09:54
今天有时间来做一下课后习题,下面的答案是自己的理解,有一些也参考的网上的解答,并不保证正确性。
第一章
ls
命令查看文件信息,-l
选项以长格式的形式查看文件详细信息,-i
选项打印文件的i结点编号,-d
选项,表示如果文件是目录文件,则只列出该目录本身的信息,不列出该目录内文件的信息,如果文件是符号链接,则打印该链接本身的信息,不打印所指向文件的信息。在Ubuntu上进行验证:
Unix是多任务系统,执行程序时,ID为852和853的进程也在运行。
strerror没必要用const,因为是赋值传递,所以不会影响原来参数的值,而perror使用的是指针,如果不加const限制,则可能影响指针所指对象的值。
调用fflush,vsprintf,fprintf等函数可能改变errno的值,如果值改变了,没有保存先前的值,则会导致输出的信息是错误的。
(2^31-1)/(365*24*60*60) ≈ 68, 1970+68 = 2038年
约248天
第二章
采用宏条件编译,先在一个单独的文件定义一个宏SIZE_T表示实际类型,然后在每个头文件中加入宏条件编译:
#ifdef _SIZE_T_ typedef _SIZE_T_ size_t; #undef _SIZE_T_ #endif
这样可以保证实际只执行了一次typedef命令。
我使用的系统是Ubuntu14.04,系统头文件是在
/usr/include/x86_64-linux-gnu/bits
下的types.h
,下图是里面的一部分内容:没太懂题目意思。。
第三章
没有缓冲机制,因为本章介绍的函数都是直接进行的系统调用,在函数内部不会对数据进行缓冲处理。系统每次读写磁盘数据都会经过内核的块缓存器,但这并不指的是缓冲,缓冲指的是进程内部对读写的数据做缓冲处理,是软件方面定义的而不是指硬件的缓存。
本题参考APUE习题 3.2 浅析
dup2函数原型是int dup2(int filedes, int filedes2>
,功能是复制filedes描述符,并将新描述符命名为filedes2,如果filedes2已存在,则将其关闭。不能使用fcntl,则只能想办法使用dup函数,dup每次复制的新描述符值为当前系统中最小可用描述符值,所以可以循环dup函数,直至结果等于filedes2。出错处理主要是针对filedes2,判断filedes2是否是无效的描述符(小于0或者大于系统定义的最大描述符值)。代码如下:#include <unistd.h> #include <limits.h> #include <stdio.h> #include <fcntl.h> #define MAX_LEN 4096int my_dup2(int filedes, int filedes2){ int s[MAX_LEN]; int i=0, top=0; int n; if(filedes2 > sysconf (_SC_OPEN_MAX) || filedes2 < 0){ printf("Invalid filedes2 %d\n", filedes2); return -1; } if(filedes == filedes2) return filedes2; while((n = dup(filedes)) < filedes2){ if(n==-1){ printf("System can't make file \n"); return -1; } s[top++] = n; } //循环执行完后,有两种情况,一是返回的描述符大于filedes2,二是返回的描述符刚好等于filedes2,无论哪种情况,都说明filedes2已打开,所以可以先关闭,再执行dup一次,返回的描述符就是filedes2 close(filedes2); if(dup(filedes) == -1){ printf("dup function error \n"); return -1; } for(i=0; i<top; i++){ close(s[i]); } return filedes2;}int main(int argc, char *argv[]) { int filedes, filedes2; if(argc != 3) { printf("Parameter error!\n"); return -1; } filedes = open(argv[1], O_RDWR); if(filedes == -1) { printf("Error! System cannot open %s\n", argv[1]); return -1; } filedes2 = atoi(argv[2]); if(my_dup2(filedes, filedes2) != -1) { write(filedes2, "test", sizeof("test")); } return 0; }
执行dup函数只是复制原描述符,即在当前进程的文件描述符表中增加一条记录,但新描述符和原描述符仍指向同一个文件表项,即仍指向同一个文件。
每次调用open函数都会生成一个新的文件表项,对同一个文件调用两次open函数,则生成两个文件表项,这两个文件表项指向同一个v结点。dup函数并不生成新文件表项,只是在进程文件描述符表中增加一条记录。F_SETFD(设置文件描述符标记)只影响fd1,F_SETTL(设置文件状态标志)更改fd1文件表项的内容,因为fd2和fd1指向一个文件表项,所以fd2也会受到影响。
如果fd是1,执行
dup2(fd,1)
后,没有关闭描述符1,执行完程序后有三个描述符项指向同一个文件表项,不需要关闭描述符。如果fd是3,执行完三次dup2函数后,有四个描述符指向同一个文件表项,而fd描述符不再使用,所以需要关闭它。a.out > outfile 2 > &1
这条命令从左到右执行,可分为两部分:a.out > outfile
将程序a.out的标准输出重定向到outfile,2 > &1
将标准出错重定向到标准输出所指向的文件,相当于指向dup2(1, 2),结果描述符1和2指向同一个文件;a.out 2 > &1 >outfile
命令先将标准出错重定向至标准输出,然后将标准输出重定向至outfile,结果描述符1和2指向不同文件。经测试发现可以使用lseek定位至文件任一位置,并用read读任一位置的内容,但是write函数会自动将文件偏移量设置在文件结尾,所以写文件时只能从文件尾开始,不能在任一位置。
#include <fcntl.h> #include <unistd.h> #include <stdio.h>int main(int argc, char* argv[]){ int fd; if(argc !=2){ printf("input error\n"); return -1; } if((fd=open(argv[1], O_RDWR | O_APPEND))<0){ printf("open error\n"); return -1; } if(lseek(fd, 10, SEEK_SET)<0){ printf("lseek error\n"); return -1; } char buf[100]; if(read(fd,buf, 20)<0){ printf("read error\n"); return -1; } printf("%s\n", buf); if(write(fd, buf, 20)!=20){ printf("write error\n"); return -1; } int new_offset = lseek(fd, 0, SEEK_CUR); printf("new offset : %d\n", new_offset); return 0;}
第四章
stat函数会追随符号链接向前,返回的是该符号链接所指向文件的信息,所以修改后的程序不会显示文件类型是“符号链接”。若符号链接指向一个不存在的文件,则stat会报错。
新创建的文件失去所有读写权限。
chmod u-r *.txt
,cat *.txt
如果用open或creat创建已存在的文件,则该文件的存取权限不变,但文件的内容被清空了。
目录的长度永远不会是0,因为它始终包含.和..项,符号链接的长度指其文件路径名中包含的字节数,所以长度也不可以为0。
因为read函数读空洞的内容时,读的字节是0,所以可以写时做判断,如果当前字节是0,就不写入文件。
#include "apue.h" #include <fcntl.h> #include <stdio.h> int main(int argc, char* argv[]){ if(argc !=2) return -1; int fd, fd1; if((fd=open(argv[1], O_RDONLY)) <0){ printf("open error\n"); return -1; } if((fd1=open("temp", O_CREAT | O_WRONLY, FILE_MODE)) <0){ printf("create error 1\n"); return -1; } char buf; while(read(fd, &buf, 1)==1){ if(buf!=0){ if(write(fd1, &buf, 1)!=1){ printf("write error\n"); return -1; } } } return 0; }
当创建新的core文件时,内核对其存写权限有一个默认设置,在本例中是rm-r–r–,这一默认值可能会也可能不会被umask值修改。shell对创建的重定向文件也有一个默认的访问许可权,本例中是rw-rw-rw,这个值总是被umask修改,在本例中为02。
不能使用du命令的原因是因为du命令必须使用文件名和目录名,因为unlink释放了tempfile的目录项,如果用
du .
来判断的话,则不会计算tempfile文件所占的内容,所以只能用df命令查看文件系统中实际可用的自由空间。unlink删除一个目录项,会导致文件i结点计数减一,如果i结点计数不为0,则文件状态更改时间会被更新,如果i结点计数为0,则文件被删除,此时文件i结点也会被删除。
假设打开一个目录后,只有当遍历完目录内所有的文件时,才关闭该目录描述符,则对打开文件描述符数目的限制会影响文件系统中目录树的深度。
略
chroot用于辅助因特网文件传输程序(FTP)中的安全性,系统中没有账户的用户(也称匿名FTP)放在一个单独的目录下,利用chroot将当前目录当作根目录,就可以阻止此用户访问根目录以外的文件。chroot只能由超级用户使用,一旦更改了一个进程的root,该进程及进程的后代再也不能恢复至原先的root。
首先调用stat函数获取文件的三个时间值,然后用utime设置想更改的值,不改变的值保持为原值。
finger对邮箱调用stat函数,最近一次修改时间是上次接收邮件的时间,最近一次访问时间是上次读文件的时间。
略
内核对目录树的深度没有内在的限制,但如果路径名长度超过了PATH_MAX,则很多命令不能正确执行,在linux上当到达路径长度限制值时,getcwd不能再继续正常工作了。
/dev目录关闭了一般用户的写权限,所以不能删除目录项,所以unlink会失败。
第五章
setbuf对流设置缓冲区,若该流指向标准输入或输出,则将流设置为行缓冲,若指向标准出错,则设置为不带缓冲,若指向其它文件,则设置为全缓冲。
#include "stdio.h" #include "apue.h" void my_setbuf(FILE *restrict fp,char *restrict buf){ if(buf == NULL){ if(setvbuf(fp,buf,_IONBF,BUFSIZ) != 0){ err_quit("setvbuf error!"); } printf("no buf\n"); }else{ if(fp == stderr){ if(setvbuf(fp,buf,_IONBF,BUFSIZ) != 0){ err_quit("setvbuf error"); } printf("no buf\n"); }else if(fp == stdin || fp == stdout){ if(setvbuf(fp,buf,_IOLBF,BUFSIZ) != 0){ err_quit("setvbuf error"); } printf("line buf\n"); }else{ if(setvbuf(fp,buf,_IOFBF,BUFSIZ) != 0){ err_quit("setvbuf error"); } printf("fully buf\n"); } } exit(0);}
fgets函数读入数据,直至缓冲区满(会留一个字符存NULL)或者遇到换行符,fputs函数负责输出数据,并不一定输出一行,只是输出以NULL结尾的字符串,最后的NULL不输出。因此将MAXLINE改为4后,程序仍能正确执行,只是循环的次数将增多。如果是gets和puts,则不能正确执行,必须保证缓存区足够大。
表明printf没有输出任何东西。
因为getc或getchar返回的是整型结果,即输入字符的ASCII码值,由于EOF经常被定义为-1,如果系统使用的是有符号字符类型,则程序可以正常工作,如果使用无符号字符类型,则将EOF赋给字符c后将不再是-1,所以程序会进入死循环。
5个字符长的前缀,4个字符长的进程内唯一标识,再加上5个字符长的系统内唯一标识(进程ID)刚好构成一个14位长的UNIX传统文件长度限制。
fsync是将内存缓存中的数据同步到磁盘上,对于标准I/O流,因为存在用户缓冲区,所以应该先调用fflush将用户缓冲区中的内容冲洗到内存中,然后调用fsync将内存缓存中的数据同步到磁盘上。
略
第六章
调用对应的函数,读取阴影口令文件中的加密口令字段。
具有超级用户权限时,可以调用getspnam读取阴影文件内容。
uname命令输出的信息多了两条
和time_t类型的实际数据类型有关,如果超出了所能表示的最大时间值,则应该会重新从0开始。
#include "../apue.h" #include <stdio.h> #include <time.h> int main(){ time_t timeval; struct tm *t; char s[MAXLINE]; if((timeval = time(NULL))==-1) err_sys("time error"); if((t = gmtime(&timeval))==NULL) err_sys("gmtime error"); if(strftime(s, MAXLINE, "%a %b %d %X %Z %Y\n", t)==0) err_sys("strftime error"); fputs(s, stdout); exit(0); }
- 《Unix环境高级编程》课后习题(1-6章)
- 实现cp函数(UNIX环境高级编程课后题)
- Unix环境高级编程 第三章习题答案
- unix高级环境编程 第二十章 数据库函数库:习题讨论
- UNIX环境高级编程———第三章习题
- unix环境高级编程第三章习题的一些拙见
- unix环境高级编程第四章习题的一些拙见
- unix环境高级编程第五章习题的一些拙见
- unix环境高级编程第六章习题的一些拙见
- unix环境高级编程第七章习题的一些拙见
- UNIX环境高级编程习题之第三章第二题
- UNIX环境高级编程习题——第二章
- UNIX环境高级编程习题——第三章
- UNIX环境高级编程习题——第四章
- UNIX环境高级编程习题——第五章
- UNIX环境高级编程习题——第六章
- UNIX环境高级编程习题——第七章
- UNIX环境高级编程习题——第八章
- 获取tomcat端口-不通过request对象
- org.apache.ibatis.binding.BindingException: Invalid bound statement (not found)新的异常解决方案
- 错题-数据结构——队列
- 两数组的交
- 如何利用Python里面的json中的dump()/dumps()函数处理中文
- 《Unix环境高级编程》课后习题(1-6章)
- Mac上安装homebrew(类似于Linux上的apt-get)
- hdu-1232 畅通工程
- CStdioFile
- JSON & JSONP 的区别
- 队列和栈
- PHP7配置环境包
- Ubuntu 16.04 安装 VNC 及 gnome 桌面环境
- Python 的第一个 hello world 程序!