五、进程管理

来源:互联网 发布:linux grub 配置 编辑:程序博客网 时间:2024/06/05 07:43
进程:它由数据、资源、状态和一个虚拟计算机组成。

进程ID

每个进程都有一个唯一识别的进程ID,简称pid。
空闲进程:当没有其他进程运行时,内核所运行的进程它的pid为0。
在启动后内核第一个运行的进程为init进程,它的pid为1。
内核分配进程ID是以严格的线性函数方式进行的。如果上一个进程的ID为17则下一个分配就是18,若17进程以及不存在了,知道内核分配pid达到了/proc/sys/kernel/pid_max内核是不会重用以前已经分配过的值。

进程体系

创建进程的那个进程称为父进程,而新进程称为子进程。这种关系保存在每个进程的父进程ID号(ppid)中。每个进程都被一个用户和组所拥有。

#include <sys/types.h>#include <unistd.h>pid_t getpid(void); //获取进程IDpid_t getppid(void); //获取父进程ID

写时复制

在现代Unix系统中,如Linux子进程保存一个指向资源的指针,只有当需要修改资源的时候才将资源复制一份。

vfork()

vfork()成功调用产生的结果和fork()相同,vfork()会挂起父进程知道子进程终止或运行了一个新的可执行文件的映像。vfork()避免了地址空间的按页复制。父进程和子进程共享相同的地址空间和页表项。子进程不能修改地址空间中的任何内存。

pid_t vfork(void);

exit()

void exit(int status);

终止进程,用来标识进程退出的状态,status & 0377这个值返回给父进程。
当进程退出时,内核会清理进程所创建的、不再用到的任何资源。

waitid()等待子进程结束

#include <sys/wait.h>int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options);

和wait()和waitpid()一样,waitid()作用是等待子进程结束和了解子进程的状态。waitid()允许程序员指定所要等待的子进程。参数idtype和id来指定要等待的子进程。idtype的值是下面:

P_PID 等待pid值是id的子进程P_GID 等待进程组ID是id的那些子进程P_ALL 等待所有子进程,参数id被忽略

参数id是id_t类型,代表一种通用的ID号,id_t类型值足够大可以保存任何pid_t值。参数options是一下的选项

WEXITED 调用进程会等待结束的子进程(有id和idtype指定)WSTOPPED 调用进程会等待收到了信号而停止了执行的子进程WCONTINUED 调用进程会等待收到了信号而继续执行子进程WNOHANG 调用进程不会阻塞,如果没有子进程结束,它就立即返回WNOWAIT 调用进程不会移除满足条件的子进程的僵死状态

成功时会填充参数infop,它必须指向一个有效的siginfo_t类型。siginfo_t结构体的具体成员是与实现相关的。但是还是有一些成员是在waitid()调用之后才有效的。也就是成功的调用会保证下面的成员会被填充:

si_pid 子进程pidsi_uid 子进程uidsi_code 根据子进程的状态是被信号所终止、杀死、停止或者继续执行而分别设置CLD_EXITED、CLD_KILLED、CLD_STOPPED或者CLD_CONTINUED中的一个si_signo 设置为SIGCHILDsi_status 如果si_code是CLD_EXITED,它是子进程的退出值。否则,它引起状态改变的那个信号的编码。

wait3()和wait4()

#include<sys/types.h>#include<sys/time.h>#include<sys/resource.h>#include<sys/wait.h>pid_t wait3(int *status, int options, struct rusage *rusage);pid_t wait4(pid_t pid, int *status, int options, struct rusage *rusage);
pid = wait3(status, options, NULL);//等价于pid = waitpid(-1, status, options);pid = wait4(pid, status, options, NULL);//等价于pid = waitpid(pid, status, optinos);

子进程返回的值可以用以下的宏

#include <sys/wait.h>int WIFEXITED(status);int WIFSIGNALED(status);int WIFSTOPPED(status);int WIFCONTINUED(status);int WEXITSTATUS(status);int WTETMSIG(status);int WSTOPSIG(status);int WCOREDUMP(status);

这里写图片描述

创建并等待一个新进程

创建一个进程然后就立刻开始等待它的结束

#define _XOPEN_SOURCE#include <stdlib.h>int system(const char *command);

system()来运行一个简单的工具程序或者shell脚本,它的调用使command指定的程序得到执行

用户和用户组

进程相关的用户ID:

实际用户ID:运行进程的用户ID有效用户ID:当前进程使用的用户ID保存设置的用户ID:进程原先的有效用户ID文件系统用户ID

更改用户ID和组ID

#include <sys/types.h>#include <unistd.h>int seteuid(uid_t euid); //设置有效用户IDint setguid(gid_t egid);

BSD版本

设置实际用户ID和游侠用户ID

#include <sys/types.h>#include <unistd.h>int setreuid(uid_t ruid, uid_t euid);int setregid(gid_t rgid, gid_t egid);

获得用户ID和组ID

//返回实际用户ID和组IDuid_t getuid();gid_t getgid();//返回有效用户ID和组IDuid_t geteuid();gid_t getegid();

会话和进程组

每个进程组ID(pgid)唯一标识。会话首进程的pid就是会话ID,进程组ID就是组长进程pid。
守护进程会创建自己的会话。
这里写图片描述

创建会话

#include <unistd.h>pid_t setsid(void);

如果调用进程不是某个进程的组长进程那么会创建一个新会话。调用进程就是会话进程的唯一进程也是新会话的首进程。setsid创建新会话,并在其中创建一个新的进程组,使得调用进程称为新会话的首进程和新进程组的组长进程。对于守护进程来说很有用。

守护进程

守护进程运行在后台,不与任何控制终端相关联。通常在系统启动时就运行,它们以root用户运行或者其他特殊用户,并处理一些系统级任务。
守护进程必须是init进程的子进程,并且不予任何控制终端相关联。
一般来说以下步骤会成为守护进程

(1)调用fork()创建新的进程,它会是将来的守护进程。(2)在守护进程的父进程中调用exit()。(3)调用setsid(),使得守护进程有一个新的进程组和新的会话。(4)用chdir()将当前工作目录改为根目录。因为之前fork继承了父进程的工作目录。(5)关闭所有的文件描述符。不需要继承任何打开的文件描述符。(6)打开0、1、2号文件描述符重定向到/dev/null