linux多进程编程(一)

最近因为一个偶然的原因要在linux平台上做一个模拟实验,其中要涉及到多进程的编程,所以特此写一系列的博客来总结一下多进程的编程问题。这一系列文章只适合了解linux,并且在Linux下使用C语言写过程序的人。

首先进程的概念就不多说了,所有大凡上过操作系统课的人一定对那一堆堆的图示永生难忘。

使用进程前,了解一个简单的linux命令“ps”命令,它用来显示当前系统进程:

ps效果:

linux多进程编程(一)_第1张图片

ps -ef效果:
linux多进程编程(一)_第2张图片

ps ax效果:
linux多进程编程(一)_第3张图片

此外还有ps -l,nice,renice命令及参数,具体的使用用man查询就可以了

一.启动一个新进程:

#inlcude<stdlib.h> system(char *);这个函数可以执行一个新的shell命令,只是启动的新进程将会取代当前进程,也即如果system启动成功,则其之后的代码将不会被运行。下面是一个system的例子:

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

void main(){
	int result_code;
	printf("Running ps with system\n");
	result_code = system("ps ax &");
	if(result_code == 127){printf("can't start a shell");}
	if(result_code == -1){printf("start error");}
	else{printf("running success");}
}

运行效果如图所示:
linux多进程编程(一)_第4张图片

二.exec系列函数
#include<unistd.h>

char **environ;
int execl(const char *path,const char *arg0,...,(char *)0);
int execlp(const char *file,const char *arg0,...,(char *)0);
int execle(const char *path,const char *arg0,...,(char *)0,char *const envp[]);

int execv(const char *path,char *const argv[]);
int execvp(const char *file,char *const argv[]);
int execve(const char *path,char *const argv[],char *const envp[]);

exec系列函数的作用是替换进程的映像,并且其会接受当前进程的所有资源,包括已经打开的文件句柄等等。这一系列函数可以分为两大类,包括以l系列,其接受一个指令路径或者目录,以可变参数,或者一个环境变量参数变,但所有参数均需以空指针结尾;第二个系列是v系列,与l系列类似,只是其将可变参数变以数组的形式传递给函数

一个exec的示例:

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

void main(){
	printf("exec series functions test\n");
	printf("execve\n");

	char *const ps_argv[] = {"ps","ax",0};
	char *const ps_envp[] = {"PATH=/bin:/usr/bin","TERM=console",0};

	execv("/bin/ps",ps_argv);
	printf("Done.\n");
}

运行效果如图:
linux多进程编程(一)_第5张图片

输入输出重定向:因为exec执行后会保留之前进程的已打开文件句柄,利用这个特性,我们可以写一个小的程序,这种思想是你可以包装一个你根本就不知道源代码,只知道功能的程序。比如下面这个upper.c将文件改为大写输出,useupper.c包装它,并为它提供一个文件名参数

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

int main(){
	int ch;
	while((ch = getchar())!=EOF){
		putchar(toupper(ch));
	}
	exit(0);
}

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

int main(int argc,char *argv[]){
	char *filename;

	if(argc != 2){
		perror("arguments number less than 2\n");
		exit(1);
	}

	filename = argv[1];

	if(!freopen(filename,"r",stdin)){
		perror("file reopen failed \n");
		exit(2);
	}

	execl("./upper","upper",0);

	perror("program failed\n");
	exit(3);
}

效果如图:
linux多进程编程(一)_第6张图片

三.复制进程映像,fork()

接下来这个方法就是大名鼎鼎的fork()函数,相信所有使用过,甚至看过linux系列书籍的人都知道这个函数。它会将当前进程分裂成两个完全一样,但是完全独立的进程。有些书上会说用它来创立了新进程,但是我觉得用分裂来得更贴切,就像生物学中无丝分裂一样,它生成的两个进程除了资源和ID不同外,其他都一样

#include<sys/types.h>#include<unistd.h> pid_t fork(void);错误时返回-1;在父进程中返回子进程ID;子进程中返回0,可以依次来区分当前在哪个进程中.

下面是一个使用fork打印父子进程的示例

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

int main(){
	pid_t pid;
	char *message;
	int n;

	printf("fork \n");
	pid = fork();

	switch(pid){
		case -1:perror("fork error");
			break;
		case  0:message="this is child";
			n=8;
			break;
		default:message="this is parent";
			n=3;
			break;
	}

	for(;n>0;n--){
		puts(message);
		sleep(1);
	}
	exit(0);
}

效果如图:
linux多进程编程(一)_第7张图片

分裂后父子进程是彼此相互独立运行的,所以要想让父进程等待子进程,只需使用wait()系统调用:
#include<sys/types.h>#include<sys/wait.h> pid_t wait(int *stat_val);有关stat_val的详细值建议去百度官网看api文档最好

一个简单的示例如下:

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

int main(){
	pid_t pid;
	char *message;
	int n;
	int exit_code;

	printf("wait test\n");
	pid = fork();

	switch(pid){
		case -1:perror("fork failed");
			exit(1);
		case  0:message = "this is child";
			n = 8;
			exit_code = 37;
			break;
		default:message = "this is parent";
			n= 5;
			exit_code = 0;
			break;
	}

	for(;n>0;n--){
		puts(message);
		sleep(1);
	}

	if(pid != 0){
		int stat_val;
		pid_t child_pid;

		child_pid = wait(&stat_val);

		printf("waiting for child to finish\n");
		if(WIFEXITED(stat_val)){
			printf("child exit code is %d\n",WEXITSTATUS(stat_val));
		}else{
			printf("child exit failed\n");
		}
	}

	exit(exit_code);
}

效果如图:
linux多进程编程(一)_第8张图片

四.僵尸进程

所谓僵尸进程,是指使用fork后,子进程先于父进程结束,但是因为父子进程间依然有关系,那么子进程实际上不会真正意义上终结,如果查看当前进程表,会发现该进程依然存在,且会被标记为<defunct>/<zombies>。人为产生僵尸进程也并不那么轻松,但是在上面那个等待例子中,如果让子进程先于父进程退出,在父进程结束前调用ps -al命令后就会发现有这么一个僵尸进程,实现方法如下:

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

int main(){
	pid_t pid;
	char *message;
	int n;
	int exit_code;

	printf("wait test\n");
	pid = fork();

	switch(pid){
		case -1:perror("fork failed");
			exit(1);
		case  0:message = "this is child";
			n = 3;
			exit_code = 37;
			break;
		default:message = "this is parent";
			n= 12;
			exit_code = 0;
			break;
	}

	for(;n>0;n--){
		puts(message);
		if(n == 1 ){printf("child finished\n");system("ps -al");}
		sleep(1);
	}

	if(pid != 0){
		int stat_val;
		pid_t child_pid;

		child_pid = wait(&stat_val);

		printf("waiting for child to finish\n");
		if(WIFEXITED(stat_val)){
			printf("child exit code is %d\n",WEXITSTATUS(stat_val));
		}else{
			printf("child exit failed\n");
		}
	}

	exit(exit_code);
}

效果如图:

linux多进程编程(一)_第9张图片

你可能感兴趣的:(linux,System,进程,fork,exec)