一、进程的管理
1.1 各种ID的获取
(1)getpid() / getppid()
#include <unistd.h>
#include <sys/types.h>
pid_t getpid(void); //pid_t = int
pid_t getppid(void);
-
功能:
-
getpid() - 获取当前进程的编号;
getppid() - 获取当前进程的父进程的编号;
(2)getuid() / geteuid()
#include <unistd.h>
#include <sys/types.h>
uid_t getuid(void); //uid_t = unsigned int
uid_t geteuid(void);
-
功能:
-
getuid() - 获取真实用户的ID,即执行程序过程中采用的ID;
geteuid() - 获取有效用户的ID,即未经su username
切换的ID;
(3)getgid() / getegid()
#include <unistd.h>
#include <sys/types.h>
gid_t getgid(void); //gid_t = unsigned int
gid_t getegid(void);
-
功能:
-
getgid() - 获取当前用户所在组的编号;
getegid() - 获取原始用户所在组的编号;(用户未经su username
切换)
1.2 进程的创建
(1) fork()
#include <unistd.h>
pid_t fork(void); //pid_t = int
功能:
主要用于以复制当前正在调用进程的方式创建新进程,其中新创建出来的进程叫做子进程,当前正在调用的进程叫做父进程;
返回值
success —- 父进程返回子进程的进程号,子进程返回0,error —- 父进程返回-1,没有子进程;
注意:
使用fork()创建子进程之后,父子进程的执行先后次序由操作系统的调用算法决定;
(2)父子进程的执行方式:
a. 对于fork()调用之前的代码来说,父进程执行一次;
b. 对于fork()调用之后的代码来说,父子进程各自执行一次;
c. 对于fork()调用的返回值来说,父子进程各自返回一个,其中父进程返回子进程的进程号,子进程返回0;
(3)父子进程之间的关系
a. 父进程启动了子进程,父子进程同时执行,如果子进程先于父进程结束,会给父进程发信号,父进程负责回收子进程的资源;
b. 如果父进程先结束,则子进程就会变成孤儿进程,此时子进程会变更父进程(一般认定init(1)为新的父进程),init进程收养了孤儿进程,因此被叫做孤儿院进程;
c. 如果子进程先结束,但是由于各种原因父进程没有收到子进程发来的信号,也就不能回收子进程的资源,此时子进程就会变成僵尸进程;
(4)父子进程之间的复制关系
使用fork()创建子进程后,子进程会复制父进程中除了代码区之外的其他内存区域,而代码区和父进程共享;
使用fork()创建子进程后,子进程会复制父进程中的文件描述符总表,但不会复制文件表等数据结构,因此父子进程对应的是同一个文件表结构;
-
扩展:
-
a. 如何创建3个新进程总共变成4个进程???
fork();fork();
4个进程:1个父进程+2个子进程+1个孙子进程
b. 如何创建2个新进程总共变成3个进程???
pid_t pid = fork();
if(pid > 0)
{
fork();
}
3个进程:1个父进程+2个子进程
c.while(1){fork();}
被称为fork()炸弹;
1.3 进程的终止
(1)正常终止进程的方式
a. 执行了main()
中的return语句;
b. 调用exit()
终止进程;
c. 调用_exit()
/ _Exit()
终止进程;
d. 最后一个线程返回;
e. 最后一个线程调用了pthread_exit()
;
(2)非正常终止进程的方式
a. 采用信号终止进程;
b. 最后一个线程被其他线程取消;
(3)相关函数的解析
#include <unistd.h>
void _exit(int status); //UC函数
#include <stdlib.h>
void _Exit(int status); //标C函数
功能:
主要用于立即终止当前正在调用的进程,在终止的同时,会关闭所有属于该进程的文件描述符;让该进程的所有子进程变更父进程为init进程;给该进程的父进程发送SIGCHLD信号;
参数status的数值主要用于返回给父进程作为当前进程的退出状态信息,若父进程想要获取该退出状态信息,则需要调用wait系列函数中的一个;
The function _Exit()
is equivalent to _exit()
.
#include <stdlib.h>
void exit(int status);
功能:
主要用于引起正常进程的终止,参数status & 0377
的结果会被返回给父进程,作为当前进程的退出状态信息,父进程若要获取该信息,也得调用wait系列函数中的一个;
该函数在终止进程时,会引起atexit()
和on_exit()
所注册(单独保存)函数的调用;
#include <stdlib.h>
int atexit(void (*function)(void));
功能:
主要用于注册(单独保存)参数指定的函数,该函数会在正常进程终止时被调用,而正常终止进程的方式主要有两种:a. 调用exit(); b. 执行了main()中的return;
返回值:
success —- 0,error —- 非0;
#include <stdlib.h>
int on_exit(void (*function)(int , void *), void *arg);
功能:
主要用于注册参数指定的函数,该函数会在正常进程终止时被调用。on_exit()
能够将指针类型参数arg
传递给函数function()
返回值:
success —- 0,error —- 非0;
1.4 进程的等待
(1)wait()
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *wstatus);
-
功能:
-
主要用于挂起当前正在调用进程的执行,直到该进程中的任意一个子进程终止为止;
当参数不为空时,该函数会将获取到的退出状态信息填充到该参数指定的int类型存储空间中;
为了正确解析该退出状态信息,需要使用以下的带参宏;
WIFEXITED(wstatus)
- - - -判断子进程是否正常终止,正常终止的方式有:调用_exit() / exit() / _Exit() / 执行了main()中的return;
WEXITSTATUS(wstatus)
- -获取子进程的退出状态信息;
WIFSIGNALED(wstatus)
- -判断子进程是(true)/否(false)被信号终止;
WTERMSIG(wstatus)
- - - - -获取终止子进程的信号值;
返回值:
- success —- 终止子进程的进程号,error —- -1;
(2)waitpid()
#include <sys/types.h>
#include <sys/wait.h>
pid_t waitpid(pid_t pid, int *wstatus, int options);
-
功能:
- 主要用于等待参数指定的子进程状态发生改变; 参数:
-
第一个参数:进程的编号;
<-1 - - - 表示等待任意一个拥有和pid绝对值相等的进程组编号的子进程状态发生改变
-1 - - - - 表示等待任意一个子进程状态发生改变
0 - - - - -表示等待任意一个拥有和调用进程相等的进程组编号的子进程状态发生改变
>0 - - - -表示等待进程号为pid的子进程状态发生改变
第二个参数:指针类型参数获取子进程的退出状态信息;
第三个参数:等待的方式,默认给0即可;
返回值:
- success —- 终止子进程的进程号,error —- -1;
The call wait(&wstatus)
is equivalent to: waitpid(-1, &wstatus, 0)
;
扩展:
重定义在同一个作用域中定义多个相同变量。
练习:
vi 08waitpid.c文件,首先创建两个子进程,子进程一先开始执行,睡眠10秒后终止,调用exit(100); 然后子进程二开始执行,睡眠20秒后终止,调用exit(200); 父进程等待任意一个子进程终止,并获取退出状态信息,最后打印出来;
-
明天预报:
-
(1)进程的管理
(2)信号的处理