《信息安全系统设计基础》第11周问题总结
第11周学习任务:
- 掌握系统编程和系统调用的概念
- 掌握系统编程错误处理的方式
- 掌握Unix/Linux系统级I/O:open close read write seek stat
- 掌握RIO
- 掌握I/O重定向的方法
下面我在阅读了完成本周作业的78位同学的博客后,将同学们所遇到的的问题进行了总结,针对没有解决的问题,给出了一些自己的看法,如有不妥,还望批评指正。下面是我的具体总结,希望可以帮助同学们更好的理解系统调用。(有重复的问题,只涉及了一次)
所遇问题及解决办法
问题1描述:不是说fork函数,产生的两个线程的执行顺序是不确定的,为什么我多次执行,都是先显示父进程的,如图所示:
解答:的确,当我们在系统上运行这个程序时,父进程先完成了他的printf语句,在另一个系统上可能正好相反;输出行的顺序根据系统不同而不同,取决于内核如何交替执行父子进程的指令,一般而言,只要是满足条件的拓扑顺序都是合法的顺序。
问题1描述:运行sigdemo1.c
、sigdemo2.c
、sigdemo3.c
、sigactdemo.c
、
sigactdemo2.c
是按Ctrl+c不能终止程序进行。
5219的解决办法:sigdemo3.c
中输入quit
结束程序运行。其他的我直接关闭了终端.
解答:ctrl-c 发送 SIGINT 信号给前台进程组中的所有进程,常用于终止正在运行的程序, 也就是直接停止当前执行的命令;ctrl-z 发送 SIGTSTP 信号给前台进程组中的所有进程,常用于挂起一个进程,也就是挂起当前执行的命令。所以,ctrl +z可以结束。
问题2描述:编译运行testpp.c
后出现段错误……
5219的解决办法:她修改了代码
解答:char **pp,我喜欢叫他重指针,与数组不同的是,他在声明的时候没有分配空间,所以要malloc一下。
问题描述:wc统计子目录代码时出现如下报错,查询资料得知如果目标文件开头用通配符 *
表示,则需转义。
5328的解决办法:*
前加\
即可
解答:wc只能统计当前目录下所指定的文件类型,不能穿过文件夹。话说,直接cd进子文件夹不就好了。。
问题描述:虚拟机崩了包括整个电脑都带不起来无法开机。只得重装系统,又因若干写入注册表的内容无法备份
5337的解决办法:重装系统
解答:这个问题。。很残酷,电脑都无法开机了,没办法的办法,只能重装了;virtual box可以多重载入,详见我之前的博客基于VirtualBox的多重载入,希望这能省去你重新配置虚拟机的麻烦。
问题1描述:对于教材中信号方面的代码不太理解。
解答:music,“如果你愿意一层一层剥开我的心”。对,抽丝剥茧,方见真谛 ,其实这些代码都不是很长,也不复杂,只要单独弄清每一个函数的意思,应用到老师的代码里就可以理解了。
问题2描述:编译forkdemo1.c的时候出错
5231解决办法:根据提示修改源代码,加入#include <stdio.h>
重新编译即可
解答:我编代码的时候有个习惯,就是把常用的头文件全加上,省去之后出错检查的麻烦。
问题描述:一体机每次打开vi都会出现如下乱码,为什么呀:
解答:会不会是不小心删了或修改了代码,毕竟testbuf2.c里就一个printf和while啊。。
问题1描述:exec1.c中:
- 疑问:为什么没有显示最后打印的一句?
5222解答:因为在系统处理器中,在执行`execvp( "ls" , arglist );`语句时,已经将最后的打印语句覆盖掉了,处理器中并没有这句打印语句。
问题描述:教材中介绍了教材中错误处理包装函数定义在一个叫做csapp.c
的文件中,他们的原型定义在一个叫做csapp.h
的头文件中,所以要想正确编译课本代码应使用gcc include/csapp.c include/csapp.h src/week11/fork.c -o bin/fork
,但是会产生以下错误提示:
5224解决方案:应该是系统没有找到函数中包含的相应函数,于是我将对应的函数都注释了再编译的时候就可以了。
问题描述:习题8.3:刚开始看着道题的时候,认为有4种输出序列:abcc、bacc、acbc、bcac。但是答案上只写了3个,没有bcac。
5337的解决办法:忽略了waitpid函数。因为有waitpid,要等待子进程结束时,才会执行父进程的打印c命令。bcac是父进程都结束了,a进程还没有开始,所以不会有这种输出顺序。
解答:有waitpid,父进程会一直等待子进程。
问题描述:在运行argtest的时候,出现了问题。
5326的解决办法:后来发现,应该将argtest.c和makeargv.c,还有freemakeargv.c和argv.h放在同一目录下;运行成功~
argv文件夹中的主要程序是argtest.c
有两种运行方法:
第一种是用-c将所有.c的文件编译成后缀为.o的文件,然后一起编译成可执行文件。
第二种方法是直接将所有的.c文件编译成可执行文件,运行结果是一样的。
问题描述:
在虚拟机中使用/proc文件系统,结果显示权限不够,如图所示:
5216解决方法:通过最后一节内容得知,是由于输入方法不对,应该输入的是cat打印指令,再接后面的目录,如下图:
解答:毕竟这只是一个文件,不是一个程序,不好执行,只能显示
问题描述:
- 课本P504的代码的第14行中的strchr是什么函数?
5240的解决办法:char *strchr(const char* _Str,int _Val)
char *strchr(char* _Str,int _Ch)
功能:查找字符串s中首次出现字符c的位置
说明:返回首次出现c的位置的指针,返回的地址是被查找字符串指针开始的第一个与Val相同字符的指针,如果s中不存在c则返回NULL。
返回值:成功则返回要查找字符第一次出现的位置,失败返回NULL
问题描述:关于pipe.c的命令顺序不同产生的运行结果不同
-
执行命令
bin/pipe man ls
和bin/pipe ls man
的结果不一样 -
由于pipe.c的效果等同与命令行中的
|
管道
5214的解决办法:出现这个结果的原因是管道命令本身的使用规则,如下图,command1正确输出,作为command2的输入 然后comand2的输出作为,comand3的输入 ,comand3输出就会直接显示在屏幕上面了,而通过管道之后comand1,comand2的正确输出不显示在屏幕上面
解答:如果是ls | sort的话,还真是不能注意到这个问题,深刻。
附指针数组、数组指针、指针函数、函数指针的区别
数组指针(也称行指针)
定义 int (*p)[n];
()优先级高,首先说明p是一个指针,指向一个整型的一维数组,这个一维数组的长度是n,也可以说是p的步长。也就是说执行p+1时,p要跨过n个整型数据的长度。
如要将二维数组赋给一指针,应这样赋值:
int a[3][4];
int (*p)[4]; //该语句是定义一个数组指针,指向含4个元素的一维数组。
p=a; //将该二维数组的首地址赋给p,也就是a[0]或&a[0][0]
p++; //该语句执行过后,也就是p=p+1;p跨过行a[0][]指向了行a[1][]
所以数组指针也称指向一维数组的指针,亦称行指针。
指针数组
定义 int *p[n];
[]优先级高,先与p结合成为一个数组,再由int*说明这是一个整型指针数组,它有n个指针类型的数组元素。这里执行p+1时,则p指向下一个数组元素,这样赋值是错误的:p=a;因为p是个不可知的表示,只存在p[0]、p[1]、p[2]...p[n-1],而且它们分别是指针变量可以用来存放变量地址。但可以这样 *p=a; 这里*p表示指针数组第一个元素的值,a的首地址的值。
如要将二维数组赋给一指针数组:
int *p[3];
int a[3][4];
p++; //该语句表示p数组指向下一个数组元素。注:此数组每一个元素都是一个指针
for(i=0;i<3;i++)
p[i]=a[i]
这里int *p[3] 表示一个一维数组内存放着三个指针变量,分别是p[0]、p[1]、p[2]
所以要分别赋值。
这样两者的区别就豁然开朗了,数组指针只是一个指针变量,似乎是C语言里专门用来指向二维数组的,它占有内存中一个指针的存储空间。指针数组是多个指针变量,以数组形式存在内存当中,占有多个指针的存储空间。
还需要说明的一点就是,同时用来指向二维数组时,其引用和用数组名引用都是一样的。
比如要表示数组中i行j列一个元素:
*(p[i]+j)、*(*(p+i)+j)、(*(p+i))[j]、p[i][j]
优先级:()>[]>*
函数指针
在C语言中,一个函数总是占用一段连续的内存区,而函数名就是该函数所占内存区的首地址.我们可以把函数的这个首地址(或称入口地址)赋予一个指针变量,使该指针变量指向该函数.然后通过指针变量就可以找到并调用这个函数.我们把这种指向函数的指针变量称为“函数指针变量”.
函数指针变量定义的一般形式为:
类型说明符 (*指针变量名)();
其中“类型说明符”表示被指函数的返回值的类型.“(* 指针变量名)”表示“*”后面的变量是定义的指针变量.最后的空括号表示指针变量所指的是一个函数.
例如:
int (*pf)();
表示pf是一个指向函数入口的指针变量,该函数的返回值(函数值)是整型.
指针函数
函数类型是指函数返回值的类型.在C语言中允许一个函数的返回值是一个指针(即地址),这种返回指针值的函数称为指针型函数.
定义指针型函数的一般形式为:
类型说明符 *函数名(形参表)
{
…… /*函数体*/
}
其中函数名之前加了“*”号表明这是一个指针型函数,即返回值是一个指针.类型说明符表示了返回的指针值所指向的数据类型.
如:
int *ap(int x,int y)
{
./*函数体*/
}
表示ap是一个返回指针值的指针型函数,它返回的指针指向一个整型变量.