fork()
fork是创建进程的唯一途径。它实际上是做一个调用它的进程的精确拷贝,包括文件描述符,寄存器值等所有内容。 调用fork后,原进程和所得的拷贝各自执行,互不相关。在执行fork时,二者所有的对应变量都有相同的值,但由于子进程在创建过程中对父进程的数据作了一个拷贝,所以在此之后,其中任一进程中变量值的改变不会对另一个进程产生任何影响(正文段不可修改,它由父、子进程共享)。 正确情况下fork的返回值对子进程为0,对父进程为一个正整数,即子进程的标识号pid。通过该返回值可以将父子进程区分开来。
waitpid(pid, &statloc, opts) //等待一个子进程结束
该系统调用使调用进程阻塞直到子进程中的任一个结束(若将其第一个参数置为-1),也可以等待一指定的子进程结束。waitpid结束时,子进程的终止状态值(正常结束或异常结束,以及正常结束时的返回值)被放在第二个参数指向的地址单元。
execve(name, argv, envp) //替换一个进程的核心映像
一般情况下,exec有三个参数:待执行的文件名,指向参数数组的指针和指向环境变量数组的指针。
一个高度简化的shell框架
while (TRUE) { /* 无限循环 */
read_command(command, parameters); /* 从终端读取输入 */
if ( fork() != 0 ) { /* 创建子进程 */
/* Parent code */
waitpid( -1, &status, 0); /* 等待子进程退出 */
} else {
/* Child code */
execve(command, parameters, 0); /* 执行命令 */
}
}
exit(status) //终止进程的执行并返回状态
EXIT系统调用的作用是结束一个进程,它只有一个参数指定其出口值(0~255)。这个值通过wait和waitpid系统调用中的变量status,返回给父进程。status的低字节存放结束状态,0为正常结束,其他值均表示出错。status的高字节包含子进程的出口值(0~255)。例如若父进程执行:n = waitpid( -1, &status, options);则父进程被阻塞直至有一个子进程结束。如果子进程结束时将4作为exit的参数值,则父进程唤醒时n被置为该结束子进程的pid,而status的值为0x0400。
brk(addr) //设置数据段大小
MINIX中进程的存储空间分为三部分:正文段(text segment,即程序代码),数据段(data segment,即变量)和堆栈段(stack segment) 。数据段是向上增长而堆栈向下增长,如图1-11所示。在两者之间是空闲区。堆栈的增长随程序的执行自动进行,而数据段的扩展则通过BRK系统调用显式地完成,BRK有一个参数指定数据段的结束地址,它可以比当前值大(数据段扩展),或比当前值小(数据段缩小)。该参数必须小于堆栈指针,否则堆栈和数据段将重叠,这是不允许的。
出于为程序员的方便考虑,系统提供了一个库函数sbrk来改变数据段大小,它的参数是数据段的变化量(负数表示数据段缩小)。其原理是跟踪数据段的当前值,(该值由BRK返回)。计算其新的大小,然后申请所需的空间。BRK和sbrk均与实现密切相关,所以不是POSIX的组成部分。
pid = getpid() //返回调用进程的标识号
pid = getpgrp() //返回调用进程的组号
pid = setsid() //创建一个新的会话并返回其组标识
另一个简单的系统调用getpid返回调用进程的进程标识号,注意在调用fork时,只有父进程能够获得子进程的进程标识号。如果子进程要得到自己的进程标识号,它必须使用getpid。同样地,getgrp返回进程的组标识号。SETSID系统调用启动一个新的会话(session),并将进程组的pid设为调用者的pid。
I = ptrace(req, pid, addr, data) //用于调试
它被调试器用来对被调试程序进行控制,通过ptrace,调试器可以读写被控进程的地址空间并实施其他控制。