Linux系统编程Day05

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>

//fcnl复制
int main()
{
        int fd = -1;
        int newfd = -1;
        int ret = -1;

        //1.打开文件
        fd = open("txt", O_WRONLY | O_CREAT, 0644);
        if(-1 == fd)
        {
                perror("open");
                return 1;
        }
        printf("fd = %d\n",fd);

        //2.文件描述符复制
        //等价于dup函数
        //第三个参数0 表示返回一个最小的可用的文件描述符,并且大于或者等于0
        newfd = fcntl(fd, F_DUPFD, 0);
        if(-1 == newfd)
        {
                perror("fcntl");
                return 1;
        }
        printf("newfd = %d\n",newfd);

        //3.写操作
        write(fd,"123456789",9);
        write(newfd,"ABCDEFG",7);

        //4.关闭文件
        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>

//fcnl设置和获取文件状态标志
int main()
{
        int fd = -1;
        int newfd = -1;
        int ret = -1;

        //1.打开文件
        fd = open("txt", O_WRONLY | O_CREAT, 0644);
        if(-1 == fd)
        {
                perror("open");
                return 1;
        }
        printf("fd = %d\n",fd);

        //2.先获取文件状态标记
        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");
        }

        //3.设置文件状态标记
        ret = ret | O_APPEND;
        ret = fcntl(fd, F_SETFL, ret);
        if(-1 == ret)
        {
                perror("fcntl");
                return 1;
        }

        //4.获取文件状态标记
        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");
        }

        //5.关闭文件
        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>

//getcwd 和 chdir
int main()
{
        //1.获取当前进程工作目录
        char buffer[32];
        int ret = -1;
        memset(buffer,0,32);
        if(NULL == getcwd(buffer,32))
        {
                perror("getcwd");
                return 1;
        }

        printf("buf:%s\n",buffer);

        //2.改变当前进程工作目录
        ret = chdir("/home/xujie");
        if(-1 == ret)
        {
                perror("chdir");
                return 1;
        }

        //3.获取当前进程工作目录
        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;

        //1.打开目录
        dir = opendir("03tempDir");
        if(NULL == dir)
        {
                perror("opendir");
                return 1;
        }
        printf("目录打开成功\n");


        //2.关闭目录
        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;

        //1.打开目录
        dir = opendir("03tempDir");
        if(NULL == dir)
        {
                perror("opendir");
                return 1;
        }
        printf("目录打开成功\n");

        //2.循环读取目录内容
        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);

        }


        //3.关闭目录
        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();
        //fork函数在子进程中返回0 在父进程中返回子进程的pid
        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>

//查看内存泄漏命令
//valgrind ./a.out

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");

        //return 0;
        //exit(0);
        _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,0);//等价于wait
        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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值