apue学习笔记——第八章进程

1. 更改用户ID和更改组ID

在UNIX系统中,特权以及访问控制,是基于用户ID和组ID的。当需要增加特权需要访问并不允许访问的资源时,我们需要更换自己的用户ID或组ID,使得新ID具有合适的特权或访问权限。与此类似,当程序需要降低其特权或阻止对某些资源的访问时,也需要更换用户ID或组ID,新ID不具有相应特权或访问这些资源的能力
最小特权模型降低了由恶意用户试图哄骗我们的程序以未预料的方式使用特权造成的安全性风险。
可以用setuid函数设置实际用户ID和有效用户ID。
可以用setgid函数设置实际组ID和有效组ID

    #include <unistd.h>
    int setuid(uid_t uid);
    int setgid(gid_t gid);

1.1. 关于谁能更改ID有若干规则

现在考虑更改用户ID的规则(适用于组ID)

  1. 进程具有超级用户特权,则setuid函数实际用户ID有效用户ID以及保存的设置用户ID设置为uid.
  2. 若进程没有超级用户特权,但是uid等于实际用户ID保存的设置用户ID,则setuid只将有效用户ID设置为uid.不更改实际用户ID和保存的设置用户ID
  3. 如果上面两个条件都不满足,则errno设置为EPERM,并返回-1

实际用户ID,有效用户ID,保存的设置用户ID**
实际用户ID(RUID):用于标识一个系统中用户是谁,一般是在登录之后,就被唯一的确定,就是登录的用户的uid
有效用户ID(EUID):用于系统决定用户对系统资源的权限,也就是说当用户做任何一个操作时,最终看它有没有权限,都是在判断有效用户ID是否有权限。如果有,则ok,否则报错不能执行。在正常的情况下,一个用户登录之后(假设是A用户),A用户的有效用户ID和实际用户ID是相同的,但是如果A用户在某些场景中想要执行一些特权操作,能顺利的执行吗?上面说到了用户的任务操作,linux内核都是通过检验有效用户ID判断当前执行这个操作的用户是否具有权限。这里的A用户想要执行的是特权操作,A用户没有这个权限,所以A用户就只能通过一定的手段来修改(!!!)当前的有效用户ID使其具有执行特权操作的权限。
为什么要修改进程的有效用户ID,就是想在某一时刻能够执行一些特权操作。
设置用户ID位:用于对外的权限的开放,它的作用是修改进行的有效用户ID,给进程赋予临时的特权
保存的设置用户ID:是有效用户ID副本,既然是有效用户ID的副本,那么它的作用肯定是为了以后恢复有效用户ID

2. 进程会计

大多数UNIX系统提供了一个选项以进行进程会计处理。启用该选项后,每当进程结束时内核就写一个会计记录。典型的会计记录包含总量较小的二进制数据,一般包括命令名,所使用的CPU时间总量,用户ID和组ID,启动时间等。
会计记录所需的各个数据(各CPU时间,传输的字符数等)都由内核保存在进程表(PCB)中,并在一个新进程被创建时被初始化(如fork之后在子进程中)进程终止时写一个会计记录
特点:

  1. 我们不能获取永远不禁止的进程的会计记录。像init这样的进程在系统生命周期中一直在运行,并不会产生会计记录。这也同样适合与内核守护进程,他们不会终止。
  2. 在会计文件中记录的顺序对应于进程终止的顺序,而不是它们启动的顺序

3.进程调度

UNIX系统历史上对进程提供的只是基于调度优先级的粗粒度的控制。调度策略和调度优先级是由内核确定的。进程可以通过调整nice值以更低优先级运行(通过调整nice值降低它对CPU的占有)。只有特权进程
首先看看Linux系统中ps -l命令显示的英文含义
##########################################################
cyang@cyang-Inspiron-5577:~/QT$ ps -l
F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD
0 S 1000 22597 22490 0 80 0 - 7525 wait pts/1 00:00:00 bash
4 R 1000 30161 22597 0 80 0 - 9008 - pts/1 00:00:00 ps
##########################################################
UID:启动这些进程的用户。
PID:进程的进程ID。
PPID:父进程的进程号(如果该进程是由另一个进程启动的)。
C:进程生命周期中的CPU利用率。
STIME:进程启动时的系统时间。
TTY:进程启动时的终端设备。
TIME:运行进程需要的累计CPU时间。
CMD:启动的程序名称。
F:内核分配给进程的系统标记。
S:进程的状态(O代表正在运行;S代表在休眠;R代表可运行,正等待运行;Z代表僵化,进程已结束但父进程已不存在;T代表停止)。
PRI:进程的优先级(越大的数字代表越低的优先级)。
NI:谦让度值用来参与决定优先级。
ADDR:进程的内存地址。
SZ:假如进程被换出,所需交换空间的大致大小。
WCHAN:进程休眠的内核函数的地址。
相对而言,PRI也还是比较好理解的,即进程的优先级,或者通俗点说就是程序被CPU执行的先后顺序,此值越小进程的优先级别越高。那NI呢?就是我们所要说的nice值了,其表示进程可被执行的优先级的修正数值。如前面所说,PRI值越小越快被执行,那么加入nice值后,将会使得PRI变为:PRI(new)=PRI(old)+nice。这样,当nice值为负值的时候,那么该程序将会优先级值将变小,即其优先级会变高,则其越快被执行。


更需要强调一点的是,进程的nice值不是进程的优先级,他们不是一个概念,但是进程nice值会影响到进程的优先级变化

以下是

#include <unistd>
int nice(int incr); 

nice() adds inc to the nice value for the calling thread. (A higher
nice value means a low priority.)

The range of the nice value is +19 (low priority) to -20 (high prior‐
ity). Attempts to set a nice value outside the range are clamped to
the range.

Traditionally, only a privileged process could lower the nice value
(i.e., set a higher priority). However, since Linux 2.6.12, an unpriv‐
ileged process can decrease the nice value of a target process that has
a suitable RLIMIT_NICE soft limit; see getrlimit(2) for details.

此函数在运用时需要检查它的errno的值,因为nice调用成功时也会返回-1


getpriority可以获取一组相关进程的nice值

int getpriority(int which, id_t who);

相关函数:setpriority, nice

头文件:#include <sys/time.h> #include <sys/resource.h>

定义函数:int getpriority(int which, int who);


函数说明:
getpriority()可用来取得进程、进程组和用户的进程执行优先权。参数 which 有三种数值, 参数who 则依which 值有不同定义。


which who 代表的意义:
1、PRIO_PROCESS who:为进程识别码
2、PRIO_PGRP who:为进程的组识别码
3、PRIO_USER who:为用户识别码
此函数返回的数值介于-20 至20 之间, 代表进程执行优先权, 数值越低代表有较高的优先次序, 执行会较频繁.


返回值:返回进程执行优先权, 如有错误发生返回值则为-1 且错误原因存于errno.


附加说明:由于返回值有可能是-1, 因此要同时检查errno 是否存有错误原因. 最好在调用次函数前先清除errno变量.

错误代码:
1、ESRCH:参数which 或who 可能有错, 而找不到符合的进程.
2、EINVAL:参数which 值错误.


#include "apue.h"
#include "apueerror.h"
#include <errno.h>
#include <sys/time.h>

#if defined (MACOS)
#include <sys/syslimits.h>
#elif defined (SOLARIS)   
#include <limits.h>
#elif defined (BSD)
#include <sys/param.h>
#endif
/*
 * Structure used in select() call, taken from the BSD file sys/time.h.
struct timeval {
        long    tv_sec;         //seconds 
        long    tv_usec;        //and microseconds 
};
tv_sec 代表多少秒
tv_usec 代表多少微秒 1000000 微秒 = 1秒
上面taken from the BSD file sys/time.h 直接用函数 gettimeofday 就可以获得时间
*/
unsigned long long count;
struct timeval end;
void checktime(char *str)
{
    struct timeval tv;
    gettimeofday(&tv,NULL);
    if(tv.tv_sec >= end.tv_sec && tv.tv_usec >= end.tv_usec) {
        printf("%s count = %lld\n",str,count);
        exit(0);
    }
}

int main(int argc,char* argv[])
{                   
    pid_t pid;
    char* s;
    int nzero,ret;
    int adj = 0;

    setbuf(stdout,NULL);
#if defined (NZERO)
    nzero = NZERO;
#elif defined (_SC_NZERO)
    nzero = sysconf(_SC_NZERO);
#else
#error NZERO undefined
#endif
    printf("NZERO = %d\n",nzero);
    if(argc  == 2)
        adj = strtol(argv[1],NULL,10);
    gettimeofday(&end,NULL);
    end.tv_sec += 10; //run for 10s

    if((pid = fork()) < 0) { 
        err_sys("fork failed");
    }
    else if(pid == 0) {
         //child process
          s = "child";
          printf("current nice value in child is %d,adjusting by %d\n",nice(0)+nzero,adj);
          errno = 0;
         if((ret = nice(adj)) == -1 && errno != 0) //当返回值为-1时,检查errno才能判断
            err_sys("child set scheduling priority");
        printf("now child nice value is %d\n",ret + nzero);
    }   
    else {
        s = "parent";
        printf("current nice value in parent is %d\n",nice(0)+nzero);
    }

    for(;;) {
        if(++count == 0)
            err_quit("%s counter wrap",s);
            checktime(s); //
    }
}

程序运行在单处理器linux系统上,以显示调度程序在不同nice值的进程间进行CPU的共享。

4.进程时间


日历时间:该值时自**协调世界时(UTC)**这个特定时间以来所经过的秒数累计值。这些时间值可用于记录文件最近一次的修改时间等。time_t用于保存这种时间值。
进程时间:被称为CPU时间,用以度量进程使用的中央处理器资源。进程时间以时钟滴答计算。每秒钟曾经取为50,60或100个时钟滴答数。
墙上时钟时间:
用户CPU时间:
系统CPU时间:


5.进程通信

操作系统内并发执行的进程可以是独立进程或协作进程。
如果一个进程不能影响其他进程或被其他进程所影响,那么该进程是独立的,显然,不与任何其他进程共享数据的进程是独立的。
如果系统中一个进程能影响其他进程或被其他进程所影响,那么该进程是协作的。
理由:

  1. 信息共享
  2. 提高运算速度
  3. 模块化
  4. 方便

两种模式:
1)共享内存
在共享内存模式中,建立起一块供协作进程共享的内存区域,进程通过向此共享区域读或写入数据来交换信息。
共享内存允许以最快的速度进行方便的通信,在计算机中它可以达到内存的速度
2)消息传递
通过在协作进程间交换信息来实现通信。
对于交换较少数量的数据很有用,因为不需要避免冲突。
在这里插入图片描述
采用共享进程的进程间通信需要通信进程建立共享内存区域。通常,一块共享内存区域驻留在生成共享内存段进程的地址空间。
在这里插入图片描述
生产者-消费者问题
为了允许生产者进程和消费者进程能并发执行,必须要有一个缓冲来被生产者填充并被消费者所使用。此缓冲驻留在生产者和消费者进程的共享内存区域内。当消费者使用一项时,生产者能产生另一项。生产者和消费者必须同步,以免消费者消费一个没有生产出来的项。

两种缓冲。
无限缓冲和有限缓冲
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值