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
", getpid(), getppid());
        }else
        {
            printf("父进程工作:PID=%d,PPID=%d,子进程PID=%d
", getpid(), getppid(),id);
            sleep(5);
        }
    }
}

测试结果:

  • exit系统调用

    • 函数作用:终止发出调用的进程

    • 形式:voidexit(int status);

    • 说明

      1. exit返回信息可由wait系统函数获得
      2. 如果父进程先退出子进程的关系被转到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
", getpid(), getppid());
            sleep(20);
            printf("此时子进程:PID=%d,PPID=%d
", getpid(), getppid());
        }else
        {
            printf("父进程工作:PID=%d,PPID=%d,子进程PID=%d
", getpid(), getppid(),id);
            sleep(5);
            exit(3);
        }
    }
    return 0;
}

测试结果:

  • exec系统调用

    • 函数作用:以新进程代替原有进程,但PID保持不变

测试代码1:

#include<stdio.h>
#include<unistd.h>

int main()
{
printf("这是第一个进程PID=%d
",getpid());
execv("exec2",NULL);
printf("asa");
return 0;
}

测试代码2:

#include<stdio.h>
#include<unistd.h>

int main()
{
printf("这是第二个进程PID=%d
",getpid());
}

测试结果:

  • wait系统调用

    • 函数作用:父进程与子进程同步,父进程调用后,进入睡眠状态,直到子进程结束或者父进程在被其他进程终止。

    • 形式:pid_twait(int *status) pid_t waitpid(pid_t pid ,int *status,int option)

    • 参数:status:exit是设置的代码
      pid:进程号
      option: WNOHANG|WUNTRACED

      WNOHANG:,即使没有子进程退出,它也会立即返回,不会像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
", name, val);  
   
    //覆盖写入环境变量  
    setenv(name, "I amsure of that I will get it", 1);  
    printf("No.2%s=%s
", name, val);  
      
    val = getenv(name);  
    printf("No.3%s=%s
", name, val);  
      
    //删除一个环境变量  
    int ret =unsetenv("ABC");  
    printf("ret =%d
",ret);  
      
    val = getenv(name);  
    printf("No.3 %s=%s
",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
",ac);  
    printf("args are:
");  
    for(i=0;i<ac;i++)  
        printf("args[%d]=%s
",i,av[i]);  
    fprintf(stderr,"This message is sent to stderr.
");  
    return 0;  
}  

结果:

3.管道

管道创建成功以后,创建该管道的进程(父进程)同时掌握着管道的读端和写端。为实现父子进程间通信呢,通常可以采用如下步骤:

  1. 父进程调用pipe函数创建管道,得到两个文件描述符fd[0]、fd[1]指向管道的读端和写端。
  2. 父进程调用fork创建子进程,那么子进程也有两个文件描述符指向同一管道。
  3. 父进程关闭管道读端,子进程关闭管道写端。父进程可以向管道中写入数据,子进程将管道中的数据读出。由于管道是利用环形队列实现的,数据从写端流入管道,从读端流出,这样就实现了进程间通信。

教材学习中的问题和解决过程

  • 问题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.
", num);
}
 
int main()
{
    signal(SIGALRM, sig_handler);
 
    alarm(2);
 
    while(1){
        pause();
        printf("pause is over.
");
    }
 
    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.
", num);
    alarm(2);
}
 
int main()
{
    signal(SIGALRM, sig_handler);
    alarm(2);
    while(1){
        pause();
        printf("pause is over.
");
    }
    exit(0);
}

结果:

代码托管

上周考试错题总结

  • 错题1:C语言中,字符串被编码为一个以0结尾的字符数组。

A .正确
B .错误

  • 分析:正确。这里分析C语言三个结束符有什么不同?( EOF ‘’ ' ')
    EOF(End of file)是C/C++里面的宏定义,具体定义式是#define EOF -1,表示的是文件的结束标志,值等于-1,一般用在文件读取的函数里面,比如fscanf fgetc fgets等,一旦读取到文件最后就返回EOF标志并结束函数调用。

    ''是转义字符,值等于0,主要用在C风格字符串的末尾,表示字符串结束标志。通常用在和字符串相关的函数里面,如strcmp strcpy等会用到它。

    ' '表示换行符,通常用作一些读取函数的读取结束标志,比如scanf,getchar(),gets()等,一旦遇到' '就结束读取并返回。

  • 错题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小时

  • 改进情况:

(有空多看看现代软件工程 课件
软件工程师能力自我评价表
)

参考资料

原文地址:https://www.cnblogs.com/haoliberale/p/7750145.html