Linux-进程控制

进程创建

fork会在已存在的进程中创建一个新进程出来,并且当前进程作为父进程,新创建出来的进程作为子进程。当进程调用fork之后,内核会分配新的内存块和数据给子进程,并且将父进程数据内容拷贝给子进程,然后将子进程添加至系统就绪队列,系统调用fork完成之后开始返回,但是这里fork将会有两个返回值父进程返回子进程的进程ID,子进程返回0,通常用if语句来做分流。

pid_t fork(void);

那么在fork调用之后,我们是如何知道fork返回两个返回值?我们可以先来看一段代码:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int main()
{
	printf("Before:pid is %d\n", getpid());
	pid_t pid = fork();
	if(pid < 0){
		perror("fork");
		exit(1);
	}
	printf("After:pid is %d, fork return %d\n", getpid(), pid);
	sleep(1);
	return 0;
}

通过执行结果可以明显看出来父进程子进程以及父子进程的返回值, 但是子进程是按照父进程的模板复制出来的,为什么只打印了一次Before,这就是我们接下来说的,父子进程虽然有同一份代码,但是并不是父子进程都要执行全部,fork之前父进程独立执行,fork之后两个分别执行。谁先执行完全由调度器决定

当然,fork调用也不一定每次都成功,可能原因就是系统中的进程数太多,还有就是实际用户的进程数超过了限制。当然,创建进程不只有fork一中方法,也可以通过vfork来创建进程,vfork创建的子进程和父进程共享地址空间,并且保证子进程先执行,父进程之后执行。

进程等待

当子进程退出,父进程不管不顾,就有可能造成僵尸进程进而造成内存泄露,另外进程一旦变成僵尸进程,就无法通过kill来杀死它,还有父进程交给子进程的任务完成的如何,父进程需要知道子进程的结果如何,父进程可以通过进程等待的方式来获取子进程的退出信息,回收子进程资源。

pid_t wait(int *status);

返回值:
    调用wait成功返回被等待的进程,失败返回-1,
参数:
    输出型参数,获取子进程的退出状态,不关心可设置为NULL

 wait可以胜任等待子进程的任务,但是我们用的更多的是waitpid:

pid_t waitpid(pid_t pid, int *status, int options);

 waitpid的返回值和wait函数的返回值是一样的,但是如果设置了选项WNOHANG,waitpid发现没有已退出的子进程可以收集,就会返回0。

pid

pid为-1,等待任意一个子进程,pid>0,等待进程ID与pid相等的进程

status

wait和waitpid都有status参数,这个参数是一个输出型参数,有操作系统填充,如果填NULL,则表示不关心子进程的退出状态,否则,操作系统会将子进程的退出状态填充至status返回给父进程。但是status不能当一个普通的整形来看待,可以当做位图来看,此处不研究高16位,只看低16位。

这样用户就可以根据status来获取子进程的退出状态。 

options

options设置WNOHANG,在说WNOHANG之前,可以先来了解两个概念:阻塞和非阻塞。wait默认是阻塞的,即调用wait之后如果返现没有退出的子进程,那么父进程就在此等待子进程退出,进入睡眠状态(可中断),此时就是阻塞式等待,但是设置WNOHANG之后,会设置父进程为非阻塞等待,所以没有已退出的子进程,waitpid立即返回。

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

int main()
{
	pid_t pid;
	
	if((pid = fork()) == -1){
		perror("fork");
		exit(1);
	}
	if(pid == 0){ //child
		sleep(20);
		exit(2);
	}
	else{ //father
		int st;
		int ret = wait(&st);
		//int ret = waitpid(pid, &st, WNOHANG);
		
		if(ret > 0 && (st & 0x7F) == 0){ //normal
			printf("child exit code:%d\n", (st>>8)&0xFF);
		}
		else if(ret > 0){ //signal
			printf("sig code:%d\n", st&0x7F);
		}
		else if(ret == 0){
			printf("非阻塞,没有子进程退出!\n");
		}
	}
}

子进程正常返回,调用exit

子进程被kill信号所杀,再打开一个终端我们可以用kill杀死子进程

 

ctrl+c终止进程

 

进程程序替换

父进程创建出子进程之后希望完成一系列动作,所以往往子进程需要其他的程序来帮助自己完成,但是子进程没有能力独立去完成,这就需要进行程序替换。子进程调用exec函数来执行另一个程序,调用exec时,进程的用户空间代码和数据完全被新程序替换,从新的程序开始执行,调用exec并不会创建新的进程,所以调用exec前后进程id并未改变。

替换函数

int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg, ..., char * const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[],char *const envp[]);

观察函数名称就可以大概知道用法,l(list)表示采用列表,v(vector)采用数组,p(path)自动搜索环境变量,e(env)自己维护环境变量。

进程退出

 进程退出有三种状态,正常退出结果正确,正常退出结果不正确,异常退出,在命令行下可以通过echo $?来查看退出码,通常正常退出有三种方式,return返回,调用exit,调用_exit,异常退出ctrl+c。

#include <unistd.h>
void _exit(int status);

 虽然status是int,但是仅有低8位可以被父进程所用,所以当exit(-1)时,就会发现返回值是255

#include <stdlib.h>

void exit(int status);

_exit是系统调用,exit是库函数,调用exit虽然会使用系统调用,但是在系统调用之前,exit会关闭所有打开的文件描述符,所有的缓存数据都被写入,最后进程退出。

执行exit就相当与执行return,main函数运行时会将main的返回值作为exit的参数。

小案例

我们学了从进程的创建到终止一系列操作,就会发现shell的基本原理和进程的一生有着及其相似的地方:

1.获取命令

2.解析命令

3.bash创建子进程(fork)

4.替换子进程(execvp)

5.父进程等待子进程退出(wait)

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

void Pares_cmd(char *buf, char *out_cmd[])
{
	int i = 0;
	int j = 0;
	while(buf[i]){
		while(buf[i] == ' '){
			i++;
		}
		out_cmd[j++] = buf + i;
		while(buf[i] != ' ' && buf[i] != 0){
			i++;
		}
		buf[i++] = '\0';
	}
	out_cmd[j] = NULL;
}

void do_exec(char *out_cmd[])
{
	pid_t pid = fork();
	if(pid < 0){
		perror("fork");
		return;
	}
	else if(pid == 0){
		execvp(out_cmd[0], out_cmd);
		exit(1);
	}
	else{
		wait(NULL);
	}
}

int main()
{
	int i;
	while(1){
		char buf[100] = {0};
		char *out_cmd[100] = {0};
		printf("[zmy@zmy]$ ");
		gets(buf);
		Pares_cmd(buf, out_cmd);
		do_exec(out_cmd);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/yikaozhudapao/article/details/83505555