[APUE]第八章 进程控制

第八章 进程控制

引言:

 

如何创建进程?执行程序?进程终止?

讲述进程属性的各种ID---实际、有效、保存的用户和ID,他们是如何受到进程控制原语的影响。

解释器文件和system函数,进程会计机制。

 

进程标识符

每一个进程都有一个非负整数表示的唯一进程ID,这个进程ID是唯一的,他的使用机制和文件描述符是不相同的。文件打开的都是最小的整数(未打开),进程ID是采用延迟重用算法。

init 0表示关机 init 6表示重新启动

ID位0的是一个交换进程,该进程是内核的一部分。

ID为1的是init进程

pid_t getpid() ;

pid_t getppid();

uid_t getuid();

uid_t geteuid();

gid_t getgid();

gid_t getegid();

 

fork

创建进程fork,返回两次,子进程返回0,父进程返回子进程的ID,

pid_t fork(); //出错返回-1

 

fork的一个特性是父进程的所有打开的文件描述符都被复制到子进程中,父子进程的每一个相同的打开文件描述符共享一个文件表项。重定向父进程对的标准输出的时候,子进程的标准输出也重定向。

 

子进程会复制父进程的数据空间。

 

子进程会继承父进程的

实际用户ID、有效用户ID、实际组ID、有效组ID

附加组ID

会话ID

控制终端

设置用户ID标志和设置组DI标志

当前工作目录、根目录

文件模式的创建屏蔽字

 

子进程不会继承父进程的

进程ID

父进程设置的文件锁

 

vfork

pid_t vfork()

在调用exec或者exit之前,子进程在父进程的地址空间运行,子进程修改的变量会修改父进程的值。还有一个特点是vfork保障子进程先运行,在他调用exec或者exit之后父进程才可能被调度运行。

 

exit函数

五种进程正常终止

1  main函数中return返回

                                     2       调用exit()

                                     3       调用_Exit()、_exit()

                                     4       最后一个线程返回

                                     5       最后一个线程调用phread_exit()

异常终止:

1          调用abort

2          进程收到某些信号,使进程退出

3          最后一个线程对取消做出反应 pthread_cancel()

 

不管进程如何终止,都会关闭文件描述符和释放他使用的存储器

 

退出状态:是return value、exit(value) 中的value

终止状态:是wait(int &value)中的value

 

关于父子进程谁先终止的问题

如果父进程先终止,那么子进程的父进程就变成init(pid是1)的进程,子进程终止的时候,init为它收尸。不会僵死占用空间。

如果子进程先终止,那么父进程必须为子进程收尸(wait),不然子进程就会一直占用空间,导致出现僵死进程。

 

子进程终止的时候,会向父进程发送SIGCHLD信号。这个时候可以对子进程进行处理。

wait和waitpid可能的情况

如果子进程正在运行,则阻塞

如果没有子进程就直接出错返回

如果子进程已终止,正等待父进程获取其终止状态,则取得该子进程的终止状态时立即返回。

pid_t wait(int &status)
pid_t waitpid(pid_t pid, int *status, intoptions)

如果pid == 0 等待其组ID等于调用进程组ID的任意子进程

         pid== -1 等待任意子进程

         pid》0 等待pid位pid的子进程

         pid《0 等待pid为|pid|的子进程。

 

exec*函数

#include <unistd.h>
int execl(const char *path, const char*arg, ...);
int execlp(const char *file, const char*arg, ...);
int execle(const char *path, const char*arg, ..., char *const envp[]);
int execv(const char *path, char *constargv[]);
int execvp(const char *file, char *constargv[]);
int execve(const char *path, char *constargv[], char *const envp[]);

最终调用的是execve,其他都是在这个函数的基础上进行封装的。

 

更改用户ID和组ID

int setuid(uid_t uid)
int setgid(gid_t gid)

如果进程具有超级用户权限,那么设置成功。

如果进程不具有超级用户权限,并且uid等于实际用户ID或者设置用户ID,那么仅仅能把有效用户ID设置为实际用户ID

如果上面都不满足就直接返回-1.

以上规则也适用于组ID


man命令切换的原理

man程序可能必须执行几个其它命令来处理包含要显示的手册页的文件。为了避免被欺骗以致运行错误的命令或覆写错误的文件,man命令必须在两个权限集里交换:运行man命令的用户和拥有man可执行文件的用户。下面的步骤会发生:


1、假设man程序文件被用户名man拥有并设置了设置用户ID,当我们exec它时,我们有:真实用户ID=我们的用户ID;有效用户ID=man;保存的设置用户ID=man。 

 

2、man程序访问所需的配置文件和手册页。这些文件被用户名man拥有,但是因为有效用户ID是man,所以文件访问被允许。 

 

3、在man为我们执行任何一个程序时,它调用setuid(getuid())。因为我们不是超级用户进程,所以这只改变有效用户ID。我们有:真实用户ID=我们的用户ID(不变);有效用户ID=我们的用户ID;保存的设置用户ID=man(不变)。现在man进程用我们的用户ID作为它的有效用户 ID运行。这意味着我们只能访问我们有普通权限的文件。我们没有更多的权限。它可以为我们执行任何过滤器。 

 

4、当过滤完成时,man调用setuid(euid),这里euid是用户名man的数值用户ID。(这是我们为什么需要保存的设置用户ID。)现在我们有:真实用户ID=我们的用户ID(不变);有效有用ID=man;保存的设置用户ID=man(不变)。 

 

5、man程序现在可以在它的文件上进行操作,因为它的用效用户ID是man。 

 

 

解释器文件、

system函数、

system调用fork execl waitpid这些函数其中fork、waitpid出错返回-1 execl出错返回127

正常返回是执行shell的终止状态

进程会计

调用accton + filename 文件名称

就会把进程执行完毕的一些信息(使用的CPU、用户ID和组ID、启动时间)记录到文件里面

下面这个程序就是解析这个文件的。程序是固定的

/*
 *=====================================================================================
 *
 *      Filename:  accton.cpp
 *
 *   Description:  accton
 *
 *       Version:  1.0
 *       Created:  2012年05月04日 21时13分45秒
 *      Revision:  none
 *       Compiler:  gcc
 *
 *        Author:  LeoK,
 *  Organization: 
 *
 *=====================================================================================
 */
#include <stdlib.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/acct.h>
#include <unistd.h>
#include <errno.h>
//#include "errorout.h"
#define     err_sys  perror
#define     err_quit perror
#ifdef HAS_SA_STAT
#define FMT "%-*.*s e = %6ld, chars =%7ld, stat = %3u; %c %c %c\n"
#else
#define FMT "%-*.*s e = %6ld, chars =%7ld, %c %c %c\n"
#endif
 
#ifndef HAS_ACORE
#define ARORE 0
#endif
 
#ifndef HAS_AXSIG
#define AXSIG 0
#endif
 
static unsigned long compt2ulong(comp_tcomptime)
{  
 
         unsignedlong   val;
         int             exp;
 
         val= comptime & 0x1fff;                       //13-bit fraction
         exp= (comptime >> 13) & 7;                     //3 -bit exponent (0-7)
         while(exp-- >0)
                   val*=8;
         returnval;
}
int main(int argc,char *argv[]){
         structacct     acdata;
         FILE            *fp;
 
         if(argc!= 2)
                   err_quit("usage:pracct filename");
         if((fp=  fopen(argv[1],"r")) == NULL)
                   err_sys("can'topen accton " );
         while(fread(&acdata,sizeof(acdata), 1,fp) ==1){
                   printf(FMT,(int)sizeof(acdata.ac_comm)
                                     ,(int)sizeof(acdata.ac_comm), acdata.ac_comm
                                     ,compt2ulong(acdata.ac_etime), compt2ulong(acdata.ac_io)
#ifdef HAS_SA_STAT
                                     ,(unsignedchar) acdata,ac_stat
#endif
                                     ,acdata.ac_flag& ACORE ? 'D' : ' '
                                     ,acdata.ac_flag& AXSIG ? 'X' : ' '
                                     ,acdata.ac_flag& AFORK ? 'F' : ' '
                                     ,acdata.ac_flag& ASU   ? 'S' : ' '
                              );
 
         }
         if(ferror(fp))
                   err_sys("readerror");
         exit(0);
}

调用execl不会增加进程会计的个数

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值