8(进程控制)

本章需要熟练掌握如下几个函数fork,exec族,_exit,wait,waitpid

1 进程标识符

#include <unistd.h>
pid_t getpid(void);
        Returns: process ID of calling process
pid_t getppid(void);
        Returns: parent process ID of calling process
uid_t getuid(void);
        Returns: real user ID of calling process
uid_t geteuid(void);
        Returns: effective user ID of calling process
gid_t getgid(void);
        Returns: real group ID of calling process
gid_t getegid(void);
        Returns: effective group ID of calling process

2 fork函数

#include <unistd.h>
pid_t fork(void);
        Returns: 0 in child, process ID of child in parent, 1 on error

Fork函数执行一次但返回两次。父进程的返回值是子进程的进程ID,子进程的返回值是0(并不代表子进程的进程ID是0)

子进程和父进程并不共享存储空间,仅是父进程的副本。

父子进程调用顺序不确定

#include "apue.h"
int     glob = 6;       /* external variable in initialized data */
char    buf[] = "a write to stdout\n";
Int main(void)
{
    int       var;      /* automatic variable on the stack */
    pid_t     pid;

    var = 88;
    if (write(STDOUT_FILENO, buf, sizeof(buf)-1) != sizeof(buf)-1)
        err_sys("write error");
    printf("before fork\n");    /* we don‘t flush stdout */

    if ((pid = fork()) < 0) {
        err_sys("fork error");
    } else if (pid == 0) {      /* child */
        glob++;                 /* modify variables */
        var++;
    } else {
        sleep(2);               /* parent */
    }

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

3 vfork函数

#include "apue.h"
int     glob = 6;       /* external variable in initialized data */
Int main(void)
{
    int     var;        /* automatic variable on the stack */
    pid_t   pid;

    var = 88;
    printf("before vfork\n");   /* we don‘t flush stdio */
    if ((pid = vfork()) < 0) {
        err_sys("vfork error");
    } else if (pid == 0) {      /* child */
        glob++;                 /* modify parent‘s variables */
        var++;
        _exit(0);               /* child terminates */
    }
    /*
     * Parent continues here.
     */
    printf("pid = %d, glob = %d, var = %d\n", getpid(), glob, var);
    exit(0);
}

和fork的两个区别:

1.子进程在调用exec之前在父进程空间中运行,共享内存

2.保证子进程先运行,内核使父进程休眠,所以不用sleep函数

exit和_exit就是用来正常终止一个进程的,主要区别是_exit会立刻进入内核,而exit先执行一些清除工作(包括执行各种终止处理程序,关闭所有标准I/O等,一旦关闭了IO,例如Printf等函数就不会输出任何东西了),然后才进入内核。这两个函数会对父子进程有一定的影响,当用vfork创建子进程时,子进程会先在父进程的地址空间运行(这跟fork不一样),如果子进程调用了exit就会把父进程的IO给关掉。

接下来我们可以做个小测试,将vfork改成fork,预估glob=6,var=88.因为子进程修改的是副本

4 wait和waitpid函数

#include <sys/wait.h>
pid_t wait(int *statloc);
pid_t waitpid(pid_t pid, int *statloc, int options);
成功返回进程ID,出错返回-1

当一个进程正常终止时,内核就向父进程发送SIGCHLD信号。因为子进程终止是个异步事件,所以发送的也是异步信号。

对于调用了Wait和waitpid的进程会发生如下情况:

(1)子进程还在运行,则阻塞

(2)如果子进程已终止,正在等待父进程获取其终止状态,则取得该进程的终止状态并立即返回

(3)如果没有子进程,则立即出错返回

两者区别:

1.子进程终止前,wait使其调用者阻塞,而waitpid有一个选项,可使调用者不阻塞

2.Waitpid有若干选项,可以控制它所等待的进程

#include "apue.h"
#include <sys/wait.h>
Int main(void)
{
    pid_t   pid;
    int     status;

    if ((pid = fork()) < 0)
        err_sys("fork error");
    else if (pid == 0)              /* child */
        exit(7);

    if (wait(&status) != pid)       /* wait for child */子进程终止
        err_sys("wait error");
    pr_exit(status);                /* and print its status */

    if ((pid = fork()) < 0)
        err_sys("fork error");
    else if (pid == 0)              /* child */
        abort();                    /* generates SIGABRT */

    if (wait(&status) != pid)       /* wait for child */
        err_sys("wait error");
    pr_exit(status);                /* and print its status */

    if ((pid = fork()) < 0)
        err_sys("fork error");
    else if (pid == 0)              /* child */
        status /= 0;                /* divide by 0 generates SIGFPE */

    if (wait(&status) != pid)       /* wait for child */
        err_sys("wait error");
    pr_exit(status);                /* and print its status */

    exit(0);
}
//修改代码。输出status的同时也让其输出pid。pr_exit(status)改成printf(“%d %d\n”, status, pid);

调用fork两次来避免僵死进程

#include "apue.h"
#include <sys/wait.h>
Int main(void)
{
    pid_t   pid;

    if ((pid = fork()) < 0) {
        err_sys("fork error");
    } else if (pid == 0) {     /* first child */
        if ((pid = fork()) < 0)
            err_sys("fork error");
        else if (pid > 0)
            exit(0);    /* parent from second fork == first child */
        /*
         * We‘re the second child; our parent becomes init as soon
         * as our real parent calls exit() in the statement above.
         * Here‘s where we‘d continue executing, knowing that when
         * we‘re done, init will reap our status.
         */
        sleep(2);//保证父进程先执行
        printf("second child, parent pid = %d\n", getppid());
        exit(0);
    }

    if (waitpid(pid, NULL, 0) != pid)  /* wait for first child */
        err_sys("waitpid error");
    /*
     * We‘re the parent (the original process); we continue executing,
     * knowing that we‘re not the parent of the second child.
     */
    exit(0);
}

第二个子进程调用sleep保证在打印父进程ID时第一个子进程已终止。如果没有sleep,它可能比父进程先执行,那么返回的ID将会是父进程ID。,而不是init进程(值为1)。

运行程序得到

$ ./a.out
$ second child, parent pid = 1

5 exec函数

当进程调用exec函数时,该进程的程序完全替换为新程序,而新程序从main函数开始执行。调用exec并不创建新进程,所以前后的进程ID并未改变。

Fork创建新进程,exec执行新程序,exit和两个wait函数处理终止和等待终止

#include <unistd.h>
int execl(const char *pathname, const char *arg0, ... /* (char *)0 */ );
int execv(const char *pathname, char *const argv []);
int execle(const char *pathname, const char *arg0, .../* (char *)0,  char *const envp[] */ );
int execve(const char *pathname, char *const argv[], char *const envp []);
int execlp(const char *filename, const char *arg0, ... /* (char *)0 */ );
int execvp(const char *filename, char *const argv []);

#include "apue.h"
#include <sys/wait.h>
char    *env_init[] = { "USER=unknown", "PATH=/tmp", NULL };
int main(void)
{
    pid_t   pid;

    if ((pid = fork()) < 0) {
        err_sys("fork error");
    } else if (pid == 0) {  /* specify pathname, specify environment */
        if (execle("/home/sar/bin/echoall", "echoall", "myarg1", "MY ARG2", (char *)0, env_init) < 0)
            err_sys("execle error");
    }

    if (waitpid(pid, NULL, 0) < 0)
        err_sys("wait error");

    if ((pid = fork()) < 0) {
        err_sys("fork error");
    } else if (pid == 0) {  /* specify filename, inherit environment */
        if (execlp("echoall", "echoall", "only 1 arg", (char *)0) < 0)
            err_sys("execlp error");
    }

    exit(0);
}

在该程序中先调用execle,它要求一个路径名和一个特定的环境。下一个调用的是execlp,它用一个文件名将环境传送给新程序。

时间: 2024-08-05 16:00:45

8(进程控制)的相关文章

Linux - 进程控制 代码(C)

进程控制 代码(C) 本文地址:http://blog.csdn.net/caroline_wendy 输出进程ID,getpid(). 代码: /*By C.L.Wang * Eclipse CDT * Ubuntu 12.04 * 2014.10.5*/ #include "apue.h" #include "error.h" int main(void) { printf("hello world from process ID %ld\n"

六、Linux进程控制

1. Linux进程概述 进程是一个程序一次执行的过程,它和程序有本质区别. 程序是静态的,它是一些保存在磁盘上的指令的有序集合:而进程是一个动态的概念,它是一个运行着的程序,包含了进程的动态创建.调度和消亡的过程,是Linux的基本调度单位. 那么从系统的角度看如何描述并表示它的变化呢?在这里,是通过进程控制块(PCB)来描述的.进程控制块包含了进程的描述信息.控制信息以及资源信息,它是进程的一个静态描述. 内核使用进程来控制对CPU和其他系统资源的访问,并且使用进程来决定在CPU上运行哪个程

Linux进程控制编程

一.获取ID #include<sys/types.h> #include<unistd.h> pid_t getpid(void)    获取本进程ID pid_t getppid(void)  获取父进程ID 父进程:现有进程中,创建新的进程. 例:getpid.c #include<stdio.h> #include<unistd.h> #include<stdlib.h> int main() { printf("PID=%d\

APUE(8)---进程控制(1)

一.进程标识 每个进程都有一个非负整型标识的唯一进程ID.因为进程ID标识符总是唯一的,常将其用做其他标识符的一部分以保证其唯一性.进程ID虽然是唯一的, 但是却是可以复用的.ID为0的进程通常是调度进程,常常被称为交换进程(swapper).该进程是内核的一部分,它并不执行任何磁盘上的程序,因此也被称为系统进程.进程ID为1通常是init进程,在自举过程结束时由内核调用.此进程负责在自举内核后启动一个UNIX系统,init通常读取与系统有关的初始化文件,并将系统引导一个状态.init进程绝不会

Linux进程控制(二)

1. 进程的创建 Linux下有四类创建子进程的函数:system(),fork(),exec*(),popen() 1.1. system函数 原型: #include <stdlib.h> int system(const char *string); system函数通过调用shell程序/bin/sh –c来执行string所指定的命令,该函数在内部是通过调用execve("/bin/sh",..)函数来实现的.通过system创建子进程后,原进程和子进程各自运行,

linux 命令及进程控制

main.c  main.o/main.obj  main/main.exe          编译                连接 程序运行;      两步: gcc/g++  -c  main.c/main.cpp  -> main.o              gcc/g++ -o main  main.o -> main(可执行文件)     一步:  gcc -o main mian.c  -> main    工程文件:       rm  *.o     gcc  -

Linux进程控制——exec函数族

原文:http://www.cnblogs.com/hnrainll/archive/2011/07/23/2114854.html 1.简介 在Linux中,并不存在exec()函数,exec指的是一组函数,一共有6个,分别是: #include <unistd.h> extern char **environ; int execl(const char *path, const char *arg, ...); int execlp(const char *file, const char

APUE学习笔记(第八章 进程控制)

本章介绍UNIX系统的进程控制,包括创建新进程.执行程序和进程终止. 进程标识 每一个进程都有一个非负整数表示的唯一进程ID,除了进程ID,每个进程还有一些其他标识符.下列函数返回这些标识符 #include <unistd.h> pid_t getpid(void); pid_t getppid(void); uid_t getuid(void); uid_t geteuid(void); gid_t getgid(void); gid_t getegid(void); 函数fork 一个现

第八章:进程控制

8.1:引言 本章介绍Unix的进程控制,包括创建新进程.执行程序和进程终止.还将说明进程属性的各种ID--实际.有效和保存的用户和组ID,以及它们如何受到进程控制原语的影响.还包括解释器文件和system函数,最后讲述大多数Unix系统所提供的进程会计机制. 8.2:进程标识符 每个进程都有一个非负整型表示的唯一进程ID.虽然是唯一的,但是进程ID却可以重用,当一个进程终止后,其进程ID就可以再次使用了.Unix使用延迟重用算法,避免新进程的ID等于最近终止的进程的ID. 除了进程ID,每个进

【转载】linux进程控制-exec系列 exec系统调用

inux进程控制-exec系列 说是exec系统调用,实际上在Linux中,并不存在一个exec()的函数形式,exec指的是一组函数,一共有6个,分别是: #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, ...,