进程控制

fork函数

  • fork描述:创建进程
  • fork函数一次调用,两次返回。父进程中,fork返回子进程的PID。子进程中,fork返回0。若出错则返回-1。
  • 每次用户向shell输入一个可执行目标文件的名字,运行程序时,shell就会创建一个新的进程,然后在这个新进程的上下文中运行这个可执行目标文件。应用程序也能创建新进程,并在这个新进程的上下文中运行他们自己的代码或其他应用程序。
  • 父进程通过调用fork函数创建一个新的运行的子进程。
  • 调用:
#include <sys/types.h>
#include <unistd.h>
pid_t fork(void);

getpid、getppid

  • getpid函数返回调用进程的PID。getppid函数返回它的父进程的PID。
  • getpid、getppid函数返回一个类型为pid_t的整数值,在Linux系统上它在types.h中被定义为int。
  • 调用:
#include <sys/types.h>
#include <unistd.h>
pid_t getpid(void);
pid_t getppid(void);

waitpid

  • 一个进程可以通过调用waitpid函数来等待它的子进程终止或停止。
  • 如果成功,返回子进程的PID。如果WNOHANG,则返回0。如果为其他错误,则返回-1。(WNOHANG、WUNTRACED等常量是由系统头文件定义的。例如WNOHANG、WUNTRACED由wait.h头文件(间接)定义)。
  • 如果调用进程没有子进程,那么waitpid返回-1,并设置errno为ECHLD。如果waitpid函数被一个信号中断,那么它返回-1并设置errno为EINTR。
  • 调用:
#include <sys/types.h>
#include <sys/wait.h>
pid_t waitpid(pid_t pid, int *statusp, int options);

检查已回收子进程的退出状态

  • WIFEXITED(status):如果子进程通过调用exit或者一个返回(return)正常终止,就返回真。
  • WEXITSTATUS(status):返回一个正常终止的子进程的退出状态。只有在WIFEXITED()返回为真时,才会定义这个状态。
  • WIFSIGNALED(status):如果子进程是因为一个未被捕获的信号终止的,就返回真。
  • WTERMSIG(status):返回导致子进程终止的信号的编号。只有在WIFSIGNALED()返回为真时,才会定义这个状态。
  • WIFSTOPPED(status):如果引起返回的子进程当前是停止的,就返回真。
  • WSTOPSIG(status):返回引起子进程停止的信号的编号。只有在WIFSTOPPED()返回为真时,才会定义这个状态。
  • WIFCONTINUED(status):如果子进程收到SIGCONT信号重新启动,则返回真。

程序实例

代码(一):
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>

int main(){
	pid_t pid;
	int x = 1;
	
	pid = Fork();
	if(pid == 0){  /*Child*/
		printf("child : x=%d\n", ++x);
		exit(0);
	}

	/*Parent*/
	printf("parent: x=%d\n", --x);
	exit(0);
}
运行结果:

在这里插入图片描述

结果分析:
  • fork函数被父进程调用一次,返回两次。一次返回到父进程,一次返回到新创建的子进程。
  • pid=0时,为子进程,输出child : x = 2(即执行printf(“child : x=%d\n”, ++x);)。为父进程时输出parent: x=0(即执行printf(“parent : x=%d\n”, --x);)。
  • 并发执行。父进程和子进程是并发运行的独立进程,内核能够以任意方式交替执行他们的逻辑控制流中的指令。即:在此处我们运行代码(一)的程序时,父进程先完成它的printf语句,然后是子进程(即运行结果为parent: x=0 child : x=2)。然而,在不同的系统上,可能是子进程先运行(运行结果为child : x=2 parent: x=0)。
  • 代码(一)对应进程图:
    在这里插入图片描述
代码(二):
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>

void fork7()
{
    if (fork() == 0) {/* Child */
		printf("Terminating Child, PID = %d\n", getpid());
		exit(0);
    } else {
		printf("Running Parent, PID = %d\n", getpid());
		while (1); /* Infinite loop */
    }
}
运行结果:

在这里插入图片描述在这里插入图片描述在这里插入图片描述

结果分析:
  • 由于父进程在printf后却有一个while(1)的循环,所以在子进程终止后父进程是无法结束的,导致shell无法出来。此时使用Ctrl+Z为挂起(Ctrl+C为终止)。
  • ps命令是最基本的进程查看命令。使用该命令可以确定有哪些进程正在运行和运行的状态、进程是否结束、进程有没有僵尸、哪些进程占用了过多的资源等等。ps显示瞬间进程的状态,并不动态连续;如果想对进程实行监控应该用top命令。
  • ps -ef :查看全格式的全部进程
    ps -ax :查看全部进程
    ps -ef|grep <进程名> :查看并筛选 跟进程名有关的进程,该进程名可以是进程的全部或者部分。
  • 僵死进程(zombie):一个终止了但还未被回收的进程。
  • 使用命令 kill -9 加上父进程的ID可杀死该僵死进程。
  • 函数kill是发送信号的函数,它并不能直接杀死进程,它位于头文件#include <sys/types.h>、#include <signal.h>中。函数的第一个参数pid若大于零,那么kill函数发送信号号码sig给进程pid;若pid等于零,kill发送sig给调用进程所在进程组中的每个进程,包括调用进程自己;若pid小于零,那么kill发送信号sig给进程组中pid的绝对值中的每个进程。
  • 代码(二)对应进程图:在这里插入图片描述
代码(三):
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

void fork9()
{
    int child_status;

    if (fork() == 0) {
	printf("HC: hello from child\n");
        exit(0);
    } else {
		printf("HP: hello from parent\n");
		wait(&child_status);
		printf("CT: child has terminated\n");
    }
    printf("Bye\n");
}
运行结果:

在这里插入图片描述

结果分析:
  • wait函数是waitpid函数的简单版本。调用wait( &status )等价于调用waitpid( -1 , &status , 0 )。
  • wait函数调用:
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *statusp);
  • 父进程在执行完第一条printf后遇到wait函数,转去执行子进程,子进程执行完后再返回去执行父进程。
  • 代码(三)对应进程图:在这里插入图片描述
代码(四):
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <signal.h>

void int_handler(int sig)
{
    printf("Process %d received signal %d\n", getpid(), sig); /* Unsafe */
    exit(0);
}
void fork13()
{
    pid_t pid[N];
    int i;
    int child_status;

    signal(SIGINT, int_handler);
    for (i = 0; i < N; i++)
	if ((pid[i] = fork()) == 0) {
	    /* Child: Infinite Loop */
	    while(1)
		;
	}

    for (i = 0; i < N; i++) {
		printf("Killing process %d\n", pid[i]);
		kill(pid[i], SIGINT);
    }

    for (i = 0; i < N; i++) {
	pid_t wpid = wait(&child_status);
	if (WIFEXITED(child_status))
	    printf("Child %d terminated with exit status %d\n",wpid, WEXITSTATUS(child_status));
	else
	    printf("Child %d terminated abnormally\n", wpid);
    }
}
运行结果:

# ==要插入图片!!还没插入!!!!!==

结果分析:
  • signal函数:进程可以通过使用signal函数修改和信号相关联的默认行为。
  • signal函数调用:
#include <signal.h> 
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum , sighandler_t handler);
  • signal函数通过调用以下三种方式之一来改变和信号signum相关联的行为:
  1. 如果handler是SIG_IGN,那么忽略类型为signum的信号。
  2. 如果handler是SIG_DFL,那么类型为signum的信号行为恢复默认行为。
  3. 否则,handler就是用户自定义的函数的地址,这个函数被称为信号处理器,只要进程收到一个类型为signum的信号就会调用这个程序。通过把处理程序的地址传递到signal函数从而改变默认行为,这叫做设置信号处理程序。调用信号处理程序被称为捕获信号。执行信号处理程序被称为处理信号
  • 信号处理程序可以被其他信号处理程序中断。
代码(五):
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>

void cleanup(void) {
    printf("Cleaning up\n");
}

void fork6()
{
    atexit(cleanup);
    fork();
    exit(0);
}
运行结果:

# ==要插入图片!!还没插入!!!!!==

结果分析:
  • atexit函数:注册终止函数。用来注册程序正常终止(也就是通过exit(0)、_exit(0)或return结束的程序)时要被调用的函数。位于头文件:#include<stdlib.h>中。
  • 用 法: void atexit(void (*func)(void));。
  • exit调用这些注册函数的顺序与它们 登记时候的顺序相反。同一个函数如若登记多次,则也会被调用多次。
  • 代码(五)对应进程图:在这里插入图片描述
代码(六):
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>

void fork16() 
{
    if (fork() == 0) {
		printf("Child1: pid=%d pgrp=%d\n",getpid(), getpgrp());
	if (fork() == 0)
	    printf("Child2: pid=%d pgrp=%d\n",getpid(), getpgrp());
		while(1);
    }
} 
运行结果:

在这里插入图片描述在这里插入图片描述

结果分析:
  • getpgrp函数返回调用进程的进程组 ID。
  • getpgrp函数调用:
#include <unistd.h>
pid_t getpgrp(void);
  • 每个进程都是只属于一个进程组,进程组是由一个正整数进程组ID来标识的。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值