APUE 学习笔记——进程控制

栏目: IT技术 · 发布时间: 7年前

内容简介:APUE 学习笔记——进程控制

1、子进程与父进程的写时赋值是什么?

  • 子进程是父进程的一份一模一样的拷贝,如子进程获取了父进程数据空间、堆、栈的副本。
    • 父子进程共享正文段(因为正文段是只读的)
    • 父子进程并不共享这些数据空间、堆、栈
  • 由于创建子进程的目的通常是为了完成某个任务,因此fork之后经常跟随exec,所以很多操作系统的实现并不执行一个父进程数据段、堆和栈的完全拷贝,而是使用写时赋值技术(copy-on-write:COW
    • 这些区域由父进程和子进程共享,而且内核将它们的访问权限改变为只读
    • 如果父子进程中有一个试图修改这些区域,则内核只为修改区域的那块内存制作一个副本

2、 子进程继承父进程的属性有哪些?

  • 当前工作目录、根目录
  • 实际用户ID、实际组ID、有效用户ID、有效组ID、附属组ID、进程组ID、会话ID、控制终端、设置用户ID标志和设置组ID标志
  • 打开的文件描述符
  • 文件模式创建屏蔽字、信号屏蔽和信号处理
  • 对任一打开文件描述符的执行时关闭标志、环境、连接的共享存储段、存储映像、资源限制

3、父子进程的文件描述符的继承关系如何?

  • 父进程的所有打开的文件描述符都被复制到子进程中。父进程和子进程每个相同的打开描述符共享同一个文件表项
    • 更重要的是:父进程和子进程共享同一个文件偏移量
    • 如果父进程和子进程写同一个描述符指向的文件,但是又没有任何形式的同步,则它们的输出会相互混合
      • 如果父进程fork之后的任务就是等待子进程完成,而不作任何其他的事情,则父进程和子进程无需对打开的文件描述符做任何处理。因为此时只有子进程处理文件
      • 如果父进程fork之后,父进程与子进程都有自己的任务要处理,则此时父进程和子进程需要各自关闭它们不需要使用的文件描述符,从而避免干扰对方的文件操作

APUE 学习笔记——进程控制

4、父子进程的区别有哪些?

  • 子进程的tms_utime,tms_stime,tms_cutime,tms_ustime的值设置为0
  • 子进程不继承父进程设置的文件锁
  • 子进程的未处理闹钟被清除
  • 子进程的未处理信号集设置为空集

5、vfork 与 fork 直接的区别?

  • vfork用于创建一个新进程,该新进程的目的是exec一个新程序,所以vfork并不将父进程的地址空间拷贝到子进程中。

    • vfork的做法是:在调用exec或者exit之前,子进程在父进程的空间中运行

    所以在exec或者exit之前,子进程可以篡改父进程的数据空间

  • vfork保证子进程优先运行,在子进程调用exec或者exit之后父进程才可能被调度运行

    当子进程调用exec或者exit中的任何一个时,父进程会恢复运行,在此之前内核会使父进程处于休眠状态

6、父进程如何获知子进程已经结束?

内核为每个终止子进程保存了一定量的信息,所以当终止进程的父进程调用wait函数或者waitpid函数时,可以得到这些信息。

  • 这些信息至少包括:终止进程的进程ID、终止进程的终止状态、终止进程的使用的CPU时间总量
  • 内核此时可以释放终止进程使用的所有内存,关闭它所有的打开文件。但是该终止进程还残留了上述信息等待父进程处理
  • 如果父进程在子进程之前终止,那么内核会将该子进程的父进程改变为init进程,称作由init进程收养。其原理为:
    • 在一个进程终止时,内核逐个检查所有活动进程,以判断这些活动进程是否是正要终止的进程的子进程
    • 如果是,则该活动进程的父进程ID就改为 1
  • 对于init超级进程,它被设计成:任何时候只要有一个子进程终止,就立即调用wait函数取得其终止状态。这种做法防止系统中塞满了僵死进程
  • 当一个进程终止时,内核就向其父进程发送SIGCHLD信号。这种信号是一个异步信号,因为该信号可能在任何时间发出
    • 父进程可以选择忽略此信号。这是系统的默认行为
    • 父进程也可以针对此信号注册一个信号处理程序,从而当接收到该信号时调用相应的信号处理程序
  • 我们称一个已经终止、但是等待父进程对它进行善后处理的进程称作僵死进程,在ps命令中显示为Z
    • 所谓善后处理,就是父进程调用wait函数或者waitpid函数读取终止进程的残留信息
    • 一旦父进程进行了善后处理,则终止进程的所有占用资源(包括残留信息)都得到释放,该进程被彻底销毁

7、waitpidpid 参数与 options 参数设置?

  • pid
    • 如果pid==-1:则等待任意一个子进程终止
    • 如果pid>0:则等待进程ID等于pid的那个子进程终止
    • 如果pid==0:则等待组ID等于调用进程组ID的任一子进程终止
    • 如果pid<0:等待组ID等于pid绝对值的任一子进程终止
  • options:或者是0,或者是下列常量按位或的结果:
    • WNOHANG:没有指定的子进程终止时,并不阻塞程序的执行
    • WUNTRACED:执行作业控制。若操作系统支持作业控制,则由pid指定的任一子进程在停止后已经继续,但其状态尚未报告,则返回其状态
    • WCONTINUED:执行作业控制。若操作系统支持作业控制,则由pid指定的任一子进程已处于停止状态,并且其状态自停止以来尚未报告过,则返回其状态

8、waitwaitpid 之间的区别?

  • wait的语义是等待任何一个子进程终止:

    • 如果当前进程的所有子进程都还在运行,则阻塞
    • 如果有一个子进程已终止,正在等待父进程获取其终止状态,则当前进程取得该子进程的终止状态并立即返回
    • 如果当前进程没有任何子进程,则立即出错返回
  • waitpid的语义是等待指定的子进程终止:

    • 如果当前进程的所有子进程都在运行:
      • 如果options指定为WNOHANG,则waitpid并不阻塞,而是立即返回 0
      • 如果options未指定为WNOHANG,则waitpid阻塞
    • 如果指定pid的子进程已终止,正在等待父进程获取其终止状态,则当前进程取得该子进程的终止状态并立即返回
    • 如果指定的pid有问题(如不存在,或者不是当前进程的子进程),则立即出错返回
  • 对于出错的情况:
    • wait出错的原因是:
      • 调用进程没有子进程
      • 函数调用(正在阻塞中)被一个信号中断
    • waitpid出错的原因是:
      • 指定的进程或者进程组不存在
      • pid指定的进程不是调用进程的子进程
      • 函数调用(正在阻塞中)被一个信号中断

9、waitid 函数中 idtypeoptions 参数的意义?

  • idtype:指定了id类型,可以为下列常量
    • P_PID:等待特定进程。此时id表示要等待的子进程的进程ID
    • P_GID:等待属于特定进程组的任一子进程。此时id表示要等待的进程组ID
    • P_ALL:等待任一子进程。此时忽略id
  • options:指示调用者关心哪些状态变化。可以是下列常量的按位或:
    • WCONTINUED:等待这样的子进程:它以前曾被停止过,此后又继续执行,但是其状态尚未报告
    • WEXITED:等待已经终止的子进程
    • WNOHANG:如无可用的子进程终止状态,立即返回而不是阻塞
    • WNOWAIT:不破坏子进程的终止状态,该子进程的终止状态可以由后续的wait,waitid,waitpid调用取得
    • WSTOPPED:等待这样的子进程:它已经停止,但是其状态尚未报告

10、exec 几个函数之间的区别?

  • 前四个函数取路径名作为参数;后两个函数取文件名作为参数;最后一个取文件描述符做参数
    • filename中包含/,则视为路径名
    • filename不包含/,则按照PATH环境变量指定的各个目录中搜寻可执行文件
  • 函数execl,execlp,execle要求将新程序的每个命令行参数都说明为一个单独的参数,这种参数表以空指针结尾;函数execv,execvp,execve,fexecve应先构造一个指向各参数的指针数组,然后将该指针数组的地址作为参数
    • l表示列表list
    • v表示矢量vector
    • l形式中,必须以空指针结尾,否则新程序根本不知道要读取多少个参数。空指针就是命令行参数序列终止的标记
    • v形式中,数组的最后一个元素必须是空指针,否则报错。
  • e结尾的execle,execve,fexecve可以传递一个指向环境字符串指针数组的指针。注意这个数组的最后一个元素必须是空指针,否则报错。其他四个函数则使用调用进程的environ变量为新程序复制现有的环境
  • 在很多UNIX操作系统中,这7个函数只有execve是内核的系统调用。另外 6 个只是库函数。它们最终都要调用该系统调用

    11、exec 系列函数对进程属性有什么影响?

  • 执行exec之后,新程序的进程ID不变,进程的大多数属性不变。但是对打开文件的处理要注意:
    • 进程中每个打开的文件描述符都有一个执行时关闭标志。若设置了此标志,则执行exec时会关闭该文件描述符;
    • 否则该文件描述符仍然保持打开。系统默认行为是不设置执行时关闭标志
  • 执行exec之后,进程的实际用户 ID 和实际组 ID不变,但是进程的有效用户 ID 要注意:
    • 进程的有效用户 ID 和有效组 ID 是否改变取决于所执行程序文件的设置用户 ID 和设置组 ID 位是否设置。
      • 若程序文件的设置用户 ID 位已设置,则进程的有效用户 ID 变成程序文件所有者的 ID;否则有效用户 ID 不变
      • 若程序文件的设置组 ID 位已设置,则进程的有效组 ID 变成程序文件所有组的 ID;否则有效组 ID 不变

12、system 函数与 exec 的区别?

  • system 用于将一个字符作为命令来执行。它等同于同时调用了 forkexecwaitpid
  • system相较于fork+exec的优点是:system进行了所需的各种出错处理以及各种信号处理。缺点是:一旦调用system的进程具有超级用户权限,则system执行的命令也具有超级用户权限。

    因为system的实现过程中并没有更改有效用户ID和实际用户ID的操作。

    • 因此如果一个进程以特殊的权限运行,而它又想生成另一个进程执行另外一个程序,则它应该直接使用fork_exec并且在fork之后,exec之前改回普通权限。
    • 设置用户ID和设置组ID程序绝不应该调用system函数

13、 setuidsetuid 设置的规则?

  • 如果进程具有超级用户特权,则setuid函数将实际用户ID,有效用户ID以及保存的设置用户ID(saved set-user-ID) 全部设置为uid(此时uid没有限制)
  • 如果进程没有超级用户特权,但是uid等于实际用户ID或者保存的设置用户ID,则setuid只会将有效用户ID设置为uid,不改变实际用户ID和保存的设置用户ID
  • 如果上面两个条件都不满足,则errno设置为EPERM并返回 -1
  • 上述讨论中,假设_POSIX_SAVED_IDS为真。如果为提供此功能,则对于保存的设置用户ID部分都无效
  • 针对setgid的讨论类似setuid

14、 seteuidseteuid 设置的规则?

  • 如果进程具有超级用户权限,则seteuid将设置进程的有效用户IDuid(此时uid没有限制)
  • 如果进程没有超级用户权限,则seteuid只能将进程的有效用户ID设置为它的实际用户ID或者保存的设置用户ID
  • 针对setegid的讨论类似seteuid

API

pid_t getpid(void);  // 返回值:调用进程的进程ID
pid_t getppid(void); // 返回值:调用进程的父进程ID
uid_t getuid(void);  // 返回值:返回进程的实际用户ID
uid_t geteuid(void); // 返回值:返回进程的有效用户ID
gid_t getgid(void);  // 返回值:返回进程的实际组ID
gid_t getegid(void); // 返回值:返回进程的有效组ID

pid_t fork(void);

pid_t wait(int *staloc);
pid_t waitpid(pid_t pid,int *staloc,int options);

WIFEXITED(status):如果子进程正常终止,则为真。此时可以执行`WEXITSTATUS(status)`获取子进程的退出状态的低 8 位
WIFSIGNALED(status):如果子进程异常终止,则为真。此时可以执行`WTERMSIG(status)`获取使得子进程终止的信号编号
WIFSTOPPED(status):如果子进程的当前状态为暂停,则为真。此时可执行`WSTOPSIG(status)`获取使得子进程暂停的信号编号
WIFCONTINUED(status):如果子进程在暂停后已经继续执行了,则为真。

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

pid_t wait3(int *staloc,int options,struct rusage *rusage);
pid_t wait4(pid_t pid,int *staloc,int options,struct rusage *rusage);

int execl(const char *pathname,const char *arg0,.../*(char *) 0 */);
int execv(const char *pathname,char *const argv[]);
int execle(const char *pathname,const char *arg0,.../*(char *) 0,char *const envp[] */);
int execve(const char *pathname,char *const argv[],char *const envp[]);
int execlp(const char *filename,const char*arg0,.../*(char *) 0*/);
int execvp(const char *filename, char *const argv[]);
int fexecve(int fd,char *const argv[],char *const evnp[]);

int system(const char *cmdstring);

int seteuid(uid_t uid);
int setegid(gid_t gid);
int seteuid(uid_t uid);
int setegid(gid_t gid);
char *getlogin(void);

clock_t times(struct tms *buf);//返回流逝的墙上始终时间(以时钟滴答数为单位);调用两次`times`,然后取两次墙上时钟的差值
struct tms{
    clock_t tms_utime;  //用户 CPU 时间
    clock_t tms_stime;  //系统 CPU 时间
    clock_t tms_cutime; //终止的子进程的用户 CPU 时间的累加值
    clock_t tms_cstime; //终止的子进程的系统 CPU 时间的累加值
}

int nice(int incr);
int getpriority(int which,id_t who);
int setpriority(int which,id_t who,int value);

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Dive Into Python 3

Dive Into Python 3

Mark Pilgrim / Apress / 2009-11-6 / USD 44.99

Mark Pilgrim's Dive Into Python 3 is a hands-on guide to Python 3 (the latest version of the Python language) and its differences from Python 2. As in the original book, Dive Into Python, each chapter......一起来看看 《Dive Into Python 3》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码