一、进程的创建,终止,等待
- 进程创建
创建进程主要有两个函数fork函数和vfork函数(头文件 unistd.h)。
fork函数创建进程时,它的父子进程代码共享,数据自己各自拷贝一份,也就是所谓的写时拷贝。使用fork函数有这两种情况:
父进程希望复制自己,使父子进程同时执行不同的代码段
一个进程要执行一个不同的程序
fork函数调用失败时,主要原因是系统内的进程太多或者实际用户的进程数超过了限制
vofork函数和fork函数主要的区别:
- vfork函数创建一个子进程,但子进程和父进程共享地址空间,而fork的父子进程具有独立的地址空间。
- vfork保证子进程先运行,在它调用exec或(exit)之后父进程才可能被调度运行。
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main(void){
int i = 100;
pid_t pid;
if((pid = vfork()) == -1) perror("fork"),exit(1);
if(pid == 0){//chlid
sleep(5);
i = 200;
printf("child i %d\n",i);
exit(0);
}else{//father
printf("father i %d\n",i);
}
return 0;
}
当为vfork函数时,吃了child和father相同说明他们共享地址空间,而当把vfork改为fork时,可见其child和father不相同,这就印证了fork和vfork的区别。
2.进程终止
进程终止有三种情况:
- 代码运行结束,结果正确。
- 代码运行结束,结果错误。
- 代码异常终止。
正常终止时我们可以通过 echo $? 查看进程的退出码,并且可以通过main函数返回,也就是return退出,exit、_exit函数终止。
异常终止时,可以使用ctrl+c进行信号终止。
exit函数和_exit函数(头文件 unistd.h)
_exit是强制终止,而调用exit时要做以下几步:
执行用户通过atexit或者on_exit定义的清理函数。
关闭所有打开的流,所有缓存数据均被写入。
调用_exit
3.进程等待
必要性:
- 父进程必须等待子进程,否则会使子进程进入僵尸状态,进程一旦进入僵尸状态,就会刀枪不入,难以杀掉。
- 父进程要通过等待来获取子进程的退出信息
- 正常情况下都是子进程先退出
进程等待的方法:wait 和 waitpid
wait是操作系统将子进程的退出信息拷贝给父进程,而waitpid是为了拿出子进程的退出信息。
wait和waitpid都有一个status参数,了解status可以通过下图理解(只了解status低16比特位)
当异常退出时,操作系统用信号杀死进程。core dump称为核心转储
如果在任意时刻调用wait/waitpid,子进程存在且正常运行,那么进程就可能阻塞。
因此等待又分为阻塞式等待和非阻塞式等待还有循环等待。
阻塞式等待顾名思义就是父进程要等子进程运行结束后方能运行
非阻塞式等待就是如果子进程还没有运行结束父进程就退出运行其他程序。
循环等待创建一个for循环,询问子进程是否运行结束。
进程的等待
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <error.h>
int main(void){
pid_t pid;
if((pid=fork()) == -1)
perror("fork"),exit(1);
if(pid == 0){
sleep(10);
exit(10);
}else{
int st;
int ret = wait(&st);
if(ret > 0 && (st &0X7F) == 0){//error
printf("child exit code:%d\n", (st>>8)&0xFF);
}else if(ret > 0){
printf("sig code:%d\n", st&0x7F);
}
}
}
二、简易的myshell
编写一个简易的myshell
#include <unistd.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *argv[8];
int argc = 0;
void do_parse(char *buf){
int i;
int status = 0;
for(argc = i = 0; buf[i]; i++){
if(!isspace(buf[i] && status ==0)){
argv[argc++] = buf + i;
status = 1;
}else if(isspace(buf[i])){
status = 0;
buf[i] = 0;
}
}
argv[argc] = NULL;
}
void do_execute (void){
pid_t pid = fork();
switch(pid){
case -1:
perror("fork");
exit(EXIT_FAILURE);
break;
case 0:
execvp(argv[0], argv);
perror("execvp");
exit(EXIT_FAILURE);
default:{
int st;
while(wait(&st) != pid);
}
}
}
int main(void){
char buf[1024] = {};
while(1){
printf("myshell>");
scanf("%[^\n]%*c",buf);
do_parse(buf);
do_execute();
}
}
这个简易的myshell不支持管道。
三、popen和system
相关内容可以点击此处查看