本次实践的内容是关于进程。涉及进程的创建、执行、等待、消除返回。进程是并发执行且相互没有干扰的。对进程内容的保存用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;
}
第三次结束了,但其实还是留下了好多问题一知半解。后面再陆续学习补充吧!
因作者水平有限,如果错误之处,请在下方评论区指正,谢谢!