1.文件操作
1.1 fcnlt函数
#include <unistd.h>
#include <fcntl.h>
int fcntl(int fd, int cmd, ... /* arg */);
功能:改变已打开的文件性质,fcntl针对描述符提供控制。
参数:
fd:操作的文件描述符
cmd:操作方式
arg:针对cmd的值,fcntl能够接受第三个参数int arg。
返回值:
成功:返回某个其他值
失败:-1
fcntl函数有5种功能:
1) 复制一个现有的描述符(cmd=F_DUPFD)
2) 获得/设置文件描述符标记(cmd=F_GETFD或F_SETFD)
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
int main ( )
{
int fd = - 1 ;
int newfd = - 1 ;
int ret = - 1 ;
fd = open ( "txt" , O_WRONLY | O_CREAT, 0644 ) ;
if ( - 1 == fd)
{
perror ( "open" ) ;
return 1 ;
}
printf ( "fd = %d\n" , fd) ;
newfd = fcntl ( fd, F_DUPFD, 0 ) ;
if ( - 1 == newfd)
{
perror ( "fcntl" ) ;
return 1 ;
}
printf ( "newfd = %d\n" , newfd) ;
write ( fd, "123456789" , 9 ) ;
write ( newfd, "ABCDEFG" , 7 ) ;
close ( fd) ;
close ( newfd) ;
return 0 ;
}
3) 获得/设置文件状态标记(cmd=F_GETFL或F_SETFL)
4) 获得/设置异步I/O所有权(cmd=F_GETOWN或F_SETOWN)
5) 获得/设置记录锁(cmd=F_GETLK, F_SETLK或F_SETLKW)
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
int main ( )
{
int fd = - 1 ;
int newfd = - 1 ;
int ret = - 1 ;
fd = open ( "txt" , O_WRONLY | O_CREAT, 0644 ) ;
if ( - 1 == fd)
{
perror ( "open" ) ;
return 1 ;
}
printf ( "fd = %d\n" , fd) ;
ret = fcntl ( fd, F_GETFL) ;
if ( - 1 == ret)
{
perror ( "fcntl" ) ;
return 1 ;
}
if ( ret & O_APPEND)
{
printf ( "has O_APPEND\n" ) ;
}
else
{
printf ( "not has O_APPEND\n" ) ;
}
ret = ret | O_APPEND;
ret = fcntl ( fd, F_SETFL, ret) ;
if ( - 1 == ret)
{
perror ( "fcntl" ) ;
return 1 ;
}
ret = fcntl ( fd, F_GETFL) ;
if ( - 1 == ret)
{
perror ( "fcntl" ) ;
return 1 ;
}
if ( ret & O_APPEND)
{
printf ( "has O_APPEND\n" ) ;
}
else
{
printf ( "not has O_APPEND\n" ) ;
}
close ( fd) ;
return 0 ;
}
2. 目录操作
2.1 getcwd函数
#include <unistd.h>
char *getcwd(char *buf, size_t size);
功能:获取当前进程的工作目录
参数:
buf : 缓冲区,存储当前的工作目录
size : 缓冲区大小
返回值:
成功:buf中保存当前进程工作目录位置
失败:NULL
2.2 chdir函数
#include <unistd.h>
int chdir(const char *path);
功能:修改当前进程(应用程序)的路径
参数:
path:切换的路径
返回值:
成功:0
失败:-1
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
int main ( )
{
char buffer[ 32 ] ;
int ret = - 1 ;
memset ( buffer, 0 , 32 ) ;
if ( NULL == getcwd ( buffer, 32 ) )
{
perror ( "getcwd" ) ;
return 1 ;
}
printf ( "buf:%s\n" , buffer) ;
ret = chdir ( "/home/xujie" ) ;
if ( - 1 == ret)
{
perror ( "chdir" ) ;
return 1 ;
}
memset ( buffer, 0 , 32 ) ;
if ( NULL == getcwd ( buffer, 32 ) ) ;
{
perror ( "getcwd" ) ;
return 1 ;
}
printf ( "buffer:%s\n" , buffer) ;
return 0 ;
}
2.3 opendir函数
#include <sys/types.h>
#include <dirent.h>
DIR *opendir(const char *name);
功能:打开一个目录
参数:
name:目录名
返回值:
成功:返回指向该目录结构体指针
失败:NULL
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>
int main ( )
{
DIR* dir = NULL ;
int ret = - 1 ;
dir = opendir ( "03tempDir" ) ;
if ( NULL == dir)
{
perror ( "opendir" ) ;
return 1 ;
}
printf ( "目录打开成功\n" ) ;
ret = closedir ( dir) ;
if ( - 1 == ret)
{
perror ( "closedir" ) ;
return 1 ;
}
printf ( "目录关闭成功!\n" ) ;
return 0 ;
}
2.4 closedir函数
#include <sys/types.h>
#include <dirent.h>
int closedir(DIR *dirp);
功能:关闭目录
参数:
dirp:opendir返回的指针
返回值:
成功:0
失败:-1
2.5 readdir函数
#include <dirent.h>
struct dirent *readdir(DIR *dirp);
功能:读取目录
参数:
dirp:opendir的返回值
返回值:
成功:目录结构体指针
失败:NULL
相关结构体说明:
struct dirent
{
ino_t d_ino; // 此目录进入点的inode
off_t d_off; // 目录文件开头至此目录进入点的位移
signed short int d_reclen; // d_name 的长度, 不包含NULL 字符
unsigned char d_type; // d_type 所指的文件类型
char d_name[256]; // 文件名
};
d_type文件类型说明:
取值 含义
DT_BLK 块设备
DT_CHR 字符设备
DT_DIR 目录
DT_LNK 软链接
DT_FIFO 管道
DT_REG 普通文件
DT_SOCK 套接字
DT_UNKNOWN 未知
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>
int main ( )
{
DIR* dir = NULL ;
int ret = - 1 ;
struct dirent* d = NULL ;
dir = opendir ( "03tempDir" ) ;
if ( NULL == dir)
{
perror ( "opendir" ) ;
return 1 ;
}
printf ( "目录打开成功\n" ) ;
while ( 1 )
{
d = readdir ( dir) ;
if ( NULL == d)
{
perror ( "readdir" ) ;
break ;
}
switch ( d-> d_type)
{
case DT_BLK:
printf ( "块设备!\n" ) ;
break ;
case DT_CHR:
printf ( "字符设备\n" ) ;
break ;
case DT_DIR:
printf ( "目录\n" ) ;
break ;
case DT_LNK:
printf ( "符号链接\n" ) ;
break ;
case DT_FIFO:
printf ( "管道文件\n" ) ;
break ;
case DT_REG:
printf ( "DT_REG文件\n" ) ;
break ;
case DT_SOCK:
printf ( "DT_SOCK文件\n" ) ;
break ;
default :
printf ( "other file\n" ) ;
break ;
}
printf ( "d_type:%hu\n d_name:%s\n" , d-> d_type, d-> d_name) ;
}
ret = closedir ( dir) ;
if ( - 1 == ret)
{
perror ( "closedir" ) ;
return 1 ;
}
printf ( "目录关闭成功!\n" ) ;
return 0 ;
}
3 进程和程序
3.1 基本概念
程序是存放在存储介质上的一个可执行文件,而进程是程序执行的过程。进程的状态
是变化的,其包括进程的创建、调度和消亡。程序是静态的,进程是动态的。
在 Linux 系统中,操作系统是通过进程去完成一个一个的任务,进程是管理事务的基
本单元。
进程拥有自己独立的处理环境(如:当前需要用到哪些环境变量,程序运行的目录在
哪,当前是哪个用户在运行此程序等)和系统资源(如:处理器 CPU 占用率、存储器、
I/O设备、数据、程序)。
3.2 并行和并发
并行:指在同一时刻,有多条指令在多个处理器上同时执行。
并发:指在同一时刻只能有一条指令执行,但多个进程指令被快速的轮换执行,使得
在宏观上具有多个进程同时执行的效果,但在微观上并不是同时执行的,只是把时间分
成若干段,使多个进程快速交替的执行。
3.3 进程的状态
进程状态反映进程执行过程的变化。这些状态随着进程的执行和外界条件的变化而转
换。
在三态模型中,进程状态分为三个基本状态,即运行态,就绪态,阻塞态。
在五态模型中,进程分为新建态、终止态,运行态,就绪态,阻塞态。
5种状态解释:
①TASK_RUNNING:进程正在被CPU执行。当一个进程刚被创建时会处于TASK_RUNNABLE,
表示己经准备就绪,正等待被调度。
②TASK_INTERRUPTIBLE(可中断):进程正在睡眠(也就是说它被阻塞)等待某
些条件的达成。一旦这些条件达成,内核就会把进程状态设置为运行。处于此状态的
进程也会因为接收到信号而提前被唤醒,比如给一个TASK_INTERRUPTIBLE状态的
进程发送SIGKILL信号,这个进程将先被唤醒(进入TASK_RUNNABLE状态),然后
再响应SIGKILL信号而退出(变为TASK_ZOMBIE状态),并不会从TASK_INTERRUPTIBLE状态直接退出。
③TASK_UNINTERRUPTIBLE(不可中断):处于等待中的进程,待资源满足时被唤
醒,但不可以由其它进程通过信号或中断唤醒。由于不接受外来的任何信号,因此无
法用kill杀掉这些处于该状态的进程。而TASK_UNINTERRUPTIBLE状态存在的意义就
在于,内核的某些处理流程是不能被打断的。如果响应异步信号,程序的执行流程中
就会被插入一段用于处理异步信号的流程,于是原有的流程就被中断了,这可能使某
些设备陷入不可控的状态。处于TASK_UNINTERRUPTIBLE状态一般总是非常短暂
的,通过ps命令基本上不可能捕捉到。
④TASK_ZOMBIE(僵死):表示进程已经结束了,但是其父进程还没有调用wait4或
waitpid()来释放进程描述符。为了父进程能够获知它的消息,子进程的进程描述符仍然
被保留着。一旦父进程调用了wait4(),进程描述符就会被释放。
⑤TASK_STOPPED(停止):进程停止执行。当进程接收到SIGSTOP,SIGTSTP,
SIGTTIN,SIGTTOU等信号的时候。此外,在调试期间接收到任何信号,都会使进程
进入这种状态。当接收到SIGCONT信号,会重新回到TASK_RUNNABLE。
3.4 ps
进程是一个具有一定独立功能的程序,它是操作系统动态执行的基本单元。
ps命令可以查看进程的详细状况,常用选项(选项可以不加“-”)如下:
选项 含义
-a 显示终端上的所有进程,包括其他用户的进程
-u 显示进程的详细状态
-x 显示没有控制终端的进程
-w 显示加宽,以便显示更多的信息
-r 只显示正在运行的进程
常用命令组合方式
ps aux :查看没有控制终端的所有进程
3.5 top
top命令用来动态显示运行中的进程。top命令能够在运行后,在指定的时间间隔更新
显示信息。可以在使用top命令时加上-d 来指定显示信息更新的时间间隔。
在top命令执行后,可以按下按键得到对显示的结果进行排序:
按键 含义
M 根据内存使用量来排序
P 根据CPU占有率来排序
T 根据进程运行时间的长短来排序
U 可以根据后面输入的用户名来筛选进程
K 可以根据后面输入的PID来杀死进程。
q 退出
h 获得帮助
3.6 kill
kill命令指定进程号的进程,需要配合 ps 使用。
使用格式:
kill [-signal] pid
信号值从0到15,其中9为绝对终止,可以处理一般信号无法终止的进程。
killall:通过进程名字杀死进程
3.7 进程号和相关函数
每个进程都由一个进程号来标识,其类型为 pid_t(整型),进程号的范围:0~
32767。进程号总是唯一的,但进程号可以重用。当一个进程终止后,其进程号就可以再
次使用。
进程号(PID):
标识进程的一个非负整型数。
父进程号(PPID):
任何进程( 除 init 进程)都是由另一个进程创建,该进程称为被创建进程的父进
程,对应的进程号称为父进程号(PPID)。如,A 进程创建了 B 进程,A 的进程号
就是 B 进程的父进程号。
进程组号(PGID):
进程组是一个或多个进程的集合。他们之间相互关联,进程组可以接收同一终端
的各种信号,关联的进程有一个进程组号(PGID)
3.7.1 getpid函数
#include <sys/types.h>
#include <unistd.h>
pid_t getpid(void);
功能:
获取本进程号(PID)
参数:
无
返回值:
本进程号
3.7.2 getppid函数
#include <sys/types.h>
#include <unistd.h>
pid_t getppid(void);
功能:
获取调用此函数的进程的父进程号(PPID)
参数:
无
返回值:
调用此函数的进程的父进程号(PPID)
3.7.3 getpgid函数
#include <sys/types.h>
#include <unistd.h>
pid_t getpgid(pid_t pid);
功能:
获取进程组号(PGID)
参数:
pid:进程号
返回值:
参数为 0 时返回当前进程组号,否则返回参数指定的进程的进程组号
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int main ( )
{
pid_t pid = - 1 ;
pid = getpid ( ) ;
printf ( "进程号:%d\n" , pid) ;
pid = getppid ( ) ;
printf ( "父进程号:%d\n" , pid) ;
pid = getpgid ( getpid ( ) ) ;
printf ( "当前进程进程组号:%d\n" , pid) ;
return 0 ;
}
3.8 进程的创建
系统允许一个进程创建新进程,新进程即为子进程,子进程还可以创建新的子进程,
形成进程树结构模型。
3.8.1 fork函数
#include <sys/types.h>
#include <unistd.h>
pid_t fork(void);
功能:
用于从一个已存在的进程中创建一个新进程,新进程称为子进程,原进程称为父进程。
参数:
无
返回值:
成功:子进程中返回 0,父进程中返回子进程 ID。pid_t,为整型。
失败:返回-1。
失败的两个主要原因是:
1)当前的进程数已经达到了系统规定的上限,这时 errno 的值被设置为 EAGAIN。
2)系统内存不足,这时 errno 的值被设置为 ENOMEM。
3.8.2 父子进程关系
使用 fork() 函数得到的子进程是父进程的一个复制品,它从父进程处继承了
整个进程的地址空间:包括进程上下文(进程执行活动全过程的静态描述)、进程
堆栈、打开的文件描述符、信号控制设定、进程优先级、进程组号等。
子进程所独有的只有它的进程号,计时器等(只有小量信息)。
准确来说,Linux 的 fork() 使用是通过写时拷贝 (copy- on-write)
实现。写时拷贝是一种可以推迟甚至避免拷贝数据的技术。内核此时并不复制整个
进程的地址空间,而是让父子进程共享同一个地址空间。只用在需要写入的时候才
会复制地址空间,从而使各个进行拥有各自的地址空间。也就是说,资源的复制是
在需要写入的时候才会进行,在此之前,只有以只读方式共享。
注:fork之后父子进程共享文件,fork产生的子进程与父进程相同的文件文件描述符
指向相同的文件表,引用计数增加,共享文件文件偏移指针。
3.8.3 区分父子进程
fork() 函数被调用一次,但返回两次。两次返回的区别是:子进程的返回值是
0,而父进程的返回值则是新子进程的进程 ID。
在子进程的地址空间里,子进程是从 fork() 这个函数后才开始执行代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main ( )
{
pid_t pid = - 1 ;
pid = fork ( ) ;
if ( 0 == pid)
{
printf ( "hello Linux| pid:%d ppid:%d\n" , getpid ( ) , getppid ( ) ) ;
exit ( 0 ) ;
}
else
{
printf ( "hello world| pid:%d cpid:%d\n" , getpid ( ) , pid) ;
}
return 0 ;
}
3.8.4 父子进程地址空间
父子进程各自的地址空间是独立的
在子进程修改变量的值,并不影响到父进程变量的值。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
int num = 100 ;
int main ( )
{
int var = 88 ;
pid_t pid = - 1 ;
int * p = NULL ;
p = malloc ( sizeof ( int ) ) ;
if ( NULL == p)
{
printf ( "malloc filed" ) ;
return 1 ;
}
* p = 200 ;
pid = fork ( ) ;
if ( - 1 == pid)
{
perror ( "fork" ) ;
return 1 ;
}
if ( 0 == pid)
{
sleep ( 1 ) ;
printf ( "子进程睡醒后 *p = %d, num = %d, var = %d\n" , * p, num, var) ;
free ( p) ;
p = NULL ;
}
else
{
printf ( "父进程之前 *p = %d, num = %d, var = %d\n" , * p, num, var) ;
++ var;
++ num;
( * p) ++ ;
printf ( "父进程之后 *p = %d, num = %d, var = %d\n" , * p, num, var) ;
free ( p) ;
p = NULL ;
}
return 0 ;
}
3.9 进程相关命令
1 ps命令
ps命令可以查看进程的详细状况,常用选项(选项可以不加“-”)如下:
选项 含义
-a 显示终端上的所有进程,包括其他用户的进程
-u 显示进程的详细状态
-x 显示没有控制终端的进程
-j 列出与作业控制相关的信息
显示当前用户下所有进程:ps aux
以比较完整的格式显示所有的进程:ps ajx
2 kill命令
1.命令功能: 发送指定的信号到相应进程。
kill 9133:9133 为应用程序所对应的进程号
2.查看信号编号: kill -l(字母)
3.有些进程不能直接杀死,这时候我们需要加一个参数“-9”,使用9号信号SIGKILL
杀死进程: kill -SIGKILL/(-9) 89899【进程标识号】
3.10 GDB调试多进程
使用gdb调试的时候,gdb只能跟踪一个进程。可以在fork函数调用之前,通过指令
设置gdb调试工具跟踪父进程或者是跟踪子进程。默认跟踪父进程。
set follow-fork-mode child 设置gdb在fork之后跟踪子进程。
set follow-fork-mode parent 设置跟踪父进程(默认)。
注意,一定要在fork函数调用之前设置才有效。
3.11 进程退出函数
#include <stdlib.h>
void exit(int status);
#include <unistd.h>
void _exit(int status);
功能:
结束调用此函数的进程。
参数:
status:返回给父进程的参数(低 8 位有效),至于这个参数是多少根据需要来填写。
返回值:
无
exit() 和 exit() 函数功能和用法是一样的,无非时所包含的头文件不一样,还有的区别就是:exit()属于标准库函数,exit()属于系统调用函数。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
int main ( )
{
printf ( "hello world" ) ;
_exit ( 0 ) ;
}
3.12 等待子进程退出函数
3.12.1 概述
1.在每个进程退出的时候,内核释放该进程所有的资源、包括打开的文件、占用
的内存等。但是仍然为其保留一定的信息,这些信息主要主要指进程控制块PCB的
信息(包括进程号、退出状态、运行时间等)。
2.父进程可以通过调用wait或waitpid得到它的退出状态同时彻底清除掉这个进程。
3.wait() 和 waitpid() 函数的功能一样,区别在于,wait() 函数会阻塞,
waitpid() 可以设置不阻塞,waitpid() 还可以指定等待哪个子进程结束。
注意:一次wait或waitpid调用只能清理一个子进程,清理多个子进程应使用循环。
3.12.2 wait函数
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status);
功能:
等待任意一个子进程结束,如果任意一个子进程结束了,此函数会回收该子进程的资源。
参数:
status : 进程退出时的状态信息。
返回值:
成功:已经结束子进程的进程号
失败: -1
1.调用 wait() 函数的进程会挂起(阻塞),直到它的一个子进程退出或收到
一个不能被忽视的信号时才被唤醒(相当于继续往下执行)。
2.若调用进程没有子进程,该函数立即返回;若它的子进程已经结束,该函数同
样会立即返回,并且会回收那个早已结束进程的资源。
3.所以,wait()函数的主要功能为回收已经结束子进程的资源。
4.如果参数 status 的值不是 NULL,wait() 就会把子进程退出时的状态取出
并存入其中,这是一个整数值(int),指出了子进程是正常退出还是被非正常结
束的。
5.这个退出信息在一个 int 中包含了多个字段,直接使用这个值是没有意义的,
我们需要用宏定义取出其中的每个字段。
宏函数可分为如下三组:
1) WIFEXITED(status)
为非0 → 进程正常结束
WEXITSTATUS(status)
如上宏为真,使用此宏 → 获取进程退出状态 (exit的参数)
2) WIFSIGNALED(status)
为非0 → 进程异常终止
WTERMSIG(status)
如上宏为真,使用此宏 → 取得使进程终止的那个信号的编号。
3) WIFSTOPPED(status)
为非0 → 进程处于暂停状态
WSTOPSIG(status)
如上宏为真,使用此宏 → 取得使进程暂停的那个信号的编号。
WIFCONTINUED(status)
为真 → 进程暂停后已经继续运行
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int main ( )
{
int status = 0 ;
pid_t pid = - 1 ;
int ret = - 1 ;
pid = fork ( ) ;
if ( - 1 == pid)
{
perror ( "fprk" ) ;
return 1 ;
}
if ( 0 == pid)
{
for ( int i = 0 ; i< 5 ; i++ )
{
printf ( "子进程:%d 运行数字:%d\n" , getpid ( ) , i) ;
sleep ( 1 ) ;
}
exit ( 10 ) ;
}
printf ( "父进程等待子进程退出,回收其资源\n" ) ;
ret = wait ( & status) ;
if ( - 1 == ret)
{
perror ( "wait" ) ;
return 1 ;
}
printf ( "父进程回收子进程资源\n" ) ;
if ( WIFEXITED ( status) )
{
printf ( "子进程退出状态码为:%d\n" , WEXITSTATUS ( status) ) ;
}
else if ( WIFSIGNALED ( status) )
{
printf ( "子进程被杀死了,状态码为:%d\n" , WTERMSIG ( status) ) ;
}
else if ( WIFSTOPPED ( status) )
{
printf ( "子进程被暂停了,状态码为:%d\n" , WSTOPSIG ( status) ) ;
}
return 0 ;
}
3.12.3 waitpid函数
函数说明:
#include <sys/types.h>
#include <sys/wait.h>
pid_t waitpid(pid_t pid, int *status, int options);
功能:
等待子进程终止,如果子进程终止了,此函数会回收子进程的资源。
参数:
pid : 参数 pid 的值有以下几种类型:
pid > 0 等待进程 ID 等于 pid 的子进程。
pid = 0 等待同一个进程组中的任何子进程,如果子进程已经加入了别的进程组,waitpid 不会等待它。
pid = -1 等待任一子进程,此时 waitpid 和 wait 作用一样。
pid < -1 等待指定进程组中的任何子进程,这个进程组的 ID 等于 pid 的绝对值。
status : 进程退出时的状态信息。和 wait() 用法一样。
options : options 提供了一些额外的选项来控制 waitpid()。
0:同 wait(),阻塞父进程,等待子进程退出。
WNOHANG:没有任何已经结束的子进程,则立即返回。
WUNTRACED:如果子进程暂停了则此函数马上返回,并且不予以理会子进
程的结束状态。(由于涉及到一些跟踪调试方面的知识,加之极少用到)
返回值:
waitpid() 的返回值比 wait() 稍微复杂一些,一共有 3 种情况:
1) 当正常返回的时候,waitpid() 返回收集到的已经回收子进程的进程号;
2) 如果设置了选项 WNOHANG,而调用中 waitpid() 发现没有已退出的子进程
可等待,则返回 0;
3) 如果调用中出错,则返回-1,这时 errno 会被设置成相应的值以指示错
误所在,如:当 pid 所对应的子进程不存在,或此进程存在,但不是调用
进程的子进程,waitpid() 就会出错返回,这时 errno 被设置为 ECHILD;
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int main ( )
{
int status = 0 ;
pid_t pid = - 1 ;
pid = fork ( ) ;
perror ( "fprk" ) ;
return 1 ;
}
if ( 0 == pid)
{
for ( int i = 0 ; i< 5 ; i++ )
{
printf ( "子进程:%d 运行数字:%d\n" , getpid ( ) , i) ;
sleep ( 1 ) ;
}
exit ( 10 ) ;
}
printf ( "父进程等待子进程退出,回收其资源\n" ) ;
ret = waitpid ( - 1 , & status, WNOHANG) ;
那么就立即退出
if ( - 1 == ret)
{
perror ( "wait" ) ;
return 1 ;
}
printf ( "父进程回收子进程资源\n" ) ;
if ( WIFEXITED ( status) )
{
printf ( "子进程退出状态码为:%d\n" , WEXITSTATUS ( status) ) ;
}
else if ( WIFSIGNALED ( status) )
{
printf ( "子进程被杀死了,状态码为:%d\n" , WTERMSIG ( status) ) ;
}
else if ( WIFSTOPPED ( status) )
{
printf ( "子进程被暂停了,状态码为:%d\n" , WSTOPSIG ( status) ) ;
}
return 0 ;
}