#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
- 父进程等待子进程完成。这种情况下,父进程无需对其描述符做任何处理。
- 父进程和子进程各自执行不同的程序段。这种情况下,fork之后,父子进程各自它们不需要使用的文件描述符。
strlen和sizeof的区别:前者不包括null字节,一次函数调用;后者包括null字节,编译时计算
除了文件描述符之外,父进程的很多其他属性也由子进程继承,包括:
- 实际用户ID、实际组ID、有效用户ID、有效组ID
- 附属组ID
- 进程组ID
- 会话ID
- 控制终端
- SUID和SGID标志(stat结构的st_mode成员)
- 当前工作目录
- 根目录
- 文件模式创建屏蔽字umask
- 信号屏蔽和处理
- 对任一打开文件描述符的执行时关闭(close-on-exec)标志
- 环境
- 连接的共享存储段
- 存储映像
- 资源限制
- 是否继承nice值由具体实现自行决定
父进程和子进程之间的区别具体如下:
- fork的返回值不同
- pid不同
- 这两个进程的父进程不同
- 子进程的tms_utime、tms_stime、tms_cutime和tms_ustime的值设置为0
- 子进程不继承父进程设置的文件锁
- 子进程的未处理闹钟被清除
- 子进程的未处理信号集设置为空集
fork失败的两个主要原因:
- 系统中已经有了太多的进程
- 该实际用户ID的进程总数超过了系统限制
fork有以下两种用法:
- 一个父进程希望复制自己,使父进程和子进程同时执行不同的代码段。这在网络服务器中是常见的。
- 一个进程要执行一个不同的程序。这对shell是常见的情况。某些系统将fork+exec组合成一个操作spawn
- vfork函数用于创建一个新进程,而该新进程的目的是exec一个新程序,故不将父进程的地址空间完全复制到子进程中,因为子进程会立即调用exec(或exit),于是也就不会引用该地址空间。不管在子进程调用exec或exit之前,它在父进程的空间中运行。
- 另一个区别是vfork保证子进程先运行,在它调用exec或exit之后父进程才可能被调度运行。故如果在调用这两个函数之前子进程依赖于父进程的进一步动作,则会导致死锁。
正常终止
方式:
- 从main中执行return,等效于调用exit
- 调用exit函数,调用各终止处理程序,关闭标准I/O流,最后调用_exit函数
- 调用_exit或_Exit
- 进程的最后一个线程在其启动例程执行return语句,该进程以终止状态0返回
- 进程的最后一个线程调用pthread_exit,进程终止状态总是0
异常终止
方式:
- 调用abort,它产生SIGABRT信号
- 当进程接收到某些信号时,信号可由进程自身(如调用abort函数)、其他进程或内核产生
- 最后一个线程对“取消”请求做出响应
注意:“退出状态”(3个exit函数的参数或main的返回值)区别于“终止状态”。在最后调用_exit时,内核将退出状态转换为终止状态。
如果父进程在子进程之前终止,则称子进程为孤儿进程
。子进程 ppid变为1,称这些进程由init进程收养
。一个init进程收养的进程终止时,init会调用一个wait函数取得其终止状态,防止它成为僵尸进程。
僵尸进程
zombie/defunct。#include <sys/wait.h>
pid_t wait(int *statloc);
pid_t waitpid(pid_t pid, int *statloc, int options);
Both return: process ID if OK, 0 (see later), or −1 on error
- 如果其所有子进程都还在运行,则阻塞
- 如果一个子进程终止,正等待其父进程获取其终止状态,则取得该子进程的终止状态立即返回
- 如果它没有任何子进程,则立即出错返回
- waitpid有一选项,可使调用者不阻塞
- waitpid可以控制它所等待的进程
若statloc不是NULL,则终止进程的终止状态就存放在它所指向的单元内。该整型状态字由实现定义,其中某些位表示退出状态(正常返回),其他位则指示信号编号(异常返回),有一位指示是否产生了core文件。
waitpid函数中的pid参数的解释:
pid == -1,等待任一子进程,等价于wait函数pid > 0,等待pid等于该值的子进程pid == 0,等待组ID等于调用进程组ID的任一子进程pid < 0,等待组ID等于pid绝对值的任一子进程
waitpid函数中的options参数:WNOHANG(不阻塞)、WCONTINUED、WUNTRACED
如果一个进程fork一个子进程,但不要它等待子进程终止,也不希望子进程处于僵尸状态直到父进程终止,实现这一要求的诀窍是调用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 = %ld/n", (long)getppid());
exit(0);
新闻热点
疑难解答