第八章 进程控制

时间:2022-11-22 07:26:24

1 fork是内核创建新进程的唯一方法(除了交换进程,init和页精灵进程)

 

2 子进程获得父进程的数据空间,堆,栈。子进程的这些空间是父进程的拷贝,并不共享。子进程也会复制父进程的IO缓存(程序8.1的例子).但是很多实现并不做父进程数据段和堆的完全拷贝,有的使用了“写时复制”的技术。

 

3 从下面的例子可以看到,子进程中变量的地址和父进程是一样的。
#include <sys/types.h>
#include "ourhdr.h"

int glob = 6;
char buf[] = "a write to stdout/n";

int main()
{
    int var;
    pid_t pid;

    var = 88;

    if(write(STDOUT_FILENO, buf, sizeof(buf)-1) != sizeof(buf)-1)
        printf("write err");
    printf("before fork   /n");

    if( (pid = fork())<0)
        printf("fork error");
    else if(pid == 0)
    {
        glob++;
        var++;
    }
    else
        sleep(2);

    printf("pid=%d, glob=%d(%p), var=%d(%p)/n", getpid(), glob, &glob, var, &var);
    exit(0);
}

--------
结果:(父进程和子进程中两个变量的地址是一样的)
a write to stdout
before fork  
pid=32638, glob=7(0x80497a0), var=89(0xbfeb0484)
pid=32637, glob=6(0x80497a0), var=88(0xbfeb0484)

 

JL问题:在RH Linux下执行两次同一个可执行文件,可执行文件对应程序中变量分配的地址是不同的,和别人请教的结果是我们的可执行文件是shell的子进程,所需会分配shell所有的环境,可能由于shell的环境在两次执行的时候不同,所以我们的可执行文件里面变量的地址也不同。

 

4 子进程同时获得父进程的文件描述符,然后,父子进程都可以操作文件描述符所指向的文件,修改其偏移量。(JL问题:假如父进程删除了文件,那么子进程操作此文件的时候就会出错?还是系统不允许这么做?)

 

5 父、子进程之间的区别是:
• fork的返回值。
• 进程I D。
• 不同的父进程I D。
• 子进程的t m s _ u t i m e , t m s _ s t i m e , t m s _ c u t i m e以及t m s _ u s t i m e设置为0。
• 父进程设置的锁,子进程不继承。
• 子进程的未决告警被清除。
• 子进程的未决信号集设置为空集。

 

6 fork有两种用法:
1)一个父进程希望复制自己,使父、子进程同时执行不同的代码段。这在网络服务进程中是常见的——父进程等待委托者的服务请求。当这种请求到达时,父进程调用f o r k,使子进程处理此请求。父进程则继续等待下一个服务请求。
 JL问题:此时为何不用多线程?
2)一个进程要执行一个不同的程序。这对s h e l l是常见的情况。在这种情况下,子进程在从f o r k返回后立即调用e x e c
 JL问题:此时exec是否又启动一个新的进程?如果是,为何需要fork之后再exec?不如直接exec. 需要往后看

 

7 vfork
1)用于创建一个新进程,而该新进程的目的是e x e c一个新程序
2)v f o r k与f o r k一样都创建一个子进程,但是它并不将父进程的地址空间完全复制到子进程中,因为子进程会立即调用e x e c (或e x i t ),于是也就不会存访该地址空间。不过在子进程调用e x e c或e x i t之前,它在父进程的空间中运行。
这种工作方式在某些U N I X的页式虚存实现中提高了效率
3)v f o r k和f o r k之间的另一个区别是: v f o r k保证子进程先运行,在它调用e x e c或e x i t之后父进
程才可能被调度运行。
JL问题:书上说下面程序中如果用exit(0)来代替_exit(0),下面那个打印pid的就不能打印出来(因为vfork出的进程运行在父进程的空间里面,exit会关闭父进程的IO流,所以printf打印会出错;而_exit只会退出,不会关闭io流,故printf可以打印出来),实际我在linux上运行的时候,无论用哪个都可以打印出来.why?
#include <sys/types.h>
#include "ourhdr.h"
int glob = 6;
int main()
{
    int var;
    pid_t pid;

    var = 88;
    printf("before vfork/n");
    if((pid = vfork())<0)
    {
        printf("vfork error/n");
    }
    else if(pid == 0)
    {
        glob++;
        var++;
        _exit(0);
    }

    printf("pid=%d, glob=%d, var=%d/n",getpid(), glob, var);
    exit(0);
}

 

 

8 wait waitpid

 最后一个要考虑的问题是:一个由i n i t进程领养的进程终止时会发生什么?它会不会变成一个僵死进程?对此问题的回答是“否”,因为i n i t被编写成只要有一个子进程终止, i n i t就会调用一个w a i t函数取得其终止状态。这样也就防止了在系统中有很多僵死进程。

JL问题:前面说到调用wait就会是进程阻塞,那么init调用wait不会使init阻塞么?init 也是一个进程啊。。。

 

9 僵死进程:

子进程终止,但是父进程尚未对其作善后处理(获取终止子进程的有关信息、释放它仍占用的资源)的进程。

如何避免终止进程?比如进程A和B,A是父,B是子。 可以让A先fork一个中间进程C,然后用C来fork一个B,然后让C先退出,那么B就成为init的子进程。此时A和B同时运行,B不会成为僵死进程。

 

9 exec:

用f o r k函数创建子进程后,子进程往往要调用一种e x e c函数以执行另一个程序。
当进程调用一种e x e c函数时,该进程完全由新程序代换,而新程序则从其m a i n函数开始执行。
因为调用e x e c并不创建新进程,所以前后的进程I D并未改变。e x e c只是用另一个新程序替换了
当前进程的正文、数据、堆和栈段。

 

10 更改用户I D和组I D

什么是有效用户ID和有效组ID?需要查资料。

 

11 waitpid的用法还没有看到