操作系统实践(三)

  本次实践的内容是关于进程。涉及进程的创建、执行、等待、消除返回。进程是并发执行且相互没有干扰的。对进程内容的保存用PCB(Process Control Block)结构体。本次实验涉及到与进程相关的四个系统调用如下:

fork() // 创建进程
exec() // 进程执行
wait() // 进程等待
exit() // 进程退出

  下面就来详细的介绍一下四个系统调用(还是给出老师的课程图了,太全面了~)

fork()

在这里插入图片描述
  fork的返回值大致分两种:0和非0。0就是子进程,通过返回值就可以区分子进程和父进程从而进行对应的操作。

exec()

  exec()实现的时候有两种:execlp、execvp。这两种的底层实现不太一样,lp的代表list链表,vp代表vector数组。

在这里插入图片描述
在这里插入图片描述

  其实每个里面还可以分为两类:带不带p。比如链表中其实有两种实现:execl,execlp。它们两个的区别如下:
在这里插入图片描述
  可以看的出来,带p的应用更加广泛。所以此处只举了带p的例子。

exit()

在这里插入图片描述

wait()

在这里插入图片描述
  每节课都有作业,这个当然不能忘啦!
在这里插入图片描述
  这个题目其实就是创建子进程实现,主函数里的printf是父进程。每个mysys要想实现就要创建并发的子进程。不创建子进程的话三个横线就会先输出。这个代码按照老师的要求,写了三版,每一版都是改进。如果着急看代码可以直接跳过最后,但我把过程记录下来,便于以后复习。感兴趣的也可以看看。

第一版

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


void mysys(const char *command) {
    
    
	pid_t  pid;
	if (command == NULL)
		return ;
	if ((pid = fork()) < 0) {
    
    
		/*
			pid < 0代表进程创建失败,可做相应处理,但此处未做 
		*/ 
	}
	else if (pid == 0) {
    
    
		/*
			pid为0代表,创建进程为子进程,可实现执行命令功能 
		*/
		// execl的第一个参数传入执行命令路径,sh代表命令,-c代表之后开始传入参数 
		execl("/bin/sh", "sh", "-c", command, NULL);
		// exit退出进程,退出码为127 
		exit(127);
	}
	else {
    
    
		/*
			这种情况是父进程本身,为了让main中父进程的printf不连续执行,用sleep做延时,给子进程执行时间 
		*/ 
		sleep(1);
	}
	return ;
}

int main() {
    
    
	printf("----------------------\n");
	mysys("echo Hello world");
	printf("----------------------\n");
	mysys("ls /");
	printf("----------------------\n");
	return 0;
}

  这个第一版呢,可以实现功能。但是调用的是shell的功能,相当于还是一个上层的封装。shell这个命令可以帮助进行字符串的切割,切分出路径、命令类型、命令参数。所以下一版的改进,主要是针对于字符串切割手动实现。

第二版

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

void mysys (char *command) {
    
    
	int num = 0;
	char op[512];
	char arg[512];
	int a = 0;
	int b = 0;
	for (;command[a] != ' '; a++) {
    
    
		op[a] = command[a];
	}
	op[a] = '\0';
	a++;
	for (;command[a] != '\0'; a++, b++) {
    
    
		arg[b] = command[a];
	}
	arg[b] = '\0';

	pid_t  pid;
	if (command == NULL)
		return ;
	if ((pid = fork()) < 0) {
    
    

	}
	else if (pid == 0) {
    
    
		execlp(op, op,arg, NULL);
		exit(127);
	}
	else {
    
    
		sleep(1);
	}
	return ;
}

int main() {
    
    
	printf("----------------------\n");
	mysys("echo Hello world");
	printf("----------------------\n");
	mysys("ls /");
	printf("----------------------\n");
	return 0;
}

  第二版呢看上去也能运行成功,结果跟想象中一样。但是其实是不对的,这里面调用的execlp是没办法处理不定参数的问题的。输入多个参数都会被划到arg这个变量中去。ls 和 echo都没问题,但对于cp这种就会报错找不到第二个参数了。所以最终一版使用execvp,能够实现不定数组功能,具体代码如下:

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

void mysys(char *command) {
    
    

	char *argv[512]; // 这里必须要用动态指针数组,因为execvp最后有一个NULL作为结束标志,静态的二维数组是无法实现的。
	int num = 0;
	char temp[512];
	for (int i=0; command[i]!='\0'; i++) {
    
    
		int index = 0;
		while (command[i]!='\0' && command[i] != ' ') {
    
    
			temp[index++] = command[i++];
		}
		temp[index] = '\0';
		if (command[i] == '\0')
			break;
		argv[num] = (char *)malloc(strlen(temp));
		strcpy(argv[num], temp);
		num++;
	}
	argv[num] = (char *)malloc(strlen(temp));
	strcpy(argv[num], temp);
	num++;
	argv[num] = NULL;
	pid_t  pid;
	if (command == NULL)
		return ;
	if ((pid = fork()) < 0) {
    
    

	}
	else if (pid == 0) {
    
    
		execvp(argv[0], argv);
		exit(127);
	}
	else {
    
    
		sleep(1);
	}
	return ;
}

int main() {
    
    
	printf("----------------------\n");
	mysys("echo Hello world");
	printf("----------------------\n");
	mysys("ls /");
	printf("----------------------\n");
	mysys("cp mysys.c mysys1.c");
	return 0;
}

  第三次结束了,但其实还是留下了好多问题一知半解。后面再陆续学习补充吧!

因作者水平有限,如果错误之处,请在下方评论区指正,谢谢!

Guess you like

Origin blog.csdn.net/gls_nuaa/article/details/115876728