2017-2018-1 20155205 《信息安全系统设计基础》第六周学习总结
2017-2018-1 20155205 《信息安全系统设计基础》第六周学习总结
教材学习内容总结
1.系统调用:进程控制
-
fork系统调用
-
函数作用:创建一个子进程
-
形式:pid_tfork(void);
pid_t vfork(void); -
说明: 使用vfork创子进程时,不会进程父进程的上下文
-
返回值:
[返回值=-1]子进程创建失败
[返回值=0]子进程创建成功
[返回值>0]对父进程返回子进程PID
测试代码:
-
#include<stdio.h>
#include<sys/stat.h>
#include<unistd.h>
int main() {
pid_t id = fork();
if (id < 0) {
perror("子进程创建失败!");
} else {
if (id == 0) {
printf("子进程工作:PID=%d,PPID=%d\n", getpid(), getppid());
}else
{
printf("父进程工作:PID=%d,PPID=%d,子进程PID=%d\n", getpid(), getppid(),id);
sleep(5);
}
}
}
测试结果:
-
exit系统调用
-
函数作用:终止发出调用的进程
-
形式:voidexit(int status);
-
说明
- exit返回信息可由wait系统函数获得
- 如果父进程先退出子进程的关系被转到init进程下
测试代码:
-
#include<stdio.h>
#include<sys/stat.h>
#include<unistd.h>
#include<stdlib.h>
int main() {
pid_t id = fork();
if (id < 0) {
perror("子进程创建失败!");
} else {
if (id == 0) {
printf("子进程工作:PID=%d,PPID=%d\n", getpid(), getppid());
sleep(20);
printf("此时子进程:PID=%d,PPID=%d\n", getpid(), getppid());
}else
{
printf("父进程工作:PID=%d,PPID=%d,子进程PID=%d\n", getpid(), getppid(),id);
sleep(5);
exit(3);
}
}
return 0;
}
测试结果:
-
exec系统调用
- 函数作用:以新进程代替原有进程,但PID保持不变
测试代码1:
#include<stdio.h>
#include<unistd.h>
int main()
{
printf("这是第一个进程PID=%d\n",getpid());
execv("exec2",NULL);
printf("asa");
return 0;
}
测试代码2:
#include<stdio.h>
#include<unistd.h>
int main()
{
printf("这是第二个进程PID=%d\n",getpid());
}
测试结果:
-
wait系统调用
-
函数作用:父进程与子进程同步,父进程调用后,进入睡眠状态,直到子进程结束或者父进程在被其他进程终止。
-
形式:pid_twait(int *status) pid_t waitpid(pid_t pid ,int *status,int option)
-
参数:status:exit是设置的代码
pid:进程号
option: WNOHANG|WUNTRACEDWNOHANG:,即使没有子进程退出,它也会立即返回,不会像wait那样永远等下去.
WUNTRACED:子进程进入暂停则马上返回,但结束状态不予以理会. -
返回值:如果成功等待子进程结束,则返回子进程PID。后者为-1
-
-
setenv函数和unsetenv函数
- 命令查看man 3 setenv
可见,两个函数都是成功返回0,失败返回-1,并记录errno信息。
- 测试代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
char* val;
const char* name ="ABC";
//获取ABC环境变量的值
val = getenv(name);
printf("No.1 %s=%s\n", name, val);
//覆盖写入环境变量
setenv(name, "I amsure of that I will get it", 1);
printf("No.2%s=%s\n", name, val);
val = getenv(name);
printf("No.3%s=%s\n", name, val);
//删除一个环境变量
int ret =unsetenv("ABC");
printf("ret =%d\n",ret);
val = getenv(name);
printf("No.3 %s=%s\n",name, val);
return 0;
}
测试结果:
2.I/O重定向
- Linux 使用三种流:
0:stdin 标准输入
1:stdout 标准输出
2:stderr 标准错误输出
1.下面代码证明是shell将输入和输出重定向的,并不将重定向标记和文件名传递给程序。
#include<stdio.h>
int main(int ac,char *av[]){
int i;
printf("number of args:%d\n",ac);
printf("args are:\n");
for(i=0;i<ac;i++)
printf("args[%d]=%s\n",i,av[i]);
fprintf(stderr,"This message is sent to stderr.\n");
return 0;
}
结果:
3.管道
管道创建成功以后,创建该管道的进程(父进程)同时掌握着管道的读端和写端。为实现父子进程间通信呢,通常可以采用如下步骤:
- 父进程调用pipe函数创建管道,得到两个文件描述符fd[0]、fd[1]指向管道的读端和写端。
- 父进程调用fork创建子进程,那么子进程也有两个文件描述符指向同一管道。
- 父进程关闭管道读端,子进程关闭管道写端。父进程可以向管道中写入数据,子进程将管道中的数据读出。由于管道是利用环形队列实现的,数据从写端流入管道,从读端流出,这样就实现了进程间通信。
教材学习中的问题和解决过程
- 问题1:按发出信号的原因如何分类?
- 问题1解决方案:
(1) 与进程终止相关的信号。当进程退出,或者子进程终止时,发出这类信号。
(2) 与进程例外事件相关的信号。如进程越界,或企图写一个只读的内存区域(如程序正文区),或执行一个特权指令及其他各种硬件错误。
(3) 与在系统调用期间遇到不可恢复条件相关的信号。如执行系统调用exec时,原有资源已经释放,而目前系统资源又已经耗尽。
(4) 与执行系统调用时遇到非预测错误条件相关的信号。如执行一个并不存在的系统调用。
(5) 在用户态下的进程发出的信号。如进程调用系统调用kill向其他进程发送信号。
(6) 与终端交互相关的信号。如用户关闭一个终端,或按下break键等情况。
(7) 跟踪进程执行的信号。
代码调试中的问题和解决过程
- 问题1:如何实现alarm()time秒时间之后向该进程发送一个定时信号,然后该进程捕获该信号并处理?
- 问题1解决方案:
由以下代码可实现:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
void sig_handler(int num)
{
printf("receive the signal %d.\n", num);
}
int main()
{
signal(SIGALRM, sig_handler);
alarm(2);
while(1){
pause();
printf("pause is over.\n");
}
exit(0);
}
结果:
如果想程序每2秒都定时一下,这样实现也很简单,在处理定时信号的函数中再次定时2秒。
代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
void sig_handler(int num)
{
printf("receive the signal %d.\n", num);
alarm(2);
}
int main()
{
signal(SIGALRM, sig_handler);
alarm(2);
while(1){
pause();
printf("pause is over.\n");
}
exit(0);
}
结果:
代码托管
上周考试错题总结
- 错题1:C语言中,字符串被编码为一个以0结尾的字符数组。
A .正确
B .错误
-
分析:正确。这里分析C语言三个结束符有什么不同?( EOF ‘\0’ '\n')
EOF(End of file)是C/C++里面的宏定义,具体定义式是#define EOF -1,表示的是文件的结束标志,值等于-1,一般用在文件读取的函数里面,比如fscanf fgetc fgets等,一旦读取到文件最后就返回EOF标志并结束函数调用。'\0'是转义字符,值等于0,主要用在C风格字符串的末尾,表示字符串结束标志。通常用在和字符串相关的函数里面,如strcmp strcpy等会用到它。
'\n'表示换行符,通常用作一些读取函数的读取结束标志,比如scanf,getchar(),gets()等,一旦遇到'\n'就结束读取并返回。
-
错题2:下面和代码可移植性相关的C语言属性有()
A .#define
B .typedef
C .sizeof()
D .union
- 分析:#define可以定义宏使得变量可移植,typedef可以使得类型可移植,sizeof()使得不同类型长度可移植。
其他(感悟、思考等,可选)
对每一个函数进行代码测试能更好地帮助理解和掌握。
学习进度条
代码行数(新增/累积) | 博客量(新增/累积) | 学习时间(新增/累积) | 重要成长 | |
---|---|---|---|---|
目标 | 5000行 | 30篇 | 400小时 | |
第一周 | 65/65 | 1/1 | 10/10 | |
第三周 | 120/185 | 2/3 | 15/15 | |
第五周 | 375/560 | 3/6 | 14/29 | |
第六周 | 287/847 | 1/7 | 14/43 |
尝试一下记录「计划学习时间」和「实际学习时间」,到期末看看能不能改进自己的计划能力。这个工作学习中很重要,也很有用。
耗时估计的公式
:Y=X+X/N ,Y=X-X/N,训练次数多了,X、Y就接近了。
-
计划学习时间:14小时
-
实际学习时间:14小时
-
改进情况:
(有空多看看现代软件工程 课件
软件工程师能力自我评价表)