工学1号馆

home

« | 返回首页 | »

Unix进程标志与fork函数

By Wu Yudong on October 24, 2016

1、进程标志

进程ID标识符是唯一、可复用的。大多数Unix系统实现延迟复用算法,使得赋予新建进程的ID不同于最近终止所使用的ID

ID为0的进程通常是调度进程,也常被称为交换进程。它是内核的一部分,是系统进程。

ID为1的进程通常是init进程,在自举过程结束时由内核调用。该进程负责在内核自举后启动一个Unix系统,它决不会终止,是一个普通的用户进程,但以超级用户特权运行。

ID为2的进程是页守护进程,负责支持虚拟存储器系统的分页操作

除了进程ID ,每个进程还有一些其他标识符。下列函数返回这些标识符

#include <unistd.h>
pid_t getpid(void);  //返回调用进程的进程ID
pid_t getppid(void); //返回调用进程的父进程ID
uid_t getuid(void);  //返回调用进程的实际用户ID
uid_t geteuid(void)  //返回调用进程的有效用户ID
gid_t getgid(void);  //返回调用进程的实际组ID
gid_t getegid(void); //返回调用进程的有效组ID

本文地址:http://wuyudong.com/2016/10/24/2894.html,转载请注明出处。

2、函数fork

一个现有的进程可以调用fork函数创建一个新进程

#include <unistd.h>
pid_t fork(void);
//返回值:子进程返回0;父进程返回进程ID;若出错,返回-1

fork函数被调用一次,返回两次。子进程中返回值是0,父进程中返回值是子进程的pid

子进程是父进程的副本,子进程获得父进程的数据空间、堆和栈的副本。注意,在是子进程拥有的副本。父子进程并不共享这些存储空间部分。父子进程共享正文段。

由于在fork之后经常跟随着exec,所以现在的很多实现并不执行一个父进程数据段、堆和栈的完全副本。作为替代,使用了写时复制技术。

#include "apue.h"

int        globvar = 6;        
char    buf[] = "a write to stdout\n";

int
main(void)
{
    int        var;        /* 在栈上的自动变量 */
    pid_t    pid;

    var = 88;
    if (write(STDOUT_FILENO, buf, sizeof(buf)-1) != sizeof(buf)-1)
        err_sys("write error");
    printf("before fork\n");    

    if ((pid = fork()) < 0) {
        err_sys("fork error");
    } else if (pid == 0) {        /* 子进程 */
        globvar++;                
        var++;
    } else {
        sleep(2);                /* 父进程 */
    }

    printf("pid = %ld, glob = %d, var = %d\n", (long)getpid(), globvar, var);
    exit(0);
}

运行程序

wu@ubuntu:~/apue.3e/proc$ ./fork1
a write to stdout
before fork
pid = 28356, glob = 7, var = 89
pid = 28355, glob = 6, var = 88

可以看到,子进程的glob和var的值改变了,而父进程的没有改变

一般来说,在fork之后是父进程先执行还是子进程先执行是不确定的,这取决于内核所使用的调度算法。如果要求父进程和子进程之间相互同步,则要求某种形式的进程间通信。

父进程和子进程每个相同的打开描述符共享一个文件表项。
重点是,父进程和子进程共享同一个文件偏移量。

在fork之后处理文件描述符有以下两种常见的情况。
1. 父进程等待子进程完成。在这种情况下,父进程无需对其描述符做任何处理。当子进程终止后,它层进行过读、写操作的任一共享描述符的文件偏移量已做了相应更新。
2. 父进程和子进程各自执行不同的程序段。在这种情况下,在fork之后,父进程和子进程各自关闭它们不需使用的文件描述符,这样就不会干扰对方使用的文件描述符。这种方法是网络服务进程经常使用的。

除了打开文件之外,父进程的很多其他属性也由子进程继承,包括:

实际用户ID、实际组ID、有效用户ID、有效组ID
附属组ID
进程组ID
会话ID
控制终端
设置用户ID标志和设置组ID标志
当前工作目录
根目录
文件模式创建屏蔽字
信号屏蔽和安排
对任一打开文件描述符的执行时关闭标志
环境
连接的共享存储段
存储映像
资源限制

父进程和子进程之间的区别具体如下。

fork的返回值不同
进程ID不同
这两个进程的父进程ID不同:子进程的父进程ID是创建它的进程的ID,而父进程的父进程ID则不变。
子进程的tms_utime、tms_stime、tms_cutime和tms_ustime的值设置为0
子进程不继承父进程设置的文件锁
子进程的未处理闹钟被清除
子进程的未处理信号集设置为空集

使fork失败的两个主要的原因是:(a)系统中已经有了太多的进程,(b)该实际用户ID的进程总数超过了系统限制。

fork有以下两种用法
1. 一个父进程希望复制自己,使父进程和子进程同时执行不同的代码段。这在网络服务器进程中是常见的——父进程等待客户端的服务请求。当这种请求到达时,父进程调用fork,使子进程处理此请求。父进程则继续等待下一个服务请求。
2. 一个进程要执行一个不同的程序。这对shell是常见的情况。在这种情况下,子进程从fork返回后立即调用exec

如果文章对您有帮助,欢迎点击下方按钮打赏作者

Comments

No comments yet.
To verify that you are human, please fill in "七"(required)