进程控制(二)

2016-02-03

wait3和wait4函数

pid_t wait3(int *statloc, int options, struct rusage *rusage)
pid_t wait4(pid_t pid , int *statloc, struct rusage *rusage)
rusage 要求内核返回由终止进程及其所有子进程使用的进程摘要
资源信息包括用户CPU时间总量,系统CPU时间总量、缺页次数、接收到信号的次数

竞态条件

当多个进程都企图对共享数据进行某种处理,而最后的结果又却绝育进程的运行的顺序,则我们认为这发生了竞态条件。
如果一个进程希望等待一个子进程的终止,则它必须调用wait。如果一个进程要等待其父进程终止,则可以

while(getppid() != 1)
    sleep(1);

这种形式的循环(成为定期询问)的问题是浪费CPU,因为调用者每隔1秒都被唤醒。
为了避免竞态条件和定期询问,在多个进程之间需要某种形式的信号机制。在unix中可以使用信号机制。各种形式的进程间通信(IPC)也可以使用。

exec函数

当进程调用一种exec函数时,该进程完全由新程序带环,而新程序从main函数开始执行。因为调用exec并不创建新的进程,所以前后的进程id并不改变。exec只是用另一个新程序替换了当前进程的正文、数据、堆和栈段
有六种不同的exec函数,他们常常被统称为exec函数,这些函数都是unix进程控制原语。用fork可以创建新进程,用exec可以执行新的程序。exit函数和两个wait函数处理终止和等待终止。

int execl(const char *pathname, const char *arg0, ...);
int execv(const char *pathname, char *const argv[])
int execle(const char *pathnme, const char *arg0, ..., char *const envp[])
int execve(const char *pathnamem char *const argv[], char *const envp[])
int execlp(const char *filename, const char *arg0,...)
int execvp(const char *filename, char *const argv[])

这些函数之间的第一个区别是前四个去路径名作为参数,后两个取文件名作为参数。当指定filename作为参数时

  • 如果filename中包含/则就将其视为路径名
  • 否则就按PATH环境变量,在有关目录中搜寻可执行文件

有很多出于安全考虑,要求在搜索路径中绝不包含当前目录。
如果execlp和execvp中的任意一个使用路径前缀中的一个找到了一个可执行文件,但该文件不是由连接编辑程序生成的可执行文件,则认为此文件是一个shell脚本,于是试着调用/bin/sh并以该filename作为shell输入
第二个区别与参数表的传递有关execl execlp和execle要求将新程序的每个命令行参数都说明为一个单独的参数。这种参数表以空指针结尾,对于execv execvp execve则应县构造一个指向各个参数的数组指针,然后改数组地址作为这单个函数的参数。
最后一个区别与向新程序传递环境表相关。以e结尾的两个函数execle和execve可以传递一个指向环境字符串指针数组的指针。其他四个函数则使用调用进程中的environ作为新程序复制现存的环境。通常一个进程语序将其环境传播给子进程环境,但有时也有这种情况,进程想要为子进程制定一个确定的环境。
前面体积在执行exec后进程id没有改变。除此之外,执行新程序的进程还保持了元金城的下列特征

  • 进程id和父进程id
  • 实际用户id和实际组id
  • 添加组id
  • 进程组id
  • 控制终端
  • 闹钟尚剩余的时间
  • 当前工作目录
  • 根目录
  • 文件方式创建屏蔽字
  • 文件锁
  • 进程信号屏蔽
  • 未决信号
  • 资源限制
  • tms_utime, tms_stime, tms_cutime,tms_ustime

更改用户id和组id

可以用setuid函数设置实际用户id和有效用户id。与此类似可以用setgid设置实际组id和有效组id
int setuid(uid_t uid)
int setgid(gid_t gid)
关于改变id的规则

  • 若进程具有草机用户特权,则setuid函数将实际用户id有效用户id及保存的设置用户id设置为uid
  • 若进程没有超级用户权限,但是uid等于实际用户id或者设置用户id,则setuid值将有效用户id设置为uid不改变实际用户id和设置用户id
  • 如果上面两个条件都不满足,则errno设置为EPERM并返回出错

关于内核维护的三个用户id:

  • 只有超级用户进程可以更改实际用户id。通常实际用户id是在用户登录时由login程序设置的,而且局不会改变它。因为login是一个超级用户进程,当它调用setuid时设置所有三个用户id
  • 仅当对程序文件设置了设置用户id时exec函数设置有效用户id。如果设置用户id没有设置,则exec函数不会改变有效用户id,而将其维持为原来的值。任何时候都可以调用setuid,经有效用户id设置为实际用户id或设置用户id
  • 设置用户id是又exec从有效用户id复制的。在exec按文件用户id设置了有效用户id后,即进行这种复制。
    |id | exec | exec | setuid | |
    |----------|------|------|--------|-------|
    |id |设置用户id关闭|设置用户id打开|超级用户|非超级用户|
    |实际用户id | 不变 | 不变 | 设置为uid | 不变|
    |有效用户id | 不变 | 设置为程序文件的用户id | 设为uid | 设为 uid|
    |保存设置用户id | 从有效id复制 | 从有效id复制 | 设置为uid | 不变|

setreuid和setregid

int setreuid(uid_t ruid, uid_t euid)
int setregid(gid_t rgid, gid_t egid)
其功能是交换实际用户id和有效用户id
一个非特权用户总能交换实际用户id和有效用户id。这样允许一个设置用户id程序转换成只具有用户的普通许可权,以后又可以再次转换得到额外的许可权。

setuid和setgid

int setuid(uid_t uid)
int setgid(gid_t gid)
一个非特权用户可以将其有效用户id设置为其实际用户id或者保存的设置用户id。对于一个特权用户可将有效用户id设置为uid。

你可能感兴趣的:(进程控制(二))