Linux之进程

1.进程的定义

在讨论进程定义之前,我们可以先来了解一下计算机操作系统为什么要引入进程这个概念。
1.1 进程的引入
当计算机系统只有一个程序运行时,称之为单道程序,此时这个程序独占系统中的所有资源,在执行的过程中不受外界的影响;而多道程序在执行时,就是所谓的程序并发执行,即若干个程序同时在系统中执行,这时,这些程序就不可能独占所有的系统资源了,而需要多个程序共享系统的资源,从而导致各个程序在执行时出现相互制约的关系。为了刻画系统内部出现的这种动态情况,描述程序并发执行的活动规律,操作系统就引入了进程这个概念,进程的出现是为了使多个程序并发执行,用以改善资源利用率,并且提高系统的吞吐量。
1.2 进程的定义
进程是一个“执行中的程序”,即程序在处理机上执行时所发生的活动。
由上述简单的定义可以发现,进程与程序有着密不可分的关系,运行中的程序在内存中的映像就叫做进程。
1.3查看进程信息
在Windows系统下,可以通过任务管理器来查看系统中的进程信息。
在Linux系统下,可以通过命令 “ps -aux”来查看系统中的进程信息。如下图所示:
在这里插入图片描述

2.进程的基本操作

2.1 进程创建
在Linux下,提供了好几个关于创建进程的操作函数,如fork(), vfork()
(1)fork()函数
该函数的功能是创建一个进程,新进程为当前进程的子进程,当前进程就被称为父进程。该函数的调用形式为:pid_t fork(void);使用该函数要引用头文件<sys/types.h>和<unistd.h>头文件,函数返回值类型为pid_t,表示一个非负整数。若程序运行在父进程中,函数返回的PID为子进程的进程号;若运行在子进程中,返回的PID为0.若创建子进程失败,则会返回-1.
程序代码如下:

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

int main()
{
    pid_t pid;
    pid = fork();
    if(pid <0)
    {
	printf("fork error!\n");
	exit(1);
    }
    else if(pid == 0)
    {
	printf("in the child process!\n");
    }
    else
    {
	printf("in the parent process!\n");
    }
    exit(0);
}

从程序的结果可以看出fork()函数的一个特点,即“调用一次,返回两次”,这又是为什么呢?
原来,在一个程序中,调用fork()函数后,就出现了分叉。在子进程中,fork()函数返回0;在父进程中,fork()函数返回子进程的PID。但是,调用fork()之后,谁先执行完全由调度器决定。
(2)vfork()函数
与fork()函数相同,这两个函数都是系统调用函数。而两者的区别是在创建子进程时fork()函数会复制父进程的所有资源,包括进程环境、内存资源等。而vfork()函数在创建子进程时并不会复制父进程的所有资源,父子进程共享地址空间。这样,在子进程中对虚拟地址空间中的变量修改,实际上是在修改父进程虚拟地址空间中的值。
以下是个实例,演示两个函数的区别。

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

int gvar = 2;

int main()
{
    pid_t pid;
    int var = 5 ;
    printf("process id: %ld\n", (long)getpid());
    printf("gvar = %d var = %d\n",gvar, var);
    pid = vfork();
    if(pid<0)
    {
	perror("error!\n");
	return 1;
    }
    else if(pid ==0)
    {
	gvar--;
	var++;
	printf("the child process id: %ld\ngvar = %d var=%d\n",(long)getpid(),gvar,var);
	_exit(0);
    }
    else
    {	
	printf("the parent process id: %ld\ngvar = %d var=%d\n",(long)getpid(),gvar,var);
	return 0;
    }
}

结果如下:
在这里插入图片描述
由运行结果可以看出,父进程中输入的变量值也是在子进程中变化后的值。由此可知,调用vfrok()函数改变子进程中的值,其实就是改变父进程中的值。

2.进程等待

进程等待就是为了同步父进程与子进程,通常需要通过wait()函数使父进程等待子进程结束。如果父进程没有调用等待函数,子进程就会进入“僵尸(Zombie)”状态。
Linux提供的等待函数原型如下:

#include<sys/types.h>
#include<sys/wait.h>
pid_t wait(int* status);
pid_t waitpid(pid_t pid,int* status, int options);

wait()函数系统调用的工作过程是:首先判断子进程是否存在,即是否成功创建了一个子进程。如果创建失败,子进程不存在,则会直接退出进程,并且提示相关错误信息;如果创建成功,那么wait()函数会将父进程挂起,直到子进程结束,并且返回结束时的状态和最后结束的子进程的PID。

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

/*定义一个功能函数,通过返回的状态,判断进程是正常退出还是信号导致的退出*/
void exit_s(int status)
{
    if(WIFEXITED(status))
    {
	printf("正常退出,status=%d\n",WEXITSTATUS(status));
    }
    else if(WIFSIGNALED(status))
    {
	printf("信号退出,status=%d\n",WTERMSIG(status));
    }
}

int main()
{
    pid_t pid, pid1;
    int status;
  //创建一个子进程
    if((pid=fork())<0)
    {
	printf("child process error!\n");
	exit(0);
    }
    else if(pid ==0) //子进程
    {
	printf("the child process!\n");
	exit(2);  //调用exit()退出函数正常退出
    }
    if(wait(&status)!=pid) //在父进程中调用wait()函数等待子进程结束
    {
	printf("This is a parent process!\nwait error!\n");
	exit(0);
    }
    exit_s(status);  //wait()函数调用成功,调用自定义的功能函数来判断退出类型

    /*又一次创建子进程,在子进程中,使用kill()函数发送信号,导致退出*/
    if((pid=fork()) <0)
    {
	printf("child process error!\n");
	exit(0);
    }
    else if(pid == 0)
    {
	printf("the child process!\n");
	pid1 = getpid();
	/*使用kill()函数发送信号*/
	kill(pid1,19);
    }
    if(wait(&status)!=pid1)
    {
	printf("This is a parent process!\nwait error!\n");
	exit(0);
    }
    exit_s(status);
    exit(0);
}

提示:在Linux系统的终端输入“kill -l”命令,可以列出信号的具体情况、信号类型和其所对应的数字。

关于进程等待函数,还有一个常用的等待函数waitpid(),该函数实现的功能与wait()函数相同,但它们区别在于:wait()函数用于等待所有子进程的结束,而waitpid()函数仅用于等待某个特定进程的结束,这个特定的进程是指其pid与函数中的参数pid相关时。所谓的相关,有如下几种可能:
(1) pid=-1,等待任一个子进程,与wait()等效
(2)pid>0, 等待其进程ID与pid相等的子进程。

3.进程结束

当想要终止或者结束一个进程时,会使用系统调用exit()函数正常退出进程。该系统调用包括exit()和_exit()两个函数。
1.exit()函数
原型为:

 #include <stdlib.h>
  void exit(int status);

注意:该函数调用成功与失败都没有返回值,并且没有出错信息的提示。
exit()函数的作用是终止进程,并将运算status&0377表达式后的值返回给父进程,在父进程中通过wait()函数来获得。
2._exit()函数
原型为:

 #include <stdlib.h>
void exit(int status);

同上,无论成功与否,都没有返回值。
在之前的进程创建时,vfork()函数创建的子进程在退出时只能使用_exit()函数退出进程,而不能使用exit()函数来退出。这是因为在调用exit()函数时,会对输入/输出流进行刷新,释放所占用的资源以及清空缓冲区等;而 _exit()函数则不具备刷新缓冲区等操作的功能。

4.进程组

所谓的进程组,就是一个或者多个进程的集合。作为一个进程组,里面的每一个进程都有统一的进程标识。
在Liunx系统中,可以通过调用函数 getpgrp()来获取进程组ID,该函数的原型为:

#include <sys/types.h>
#include <unistd.h>
pid_t getpgrep(void);

调用该函数可以返回调用该函数的进程所在的进程组ID。在进程组中有一个特殊的进程,该进程的ID与进程组的ID相同。
每一个进程都有其生命周期,从创建进程到进程终止,这是一个进程的生命期,而进程组的生命期是从该进程组的创建到最后一个进程终止。在Linux系统中,可以使用setpgid()函数创建一个新的进程组或者将一个进程加入到一个进程组中。该函数原型为:

#include <sys/types.h>
#include <unistd.h>
int setpgid(pid_t pid, pid_t pgid);
//调用成功返回0.失败返回-1;

以下为该函数的应用:

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
int main()
{
    int a ;
    pid_t pgid, pid;
    pid = (long)getpid();
    pgid = (long) getpgrp();
    a = setpgid(pid, pgid);
    printf("a = %d, pid = %ld, pgid = %ld\n", a, pid, pgid);
    return 0; 
}

结果:
在这里插入图片描述
注意:在setpgid()函数中,如果参数pid和pgid相等,则该函数功能是创建一个新的进程组;如果两个值不同,并且pgid是一个已经存在的进程组,那么该函数的功能是将pid进程加入到pgid这个进程组中。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值