2021-9-17 Linux操作系统实验一:进程管理

Linux操作系统实验一:进程管理

题目:

【实验目的】

掌握 Linux 操作系统的使用方法;掌握 Linux 环境下 C 程序开发方法;加深对进程概念的理解,进一步认识并发执行的实质;学习通过进程执行新目标程序的方法;了解 Linux 系统中进程信号处理的基本原理;掌握 make工具的使用方法。

【实验预备内容】

(1)学习 Linux 操作系统使用方法;C 语言编程方法,Linux 下编译、调试、运行 C 程序的方法;理解进程与程序的区别,并发执行的概念。
(2)理解系统调用 fork( )的作用,分析进程的创建过程。
(3)阅读相关章节参考资料:make 的使用;进程控制;信号。加深对进程管理概念的理解。

【实验内容】

阅读基础篇、进程控制、信号等章节的相关参考资料,分析示例程序代码,熟悉实验环境,用 C 语言编写实现包含以下四个要求的程序。

  • 编译使用 make 工具;
  • 代码使用 fork 函数;
  • 代码使用 exec 函数;
  • 代码使用 signal 函数或 sigaction 函数。

学号:021900208 姓名:高旭 专业:计算机科学与技术 班级:02

一、实验环境:

Oracle VM VirtualBox、Ubuntu(64-bit)

二、实验内容:

代码部分:

gx1.c

/*gx1.c*/
/*
代码先后调用两次fork,创建两个子进程,
第一个子进程调用execv实现编译,第二个子进程调用execl执行程序
父进程在这个过程中会堵塞自己,等待捕获成为僵尸进程的子进程。
*/

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>

int main(void)
{
    
    
    char *arg1[]={
    
    "gcc","-o","gx2","gx2.c",NULL};
    pid_t pid,pr;                                               /*进程标识符*/
    if((pid=fork())<0)                                          /*创建第一个子进程*/
    {
    
    
        perror("fork");                                         /*错误处理*/
        printf("第一个子进程创建失败。");
        exit(0);
    }
    if(pid==0)
    {
    
    
        sleep(3);
        printf("第一个子进程创建成功\n");
        if((execv("/bin/gcc",arg1))==-1)                        /*转去编译gx2.c并判断是否出错*/
        {
    
    
            perror("execv");                                        /*错误处理*/
            printf("第一次尝试调用进程失败\n");
		    exit(0);
        }
    }
    if((pid=fork())<0)                                          /*创建第二个子进程*/ 
    {
    
    
        perror("fork");                                         /*错误处理*/
        printf("第二个子进程创建失败\n");
        exit(0);
    }
    else if(pid==0)
    {
    
    
        sleep(5);
        printf("第二个子进程创建成功\n");
        if((execl("/home/gaoxu/ex1/gx2","",NULL))==-1)          /*转去执行程序gx2并判断是否出错*/
        {
    
    
            perror("execl");
            printf("第二次尝试调用进程失败\n");
		    exit(0);
        }
        
    }
    else
    {
    
    
        printf("标识符为%d的父进程正在等待捕获僵尸进程\n",pid);
        pr=wait(NULL);                                          /*父进程阻塞自己,收集僵尸进程信息*/
        printf("父进程成功捕获僵尸进程,标识符为%d\n",pr);
    }
    return 0;
}

gx2.c

/*gx2.c*/
/*代码实现当没有接受到SIGINT或SIGQUIT信号时,执行死循环*/

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>

int wait_mark=1;

void hangup()
{
    
    
	while(wait_mark==1);
}

void Newhandler1(int iSignNo,siginfo_t *pSignInfo, void *pReserved)
{
    
    
    printf("接受到SIGINT信号,进入信号处理函数Newhandler1,signo:%d.\n",iSignNo);
    wait_mark=0;                                                                    /*终止死循环*/
    printf("离开信号处理函数Newhandler1,signo:%d\n",iSignNo);
}

void Newhandler2(int iSignNo,siginfo_t *pSignInfo, void *pReserved)
{
    
    
    printf("接受到SIGQUIT信号,进入信号处理函数Newhandler2,signo:%d.\n",iSignNo);
    wait_mark=0;                                                                    /*终止死循环*/
    printf("离开信号处理函数Newhandler1,signo:%d\n",iSignNo);
}

int main(void)
{
    
    
    struct sigaction act1,act2;                                                     /*包含信号处理动作的结构体*/
    act1.sa_sigaction=Newhandler1;                                                  /*指定信号处理函数*/
    act2.sa_sigaction=Newhandler2;                                                  
    act1.sa_flags=SA_SIGINFO;                                                       /*表明信号处理函数由sa_sigaction指定*/
    act2.sa_flags=SA_SIGINFO;                                                       
    sigemptyset(&act1.sa_mask);                                                     /*信号集处理函数,将act.sa_mask所指向的信号集清空,*/
    sigemptyset(&act2.sa_mask);                                                     /*即不包含任何信号*/
    sigaction(SIGINT,&act1,NULL);                                                   /*注册SIGINT信号*/
    sigaction(SIGQUIT,&act2,NULL);                                                  /* 注册SIGQUIT信号*/
    hangup();                                                                       /*等待信号*/
    pid_t pid=getpid();                                                             /*获取进程标识*/
    printf("标识符为%d的子进程已经被杀死\n",pid);
    exit(0);
    return 0;
}

结果截图:

在这里插入图片描述

三、实验总结:

1.程序思想:

流程图:

在这里插入图片描述
如上图,本次实验编写了gx1.c与gx2.c文件,

①gx1.c中使用fork函数,sleep函数,exec函数族,wait函数

实现了
(1)fork生成一个子进程,该子进程调用execv函数实现对gx2.c的编译
(2)再次fork生成一个子进程,调用execl函数实现对gx2程序的运行,其中使用sleep函数实现同步,避免出现先调用运行程序命令再调用gcc编译命令。
(3)父进程使用wait函数等待第一个子进程生命周期结束,捕获僵尸进程。

②gx2.c中使用sigaction函数,定义hangup死循环函数、Newhandler1和Newhandler2信号处理函数。

实现了
(1)在没有收到SIGINT信号与SIGQUIT信号时,执行hangup函数进入死循环。
(2)接受到SIGINT信号或SIGQUIT信号,信号处理函数将死循环参数清零,杀死进程。

2.本次实验自学的知识:

①fork函数

②exec函数族

③wait函数

④signal函数

⑤sigaction函数

⑥make工具

⑦Linux的并发执行

⑧Linux的进程管理与通信

3.遇到的问题与解决:

①将Makefile.txt工具拖入虚拟机,使用make命令出现报错make: *** No targets specified and no makefile found. Stop.

解决:通过查询资料得知,该报错是因为make命令找不到名称为Makefile或是makefile的文件。
首先尝试指定文件名为Makefile再次报错,猜测可能是txt文件不可以做makefile文件。
最后使用vi Makefile命令直接在Linux虚拟机中编写Makefile文件。

②使用exec函数不能实现编译其他.c文件并执行。

解决:查阅资料得知,
调用gcc编译命令pathname路径应该指定为根目录gcc文件所在位置,本次实验为/bin/gcc。
使用execl调用可执行程序应该给出完整的全路径:/home/gaoxu/ex1/gx2,并且即便该程序无参数也不能直接使用NULL结尾,应该在在NULL前加入“”空字符。

③execl调用程序gx2,但gx2无法接受到键盘给出的ctrl+c或ctrl+\的信号。

解决:在长时间尝试无果后询问老师,猜测因为键盘触发的ctrlc信号或ctrl\信号只能发给当前运行的前台进程,分析可能原因是不是程序中调用的程序会转为后台运行中,使用ps可知gx2确实已经在系统中运行,在终端使用kill命令发送SIGINT信号或SIGQUIT信号,gx2进程也能正常被杀死。

猜你喜欢

转载自blog.csdn.net/ShakingSH/article/details/120447901