对cs给的第十章的代码的理解,与运行结果
关于代码如何运行,大概可以用如下的方法:
先用gcc -c 命令生成文件(这里用file.c代替)以及csapp.c的.o文件然后用
gcc -o file file.o csapp.o -lpthread
因为csapp里用到了线程有关的东西,所以要加-lpthread
#include "csapp.h"
int main(int argc, char *argv[])
{
int fd1, fd2, fd3;
char c1, c2, c3;
char *fname = argv[1];
fd1 = Open(fname, O_RDONLY, 0);
fd2 = Open(fname, O_RDONLY, 0);
fd3 = Open(fname, O_RDONLY, 0);
dup2(fd2, fd3);
Read(fd1, &c1, 1);
Read(fd2, &c2, 1);
Read(fd3, &c3, 1);
printf("c1 = %c, c2 = %c, c3 = %c\n", c1, c2, c3);
Close(fd1);
Close(fd2);
Close(fd3);
return 0;
}
abcde.txt文件的内容为abcde
运行./file1 abcde.txt的运行结果如下:
对于代码的解释,大写的Open,Read,Close仅是在csapp里进行了有关错误的处理,重新定义的函数,其他的均与原函数相同,对于类似的题可以通过画出图来处理,画出的图如下:
图一
图二
在用了三个open函数打开了同一文件三次,每次都会在文件打开表里创建一个项,且文件描述符表的项指向打开表中的表项,如第一张图,而调用dup2(fd2,fd3)后将fd2的文件打开表的表项赋给了fd3,所以fd2与fd3指向的是同一个文件打开表的表项,第一条read不必多说,在fd2读了一个字符后,光标往后移一位,因为fd3与fd2的文件打开表的表项相同,所以fd3的read会接着读,所以fd2读的是a,而fd3会接着读a后面的b所以输出的结果是c1 = a,a2 = a,c3 = b(之后不再解释类似问题,建议画图)
#include "csapp.h"
int main(int argc, char *argv[])
{
int fd1;
int s = getpid() & 0x1;
printf("pid = %d,s = %d\n",getpid(),s);
char c1, c2;
char *fname = argv[1];
fd1 = Open(fname, O_RDONLY, 0);
Read(fd1, &c1, 1);
if (fork()) {
/* Parent */
sleep(s);
Read(fd1, &c2, 1);
printf("Parent: c1 = %c, c2 = %c\n", c1, c2);
} else {
/* Child */
sleep(1-s);
Read(fd1, &c2, 1);
printf("Child: c1 = %c, c2 = %c\n", c1, c2);
}
return 0;
}
我再源代码的基础上作了一些修改添加了
printf(“pid = %d,s = %d\n”,getpid(),s);
这一行,且运行如下./file2 abcde.txt,可以让结果更加的明显,运行的结果如下:
这里的s为进程的pid与上0x1也就是将进程的pid号最低位(二进制)赋给s,也就是说该进程(fork前)的pid号为奇数则父进程等待1s,偶数则是子进程等待1s,有关的图如下:
图一
图二
原本打开文件如图一,因此读出的字符为a,光标往后移一位,之后fork创建子进程,子进程得到了父进程所有的资源,包括打开的文件表,而后因为原进程pid为奇数,所以s为1,1-s为0,所以父进程会等待1s再执行,所以子进程接着往后读得到c2 = b,父进程等待完之后往后读c2 = c,所以有了如图的结果,有趣的是多次执行该程序并不会改变父进程子进程的执行顺序,这是因为创建了一个子进程,所以每次都是执行的两个进程,并不会改变之后执行进程的pid号,这里我们运行题1的程序再运行题2的程序,来改变奇偶性,结果如图:
这样就变成了父进程先运行,子进程等待1s,对于shell的提示符的解释是:提示符的输出取决于最初的父进程,也就是原进程,一旦父进程结束shell提示符就会输出,而之后运行完的子进程只能再提示符之后输出(这里为个人理解,欢迎指出错误的地方),所以子进程的结果输出在了下一个提示符之后。
#include "csapp.h"
int main(int argc, char *argv[])
{
int fd1, fd2, fd3;
char *fname = argv[1];
fd1 = Open(fname, O_CREAT|O_TRUNC|O_RDWR, S_IRUSR|S_IWUSR);
Write(fd1, "pqrs", 4);
fd3 = Open(fname, O_APPEND|O_WRONLY, 0);
Write(fd3, "jklmn", 5);
fd2 = dup(fd1); /* Allocates new descriptor */
Write(fd2, "wxyz", 4);
Write(fd3, "ef", 2);
Close(fd1);
Close(fd2);
Close(fd3);
return 0;
}
这一题比较麻烦,还是把图画出来:
开始时这里fd1用open打开文件,带的属性的意思为如果文件不存在则创建文件并打开,存在则打开并且截断它(清空),以可读可写的方式打开,之后在空文件中写入pqrs,此时fd1的光标再s之后,而fd3是在fd1写完之后再打开的,所以打开时里面已经有pqrs存在了,而fd2的打开属性设置是O_APPEND,指的是每次写都在最后,所以fd3写了之后文件的内容为pqrsjklmn,此时fd3的光标在n之后,而之后使用dup函数将fd1的文件打开表项给fd2,所以fd2会接着fd1的光标写入wxyz,也就是在s后面写入第5~8个字符wxyz,也就是原来的jklm被覆盖了,此时文件的内容为pqrswxyzn,之后fd3接着在n后写入ef,到此时文件的内容为pqrswxyznef,所以最后输出的值为pqrswxyznef
程序运行的结果如下:
这一题说起来有点悲伤,在第一次做的时候做错了
暂时写到这,欢迎大家指正错误。