15、深入理解计算机系统笔记:进程控制

1、获取进程ID[1]

每个进程都有一个唯一的正数(非0)进程IDPID)。

示例代码

#include <unistd.h>
#include <sys/types.h>
pid t getpid(void);
pid t getppid(void);
returns: PID of either the caller or the parent

    The getpid and getppid routines return an integer value of type pid_t, which on Linux systems

is defined in types.h as an int.

进程总是处于运行,停止,终止三种状态间。

2、创建和终止

示例代码

#include <stdlib.h>
void exit(int status);
this function does not return

#include <unistd.h>
#include <sys/types.h>
pid t fork(void);
returns: 0 to child, PID of child to parent, -1 on error

#include <sys/types.h>
#include <sys/wait.h>
pid t waitpid(pid t pid, int *status, int options);
returns: PID of child if OK, 0 (if WNOHANG) or -1 on error
The members of the wait set are determined by the pid argument:
 If pid > 0, then the wait set is the singleton child process whose process ID is equal to pid.
 If pid = -1, then the wait set consists of all of the parent’s child processes.

    The newly created child process is almost, but not quite, identical to the parent. The child

gets an identical (but separate) copy of the parents userlevel virtual address space, including the text, data,

and bss segments, heap, and user stack. The child also gets identical copies of any of the parents open file descriptors, which

means the child can read and write any files that were open in the parent when it called fork. The most significant

difference between the parent and the newly created child is that they have different

PIDs.

fork函数常令人迷惑,因为它只被调用一次,却会返回两次:一次是在调用进程(父进程)中,一次是在新创建的子进程中。

示例见原文中。

3、回收子进程

进程终止时,内核并不是立即把它从系统中清除;而是保持一种终止状态,直到被父进程回收(reaped)。父进程回收已终止的子进程时,内核将子进程的退出状态传递给父进程,然后抛弃已终止的进程。一个终止了但仍未被回收的进程称为僵尸进程(zombie)。

如果父进程没有回收,则通过init进程来回收。

一个进程通过调用waitpid函数来等待它的子进程终止或暂停。

4、进程休眠

示例代码

#include <unistd.h>
unsigned int sleep(unsigned int secs);
returns: seconds left to sleep
//让进程挂起一段时间
#include <unistd.h>
int pause(void);
always returns -1
//puts the calling function to sleep until a signal is received by the process.

5、加载并运行程序

示例代码

#include <unistd.h>
int execve(char *filename, char *argv[], char *envp);
does not return if OK, returns -1 on error
int main(int argc, char **argv, char **envp);
int main(int argc, char *argv[], char *envp[]);

execve加载了filename之后,启动代码准备栈,并将控制传递给新程序的主函数,函数原形如代码中所示。

15、深入理解计算机系统笔记:进程控制15、深入理解计算机系统笔记:进程控制

15、深入理解计算机系统笔记:进程控制

通过getenv,setenv,unsetenv来设备环境变量。

6、进程是执行中程序的一个特殊实例;程序总是运行在某个进程的上下文中。fork函数在新的子进程中运行相同的程序,新的子进程是父进程的一个复制品;execve函数在当前进程的上下文中加载并运行一个新的程序,它会覆盖当前进程的地址空间,但并没有创建一个新进程,新的程序仍然有相同的PID,并且继承了调用execve函数时打开的所有文件描述符。

示例代码

/* $begin shellmain */
#include "csapp.h"
#define MAXARGS   128

/* function prototypes */
void eval(char*cmdline);
int parseline(char *buf, char **argv);
int builtin_command(char **argv); 

int main() 
{
    char cmdline[MAXLINE]; /* command line */

    while (1) {
	/* read */
	printf("> ");                   
	Fgets(cmdline, MAXLINE, stdin); 
	if (feof(stdin))
	    exit(0);

	/* evaluate */
	eval(cmdline);
    } 
}
/* $end shellmain */
  
/* $begin eval */
/* eval - evaluate a command line */
void eval(char *cmdline) 
{
    char *argv[MAXARGS]; /* argv for execve() */
    char buf[MAXLINE];   /* holds modified command line */
    int bg;              /* should the job run in bg or fg? */
    pid_t pid;           /* process id */
    
    strcpy(buf, cmdline);
    bg = parseline(buf, argv); 
    if (argv[0] == NULL)  
	return;   /* ignore empty lines */

    if (!builtin_command(argv)) { 
	if ((pid = Fork()) == 0) {   /* child runs user job */
	    if (execve(argv[0], argv, environ) < 0) {
		printf("%s: Command not found.\n", argv[0]);
		exit(0);
	    }
	}

	/* parent waits for foreground job to terminate */
	if (!bg) {
	    int status;
	    if (waitpid(pid, &status, 0) < 0)
		unix_error("waitfg: waitpid error");
	}
	else
	    printf("%d %s", pid, cmdline);
    }
    return;
}

/* if first arg is a builtin command, run it and return true */
int builtin_command(char **argv) 
{
    if (!strcmp(argv[0], "quit")) /* quit command */
	exit(0);  
    if (!strcmp(argv[0], "&"))    /* ignore singleton & */
	return 1;
    return 0;                     /* not a builtin command */
}
/* $end eval */

/* $begin parseline */
/* parseline - parse the command line and build the argv array */
int parseline(char *buf, char **argv) 
{
    char *delim;         /* points to first space delimiter */
    int argc;            /* number of args */
    int bg;              /* background job? */

    buf[strlen(buf)-1] = ' ';  /* replace trailing '\n' with space */
    while (*buf && (*buf == ' ')) /* ignore leading spaces */
	buf++;

    /* build the argv list */
    argc = 0;
    while ((delim = strchr(buf, ' '))) {
	argv[argc++] = buf;
	*delim = '\0';
	buf = delim + 1;
	while (*buf && (*buf == ' ')) /* ignore spaces */
	       buf++;
    }
    argv[argc] = NULL;
    
    if (argc == 0)  /* ignore blank line */
	return 1;

    /* should the job run in the background? */
    if ((bg = (*argv[argc-1] == '&')) != 0)
	argv[--argc] = NULL;

    return bg;
}
/* $end parseline */

<Computer Systems:A Programmer's Perspective>

注:[1] 原书8.4节有详细的论述。

上一篇:路由器IOS升级方法总结


下一篇:安全研究员公开一千万密码库