系统级程序设计-第三课


一、进程管理2

1.1exec函数族

exec函数族的功能是:根据指定的文件名或路径找到可执行文件,用该文件取代调用该函数进程中的程序,再从该文件的main()函数开始执行文件的内容。
exec函数族中包含6个函数,分别为execl()、execlp()、execle()、execv()、execvp()、execve()。他们包含在系统库unistd.h中,其声明分别如下:

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 execve(const char *path, char * const argv[], char * const envp[]);

1.2案例

test_exec.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(){//of main
    pid_t tempPid;
    tempPid=fork();
    if(tempPid == -1){//of if
        perror("fork error");
        exit(1);
    } else if(tempPid > 0) {   
        printf("parent process:pid=%d\n", getpid());
    } else {   
        printf("child process:pid=%d\n", getpid());
        //execl("/bin/ls","-a","-l","test_exec.c",NULL);	//①
        //execlp("ls","-a","-l","test_exec.c",NULL);	//②
        char *arg[]={"-a","-l","test_exec.c", NULL};	//③
        execvp("ls", arg);
        perror("error exec\n");
        printf("child process:pid=%d\n", getpid());
    } 
    return 0;
} 

2.1进程退出

Linux系统中进程的退出通过exit()函数实现。exit()函数存在于系统函数库stdlib.h中,其函数声明如下:

void exit(int status);

在进程中,exit()函数的功能其实就相当于“return返回值”,其中参数status表示进程的退出状态(0表示正常退出,非0表示异常退出,一般使用-1或1表示)。为了增强可读性,标准C中定义了两个宏:EXIT_SUCCESS和EXIT_FAILURE,分别表示正常退出和非正常退出。
Linux系统中有一个与exit()函数非常相似的函数——_exit(),_exit()函数定义在unistd.h中,其函数声明如下:

void _exit(int status);

exit()和_exit()函数都是用来终止进程的,但他们所做的操作有些许差别:挡程序执行到_exit()函数时,系统会无条件地停止操作,终止进程并清除进程所用内存空间以及进程在内核中的各种数据结构;exit()函数对_exit()进行了包装,在执行退出前还有若干道工序,最重要的就是它会在调用_exit()之前先检查文件的打开情况,将缓冲区中的内容写回文件。相对而言,exit()函数比_exit()函数更为安全。

2.2特殊进程

孤儿进程:父进程负责回收子进程,如果父进程在子进程退出之前退出,子进程就会变成孤儿进程,此时init进程将代替父进程完成子进程的回收工作;

僵尸进程:调用exit函数后,该进程不会马上消失,而是留下一个称为僵尸进程的数据结构。它几乎放弃进程退出前占用的所有内存,既没有可执行代码也不能被调度,只是在进程列表中保留一个位置,记载进程的退出状态等信息供父进程回收。若父进程没有回收子进程的代码,子进程将会一直处于僵尸态。

2.3特殊进程的危害

僵尸进程不能再次被运行,会占用一定的内存空间,并占据进程编号,当僵尸进程较多时,将会消耗系统内存,新进程可能因内存不足或无法获取pid而无法创建;

父进程通过wait()和waitpid()函数可以有效防止僵尸进程的产生,对于已存在的僵尸进程,则可通过杀死其父进程的方法解决;

当僵尸进程的父进程被终止后,僵尸进程将作为孤儿进程被init进程接收,init进程会不断调用wait()函数获取子进程状态,对已处于僵尸态的进程进行处理;

孤儿进程永远不会成为僵尸进程。

二、进程同步

1.1wait()函数

wait()存在于系统库函数sys/wait.h中,函数声明如下:

pid_t wait(int *status);

1.2案例

test_wait.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
int main(){//of main
	pid_t tempPid, tempW;
	tempPid = fork();
	if(tempPid == -1){//of if
		perror("fork error");
		exit(1);
	}else if(tempPid == 0){//child
		sleep(3);
		printf("Child process, pid = %d, ppid = %d\n", getpid(), getppid());
	}else{//parent 
		tempW = wait(NULL);
		printf("Catched a child process, pid = %d, ppid = %d\n", tempW, getppid());
	}
	printf("......finish......");
	return 0;
}

使用wait()函数同步进程,并使用宏获取子进程的返回值:

test_wait2.c
#include <stdio.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <unistd.h>
int main(){//of main
    int tempStatus;
    pid_t tempPid, tempW;
    tempPid = fork();
    if(tempPid == -1){//of if
        perror("fork error");
        exit(1);
    } else if(tempPid == 0){//child
        sleep(3);
        printf("Child process: pid=%d\n",getpid());
        exit(5);
 	}  else{//parent
        tempW = wait(&tempStatus);
        if(WIFEXITED(tempStatus)){//of if
            printf("Child process pid=%d exit normally.\n", tempW );
            printf("Return Code:%d\n",WEXITSTATUS(tempStatus));
        } else {
            printf("Child process pid=%d exit abnormally.\n", tempW);
        }
    }
    return 0;
}//of main

2.1waitpid()函数

wait()函数具有一定局限性,若当前进程有多个子进程,那么该函数就无法确保作为先决条件的子进程在父进程之前执行,此时可使用waitpid()函数实现进程同步。
waitpid()函数同样位于系统函数库sys/wait.h中,它的函数声明如下:

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

2.2案例

使父进程等待进程组中某个指定的进程,若该进程不退出,则让父进程一只阻塞。

test_waitpid.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
int main(){//of main
	pid_t tempPid, tempP, tempW;
	tempPid= fork();
	if (tempPid == -1){//of if				
		perror("fork1 error");
		exit(1);
	} else if (tempPid == 0){
		sleep(5);
		printf("First child process:pid=%d\n", getpid());
	} else {
		int i;
		tempP = tempPid;
		for (i = 0; i < 3; i++){// of for
			if ((tempPid = fork()) == 0){//of if
				break;
			}
		}
		if (tempPid == -1){//of if
			perror("fork error");
			exit(2);
		} else if (tempPid == 0){
			printf("Child process:pid=%d\n", getpid());
			exit(0);
		} else {
			tempW = waitpid(tempP, NULL, 0);
			if (tempW == tempP){
				printf("Catch a child Process: pid=%d\n", tempW);
			}else{
				printf("waitpid error\n");
			}
		}
	}
	return 0;
}

使用waitpid()函数不断获取某进程中子进程的状态。

test_waitpid2.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
int main(){//of main
	pid_t tempPid, tempW;
	tempPid = fork();
	if (tempPid == -1){//of if
		perror("fork error");
		exit(1);
	} else if (tempPid == 0){
		sleep(3);
		printf("Child process:pid=%d\n", getpid());
		exit(0);
	} else {
		do{
			tempW = waitpid(tempPid, NULL, WNOHANG);
			if (tempW == 0){
				printf("No child exited\n");
				sleep(1);
			}
		} while (tempW == 0);
		if (tempW == tempPid){
			printf("Catch a Child process:pid=%d\n", w);
		}else{
			printf("waitpid error\n");
		}
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值