Linux进程控制编程实验

任务1:编写一个进程创建实验程序task51.c,创建如图所示的进程族亲结构,其中p1是程序启动时由加载程序创建第一个进程。各进程的输出信息分别如下:
p1:I am father process p11: 当前时间是< 年 月 日 时 分 秒> p12: I am young
brother process p121:我的学号是<您的学号xxx> p122:我的姓名是<您的姓名xxx>
提示:获得当前系统时间的函数是 time_t time(time_t *t);
将time_t类型的的时间转换成时间字符串的函数是:char *ctime(const time_t *timep);
其使用方法见教材3.2.4 思考:如何验证产生的进程符合如图的族亲关系?

在这里插入图片描述
思考题:使用getpid()和getppid()函数结合使用就可以验证设计的族亲结构是否准确,其中getpid是得到自己的pid,getppid是得到父进程的pid。

任务2:参考教材shellex.c代码,实现一个简单的交互式shell程序task52.c,具备的功能至少有:
(1)打印提示符%;获取用户输入指令;解析指令;寻找命令文件,执行指令 (1)显示命令提示符%;
(2)获取用户输入指令;解析指令;寻找命令文件,执行指令; (3)前一个命令完成后才继续显示命令提示符%,等待用户输入下一条命令;
(3)如果输入“exit”或“log out”,则退出shell. (4)允许命令参数间有多个空格,也允许命令前后有空格
(5)支持输出重定向和管道功能。 提示:可参考上一次实验分解命令行的代码

任务3:写一个信号机制实验程序task53.c,由父进程创建两个子进程,通过终端输入Crtl+\组合键向父进程发送SIGQUIT软中断信号或由系统时钟产生SIGALRM软中断信号发送给父进程;父进程接收到这两个软中断的其中某一个后,向其两个子进程分别发送整数值为SIGUSR1
(10)和SIGUSR1 (12)软中断信号,子进程获得对应软中断信号后,分别输出“<进程PID> killed by
<信号编号>”后,终止运行;父进程调用wait()函数等待两个子进程终止,然后自我终止。

任务4(可选):写一个子进程管理程序task54.c,借鉴图5-34的procmask2.c方法管理子进程,父进程循环读取用户输入的操作命令,创建子进程、显示相关信息和终止子进程等。具体用户命令为:

  1. 命令1:功能是创建一批子进程,格式为“create <进程数>” ,命令执行成功后,显示所有新创建子进程PID。比如“create
    10”表示创建10个子进程,子进程执行的代码可以为:“while(1){};exit(100);”
    2)命令2:终止一批子进程,格式为“kill …”(如“kill 123 456
    789”为终止PID号为123、456、789的三个子进程),子进程显示“killed by
    parent”后终止,父进程通过SIGCHLD信号处理程序等待子进程终止,显示终止的子进程PID。
    3)命令3:显示当前子进程列表,命令格式为:“ps -u”
    4)命令4:父进程终止命令,格式为“exit”,当所有子进程都结束后,才允许执行该命令。

提示:可用fgets函数将整个命令作为一行文本输入,再调用库函数(如strtok或strchr)将各个命令参数提取出来。

任务1:

#include"wrapper.h"
#include<stdio.h>
#include<time.h>
int main() {                         //main函数就是p1进程
	pid_t  pid1, pid2,pid3;
	printf("I am father process\n");      //p1进程的输出语句
	pid1 = fork();                    //p进程创建第一个子进程p11
	if (pid1 == 0) {
		time_t timer;
		time(&timer);
		printf("p1:%s\n", ctime(&timer));
		exit(0);
	}
	else {
		pid2 = fork();                 //由p进程创建子进程p12
		if (pid2 == 0) {
			printf("p12: I am young brother process\n");
			pid3 = fork();
			if (pid3 == 0) {            //由p12进程创建子进程p121
				printf("p121:我的学号:201841413302\n");
				exit(0);
			}
			pid3 = fork();
			if (pid3 == 0) {            //由p12进程创建子进程p122
				printf("p122:我的姓名:陈霖\n");
				exit(0);
			}
			exit(0);
		}
		exit(0);
	}

}

思考题:使用getpid()和getppid()函数结合使用就可以验证设计的族亲结构是否准确, 其中getpid是得到自己的pid,getppid是得到父进程的pid。

任务2:

#include"wrapper.h"
#include<stdio.h>
void execute(char* cmdline);
int builtin_command(char** argv) ;
int parseline(char* buf, char** argv);
void MYdup1();
void MYdup2();
int main() {
	char cmdline[MAXLINE]; /* 命令行缓冲区 */
	while (1) {
		printf("%%");
		fgets(cmdline, MAXLINE, stdin);/* 读取命令行 */
		if (feof(stdin))
			exit(0);
		execute(cmdline);/* 执行命令 */
	}
}
void MYdup1(){	//重定向到一个data.txt文件路径下面
	int fd;
	fd = open("data.txt",O_CREAT|O_WRONLY|O_APPEND,0777);
   dup(1);//先把标准输出备份好,等待未来重定向恢复
	close(1);
	dup(fd);
}
void MYdup2(){	//路径恢复,标准输出
	close(1);
	dup2(4,1);
}
void execute(char* cmdline) {
	char* argv[MAXLINE]; /*execve()参数表 */
	char buf[MAXLINE]; /* 保存修改后的命令行 */ 
	int bg; /* 是否在后台执行 */ 
	pid_t pid; /* 子进程PID*/ 
	strcpy(buf, cmdline); 
	bg = parseline(buf, argv);/* 解析命令行 */ 
	if (argv[0] == NULL) return; /* 如果第1个参数为空,则忽略命令 */
	if (!builtin_command(argv)) {
		if ((pid = fork()) == 0) { /* 创建子进程 */ 
			if (execvp(argv[0], argv) < 0) { 
				printf("%s:Command not found.\n", argv[0]); exit(0); 
			} 
		}
		if (!bg) { /* 前台执行 */ 
			int status;

			if (waitpid(pid, &status, 0) < 0) 
				perror("waitpid error");
		}
		else 
			printf("%d%s", pid, cmdline);
	} return;
}
/* 判断和执行内置命令 */ 
int builtin_command(char** argv) {
	if (!strcmp(argv[0], "exit")) /* 内置命令exit */
		exit(0);
	if(!strcmp(argv[0],"log out"))/* 内置命令logout */
		exit(0);
	if(!strcmp(argv[0],"MYdup1")){/* 重定向 */
		MYdup1();
       return 1;
	}
	if(!strcmp(argv[0],"MYdup2")){/* 路径恢复 */
		MYdup2();
       return 1;
	}
	if (!strcmp(argv[0], "&")) /* 忽略由&开始的命令串 */
		return 1;
	return 0; /* 非内置命令 */
}
int parseline(char* buf, char** argv) {
	char* delim; /* 指向第1个分隔符 */
	int argc; /* 字符串数组args中命令行参数个数 */
	int bg; /* 后台作业 */
	buf[strlen(buf) - 1] = ' '; /* 用空格替换行末换行符 */
	while (*buf && (*buf == ' '))/* 删除行首空格 */
		buf++;
	/* 创建 argv数组 */
	argc = 0;
	while ((delim = strchr(buf, ' ')))
	{
		argv[argc++] = buf;
		*delim = '\0';
		buf = delim + 1;
		while (*buf && (*buf == ' ')) /* 忽略空格,查找下一个参数起始位置 */
			buf++;
	}
	argv[argc] = NULL;
	if (argc == 0) /* 忽略空行*/
		return 1;
	/* 命令是否应在后台执行 */
	if ((bg = (*argv[argc - 1] == '&')) != 0)
		argv[--argc] = NULL;
	return bg;
}

任务3

#include <stdio.h> 
#include <stdlib.h> 
#include <signal.h> 
#include <unistd.h>  
#define sec 5  
void waiting();
void stop();
int wait_mark;
int main() {
	int p1, p2;  /*定义两个进程号变量*/
	while ((p1 = fork()) == -1);  /*循环创建进程至成功为止*/
	if (p1 > 0) {
		while ((p2 = fork()) == -1);  /*循环创建进程至成功为止*/
		if (p2 > 0) {
			wait_mark = 1;
			alarm(sec);/*5秒后执行*/
			signal(SIGQUIT, stop);
			signal(SIGALRM, stop);/*父进程接受两种信号,均用stop去响应*/
			waiting();
			kill(p1, 10);/*向p1发送软中断信号10*/
			kill(p2, 12);/*向p2发送软中断信号12*/
			wait(0);
			wait(0);/*等待两个子进程终止后才进行下一条代码*/
			printf("parent process is killed!\n");
			exit(0);/*自我终止*/
		}
		else {
			signal(SIGQUIT, SIG_IGN);/*忽略信号*/
			signal(SIGALRM, SIG_IGN);/*忽略信号*/
			wait_mark = 1;
			signal(12, stop); /*接收到软中断信号12,转stop*/
			waiting();/*在wait置0前,不可往下执行*/
			lockf(1, 1, 0);  /*加锁*/
			printf("进程%d is killed by 信号12!\n", getpid());
			lockf(1, 0, 0); /*解锁*/
			exit(0); /*子进程2退出*/
		}
	}
	else {
		signal(SIGQUIT, SIG_IGN);/*忽略信号*/
		signal(SIGALRM, SIG_IGN);/*忽略信号*/
		wait_mark = 1; /*将等待标记置1直到中断信号刺激stop*/
		signal(10, stop);/*接收到软中断信号10,转stop*/
		waiting();  /*在wait置0前,不可往下执行*/
		lockf(1, 1, 0);
		printf("进程%d  is killed by 信号10!\n", getpid());/*接收到父进程发送信号后,父进程杀死子进程1*/
		lockf(1, 0, 0);/*解锁*/
		exit(0); /*子进程1退出*/
	}
	return 0;
}
void waiting() {
	while (wait_mark != 0);
}
void stop() {
	wait_mark = 0;
}

任务4:

#include"wrapper.h"
#include<stdio.h>
#include<string.h>
void execute(char* cmdline);/*执行命令*/
int builtin_command(char** argv);/*检查是否为用户内置命令*/
int parseline(char* buf, char** argv);/*将命令切割成标准格式的若个部分*/
void create(int num);		/*创建子进程num个*/
void Exit();
void stop();
void waitBack();
void MyKill(char** argv);
int n;
int mask;
static int count = 0;/*父进程下面的子进程数量,若为0才能调用Exit()函数*/

int main() {
	char cmdline[MAXLINE]; /* 命令行缓冲区 */
	signal(SIGCHLD, waitBack);

	while (1) {
		printf("%%");
		fgets(cmdline, MAXLINE, stdin);/* 读取命令行 */
		if (feof(stdin))
			exit(0);

		execute(cmdline);/* 执行命令 */
		//usleep(100);/*每次执行完任务,就停止100微秒*/
	}
}

void execute(char* cmdline) {
	char* argv[MAXLINE]; /*execve()参数表 */
	char buf[MAXLINE]; /* 保存修改后的命令行 */
	pid_t pid; /* 子进程PID*/
	strcpy(buf, cmdline);
	parseline(buf, argv);/* 解析命令行 */
	if (argv[0] == NULL) 
		return; /* 如果第1个参数为空,则忽略命令 */
	if (!builtin_command(argv)) {
		if ((pid = fork()) == 0) { /* 创建一个临时进程,因为调用execvp后的进程就不会回来了 */
			count++;
			if (execvp(argv[0], argv) < 0) {
				printf("%s:Command not found.\n", argv[0]);
				exit(0);/*立即放弃该临时进程*/
			}
		}
		else{
			usleep(200);
			waitpid(pid,NULL,0);
		}
	} 
}
/* 判断和执行内置命令 */
int builtin_command(char** argv) {
	if (!strcmp(argv[0], "exit")) { /* 内置命令exit */
		Exit();
		return 1;
	}

	if (!strcmp(argv[0], "create")) { /* 内置命令create */
		create(atoi(argv[1]));
		return 1;
	}
	if (!strcmp(argv[0], "kill")) { /* 内置命令kill */
		MyKill(argv);
		return 1;
	}
	if (!strcmp(argv[0], "&")) /* 忽略由&开始的命令串 */
		return 1;

	return 0; /* 非内置命令 */
}

int parseline(char* buf, char** argv) {
	char* delim; /* 指向第1个分隔符 */
	int argc; /* 字符串数组args中命令行参数个数 */
	int bg; /* 后台作业 */
	buf[strlen(buf) - 1] = ' '; /* 用空格替换行末换行符 */
	while (*buf && (*buf == ' '))/* 删除行首空格 */
		buf++;
	/* 创建 argv数组 */
	argc = 0;
	while ((delim = strchr(buf, ' ')))
	{
		argv[argc++] = buf;
		*delim = '\0';
		buf = delim + 1;
		while (*buf && (*buf == ' ')) /* 忽略空格,查找下一个参数起始位置 */
			buf++;
	}
	argv[argc] = NULL;
	n = argc;/*记录命令中出现的参数个数*/
	if (argc == 0) /* 忽略空行*/
		return 1;

	/* 命令是否应在后台执行 */
	if ((bg = (*argv[argc - 1] == '&')) != 0)
		argv[--argc] = NULL;
	return bg;
}
void create(int num) {/* 命令1:创建num个子进程 */
	int pid, i;
	if (num <= 0) {
		return;
	}
	if ((pid = fork()) > 0) {
		create(num - 1);
		count++;
		//printf("%d\n",count);      //测试
	}
	else {
		signal(10, stop);
		printf("新创建的子进程PID=%d\n", getpid());
		mask = 1;
		while (mask);/*持续循环,等待终止信号10*/
		printf("%d killed by parent\n",getpid());
		exit(100);
	}
}
void MyKill(char** argv) {
	int i = 1;
	while (i < n) {	/*向命令中出现的所有Pid进程发送信号10*/
		
		kill(atoi(argv[i]), 10);/* 10 用户自定义信号,我定义为终止信号*/
		i++;
	}
	//sleep(1); //测试
}
void Exit() {/* 命令4 */
	if (count <= 0)
		exit(0);
	else {
		printf("error!还有子进程没有终止\n");
	}
}
void stop() {/*子进程停止循环*/
	mask = 0;
}
void waitBack() {/*父进程回收子进程资源,并显示子进程的pid*/
	int a;
	while ((a = waitpid(-1, NULL, 0)) > 0) {
		printf("终止的子进程PID=%d\n", a);
		count--;
		//printf("%d\n",count); //测试
	}
}

总结

任务Value
任务一考察对进程的创建和族亲结构的掌握,一般 来说如果不太确定的话,可以在纸上做个草图,主要是熟悉fork()函数的使用。
任务二通过调用fgets函数来获取用户输入指令,然后参照shellex.c的思想,调用parseline函数将这行输入切割成多个字符串;即解析指令;然后寻找命令文件,执行指令。按照题目要求设置两个内置命令:“exit”或“log out”,接收到则退出shell。最后就是设置两个函数MYdup1和MYdup2,来支持输出重定向和管道功能。
任务三这个信号发送的章节掌握的不是很到位,所以费了不少功夫。主要是理解几个新函数,signal->指明当前进程遇到信号(参数1)时,该怎么处理(参数2)。Wait(0)->停止向下运行,等待子进程终止。Kill()->向pid(参数1)的进程发送信号(参数2),alram()->在(参数一)/秒后产生时钟信号,然后是自制函数,waiting()->持续执行,知道wait_mask变成0,而stop函数就是把wait_mask置0.
任务四(可选)最后写出来的程序还是有bug的,最严重的bug是有时候shell终端就会瘫痪不接受stdin的数据,导致命令shell无法向下运行,所以最后只好ctrl+\中断,但还是大部分时候还是可以运行的,苟蒻技拙,找了整整一天也没能发现问题所在,但总体的业务逻辑大体上是合乎情理的,争取有空时再弥补一下,唉,还是太懒了-- _- -,就怕没有有空的时候了。(不努力的苦果。。。)

ps:以上代码的头文件均有“wrapper.h”,这是一个万能头文件,如果需要可去本人的下载资源查找,但设置的0分下载被系统修改了,如果没有积分的同学可以私信我,但是作者很懒,说不准哪个时候上博客,只能说尽量吧。-_-!

  • 10
    点赞
  • 47
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值