Linux C学习:进程控制

欢迎进入C/C++编程社区论坛,与300万技术人员互动交流 >>进入

  7.1 进程概述

  7.1.1 进程的定义

  1)进程的定义

  ①进程是一个具有独立功能的程序关于某个数据集合的一次运行活动

  ②进程是一个程序与其数据一道通过处理机的执行所发生的活动

  ③进程是一个“执行中的程序”,即程序在处理机上执行时所发生的活动,而程序只是行为的一种规则

  2)进程的特性

  动态性 并发性 独立性 异步性 结构特性

  7.1.2 进程的相关信息

  1)进程ID:在Linux系统中,每一个进程都有其唯一的ID。在Linux系统下编写关于进程的C程序时,经常用到这样一个数据类型pid_t,该数据类型专门用来定义进程ID,其实可以将这个数据类型理解为一个非负数整数。

  2)进程的状态:进程有3中基本状态,分别是运行状态、等待状态和结束状态。除了这三种基本状态外,进程还有就绪、挂起和僵尸等状态。

  3)进程切换:关于进程切换,就是从正在运行的进程中收回处理器的使用权,等待运行进程进来时占用此处理器。

  4)虚拟内存:在Linux系统中,每个进程都运行在格子的虚拟内存空间中。在Linux系统中的虚拟内存具有以下几点功能,如拥有巨大的寻址空间、可以共享虚拟内存及对进程进行保护等。

  7.2 进程的基本操作

  7.2.1 进程创建

  1)fork()函数:fork()函数的功能是创建一个新的进程,新进程为当前进程的子进程,那么当前进程就被称为父进程。在一个函数中,可以通过fork()函数的返回值判断进程是在子进程中还是在父进程中。调用形式为:pid_t fork(void)

  使用fork()函数需要引用和头文件,该函数的返回值类型为pid_t,表示一个非负整数。若程序运行在父进程中,函数返回的PID子进程的进程号;若程序运行在紫禁城中,返回PID为0。

  如若调用fork()函数创建子进程失败,那么就会返回-1,并且提示错误信息。错误信息有以下两种形式:

  EAGAIN:表示fork()函数没有足够的内存用于赋值父进程的分页表和进程结构数据。

  ENOMEN:表示fork()函数分配必要的内核数据结构时,内存不足。

  2)vfork()函数:vfork()函数与fork()函数相同,都是系统调用函数,两者的区别是在创建子进程时fork()函数会复制所有的父进程的资源,包括进程环境、内存资源等,而vfork()函数在创建子进程时不会复制父进程的所有资源,父进程共享地址控件。 这样,子进程中对虚拟内存控件变量的修改,实际上是在修改父进程虚拟内存空间的值。

  在使用vfork()函数时,父进程会被阻塞,需要在子进程中调用_exit()函数退出子进程,不能使用exit()退出函数。

  3)exec()函数族:通过调用fork()函数和vfork()函数创建子进程,子进程和父进程执行的代码是相同的。但是,通常创建了一个新进程也就是子进程后,目的是要执行与父进程不同的操作,实现不同的功能。因此Linux系统提供了一个exec()函数族,用 于创建和修改子进程。调用exec()函数时,子进程中的代码段、数据段、和堆栈段都将被替换。由于调用exec()函数并没有创建新进程,因此修改后的子进程ID并没有改变。exec()函数族由6种以exec()开头的函数组成,定义形式分别如下:

  int execl(const char *path, const char *arg, …)

  int execlp(honst char *file, const char *arg, …)

  int execle(const char *pathconst char *arg, …, char* const envp[])

  int execv(const char *path, const char *argv[])

  int execve(const char *path, const char *argv[], char *const envp[])

  int execvp(const char *file, const *argv[])

  这些函数都定义在系统函数库中,在使用前需要引用头文件和,并且必须在预定义时定义一个外部全局变量,例如:extern char **environ;

  上面定义的变量是一个指向Linux系统全局变量的指针。定义了这个变量后,就可以在当前工作目录中执行系统程序,如同在shell中不输入路径直接运行VIM和Emacs等程序一样。

  exec()函数族中的函数都实现了对子进程中的数据段、代码段和堆栈段进行替换的功能,如果调用成功,则加载新的程序,没有返回值。如果调用出错,则返回值为-1.

  这几个exec()函数的书写方式很相似,很容易记混,但是这几个函数都各有区别。通过exec()函数名称的拼写规律可以轻松帮助读者牢记这几个函数实现替换功能的不同方法。

  ①函数名中带有字母p:字母p是path的首字母,代表文件的绝对路径(或称相对路径)。当前函数名中带有字符p时,函数的参数就可以不用写出文件的相对路径,只写出文件名即可,因为函数会自动搜索系统的path路径

  ②函数名中带有字符l:字符l是list的首字母,表示需要将新程序的每个命令行参数都当做一个参数传给它,参数个数是可变的,并且最后要求输入一个NULL参数,表示参数输入结束。

  ③函数名中带有字母v:字符v是vector的首字母,表示该类函数支持使用参数数组,数组中的最后一个指针也要输入NULL参数,作为结束标志,这个参数数组就类似于main()函数的形式argv[]

  ④函数名以e结尾:字符e是environment的首字母,该类函数表示可以将一份新的环境变量表传给它。

  在exec()函数族中,execve()函数是其余5个exec()函数的基础,因为只有execve()函数时经过系统调用的,其余5个函数在执行时,都要在最后调用一次execve()函数。

  7.2.2 进程等待

  1)进程等待就是为了同步父进程和子进程,通常需要通过调用wait()等待函数使父进程等待子进程结束。如果父进程没有调用等待函数,子进程就会进入“僵尸(Zombie)”状态。了解了等待函数的工作过程,就可以知道为什么蜜柚调用等待函数时,子进程会进入僵尸状态,关于进入进程的等待状态,Linux系统提供的等待函数原型如下:

  #include

  #include

  pid_t wait(int *status)

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

  int waitid(idtype_t idtype, id_t id, siginfo_t *info, int options)

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

  ①子进程正常结束:当调用wait()函数,子进程正常结束后,函数会返回子进程PID和status状态,此时的参数status所指向的状态变量就存放在紫禁城的退出码中。退出码是所谓的从子进程的main()函数中返回的值或者子进程中exit()函数的参数。

  ②信号引起子进程结束:wait()函数系统调用中发送信号给子进程,可能会导致子进程结束运行。若发送的信号被子进程捕获,就会起到终止子进程的作用;若信号没有被子进程捕获,则会使子进程非正常结束。此时参数status返回的状态值为接收到的信号值,存放在最后一个字节中。

[1][2]

所有的失败,与失去自己的失败比起来,更是微不足道

Linux C学习:进程控制

相关文章:

你感兴趣的文章:

标签云: