赞
踩
下面是3种状态转换图
当然理论上上述三种状态之间转换分为六种情况:
以下两种状态是不可能发生的:
我们知道,每个进程在内核中都有一个进程控制块(PCB)来维护进程相关的信息,Linux内核的进程控制块是task_struct结构体。其内部成员有很多,我们重点掌握以下部分即可:
fork函数 创建一个子进程。
getpid函数 获取当前进程ID
getppid函数 获取当前进程的父进程ID
- #include <stdio.h>
- #include <unistd.h>
- #include <stdlib.h>
-
- int var = 34;
-
- int main(void)
- {
- pid_t pid;
-
- pid = fork();
- if (pid == -1)
- {
- perror("fork");
- exit(-1);
- }
- else if (pid > 0)
- {
- sleep(2);
- var = 55;
- printf("I'm parent pid = %d, parentID = %d, var = %d\n", getpid(), getppid(), var);
- }
- else if (pid == 0)
- {
- var = 100;
- printf("I'm child pid = %d, parentID = %d, var = %d\n", getpid(), getppid(), var);
- }
-
- printf("var = %d\n", var);
-
- return 0;
- }
运行结果
getuid函数 获取当前进程实际用户ID
geteuid函数 获取当前进程有效用户ID
getgid函数 获取当前进程使用用户组ID
getegid 获取当前进程有效用户组ID
查看进程信息
循环创建n个子进程
循环创建N个子进程
练习:通过命令行参数指定创建进程的个数,每个进程休眠1S打印自己是第几个被创建的进程。如:第1个子进程休眠0秒打印:“我是第1个子进程”;第2个进程休眠1秒打印:“我是第2个子进程”;第3个进程休眠2秒打印:“我是第3个子进程”。
- #include <stdio.h>
- #include <unistd.h>
- #include <stdlib.h>
-
- int main(int argc, char *argv[])
- {
- int n, i; //默认创建5个子进程
-
- if (argc == 2)
- {
- n = atoi(argv[1]);
- }
-
- for (i = 0; i < n; i++) //出口1,父进程专用出口
- if (fork() == 0)
- break; //出口2,子进程出口,i不自增
-
- if (n == i)
- {
- sleep(n);
- printf("I am parent, pid = %d, parent pid = %d\n", getpid(), getppid());
- }
- else
- {
- sleep(i);
- printf("I'm %dth child, pid = %d, parent pid = %d\n", i + 1, getpid(), getppid());
- }
-
- return 0;
- }
运行结果:
注意
- #include <stdio.h>
- #include <stdlib.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- #include <unistd.h>
- #include <sys/types.h>
- #include <sys/wait.h>
- #include <string.h>
-
- int main(void)
- {
- int fd;
- pid_t pid;
-
- fd = open("./text", O_RDWR | O_CREAT | O_TRUNC, 0755);
-
- if (fd < 0)
- {
- perror("open error");
- exit(-1);
- }
-
- pid = fork();
- if (pid < 0)
- {
- perror("fork error");
- exit(-1);
- }
- else if (pid == 0) // son
- {
- write(fd, "hello ", strlen("hello "));
- }
- else // parent
- {
- sleep(1);
- write(fd, "world!\n", strlen("world!\n"));
- wait(NULL);
- }
-
-
- return 0;
- }
练习:编写程序测试,父子进程是否共享全局变。
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
-
- int a = 100; //.data
-
- int main(void)
- {
- pid_t pid;
- pid = fork();
-
- if(pid == 0)
- { //son
- a = 2000;
- printf("child, a = %d\n", a);
- }
- else
- {
- sleep(1); //保证son先运行
- a = 3000;
- printf("parent, a = %d\n", a);
- }
-
- printf("a = %d\n", a);
-
- return 0;
- }
运行结果
全局变量也是遵循读时共享写时复制原则。
其实有六种以exec开头的函数,统称exec函数:
- int execl(const char *path, const char *arg, ...);
-
- int execlp(const char *file, const char *arg, ...);
-
- int execle(const char *path, const char *arg, ..., char *const envp[]);
-
- int execv(const char *path, char *const argv[]);
-
- int execvp(const char *file, char *const argv[]);
-
- int execve(const char *path, char *const argv[], char *const envp[]);
execl函数 加载一个进程, 通过 路径 + 程序名来加载。
int execl(const char *path, const char *arg, ...); 成功:无返回;失败:-1
execlp函数 加载一个进程,借助PATH环境变量
int execlp(const char *file, const char *arg, ...); 成功:无返回;失败:-1
- #include <stdio.h>
- #include <unistd.h>
- #include <stdlib.h>
-
- int main(void)
- {
- pid_t pid;
- pid = fork();
- if (pid == -1 )
- {
- perror("fork");
- exit(-1);
- }
- else if (pid > 0)
- {
- sleep(3);
- printf("I'm parent pid = %d, parentID = %d\n", getpid(), getppid());
- }
- else if (pid == 0)
- {
- //sleep(3);
- printf("I'm child pid = %d, parentID = %d\n", getpid(), getppid());
- //execl("/bin/ls", "ls", "-l", NULL);
- execlp("ls", "ls", "-l", NULL);
- perror("exec");
- exit(1);
- }
-
- printf("-------finish...%d\n", getpid());
-
- return 0;
- }
运行结果
execvp函数 加载一个进程,使用自定义环境变量env
int execvp(const char *file, const char *argv[]);
exec函数族一般规律
wait函数
① 阻塞等待子进程退出
② 回收子进程残留资源
③ 获取子进程结束状态(退出原因)。
pid_t wait(int *status); 成功:清理掉的子进程ID;失败:-1 (没有子进程)
父进程调用wait函数可以回收子进程终止信息。该函数有三个功能:当进程终止时,操作系统的隐式回收机制会:1.关闭所有文件描述符 ;2. 释放用户空间分配的内存。内核的PCB仍存在。其中保存该进程的退出状态。(正常终止→退出值;异常终止→终止信号)。可使用wait函数传出参数status来保存进程的退出状态。借助宏函数来进一步判断进程终止的具体原因。宏函数可分为如下三组:
1. WIFEXITED(status) 为非0 → 进程正常结束
WEXITSTATUS(status) 如上宏为真,使用此宏 → 获取进程退出状态 (exit的参数)
2. WIFSIGNALED(status) 为非0 → 进程异常终止
WTERMSIG(status) 如上宏为真,使用此宏 → 取得使进程终止的那个信号的编号。
*3. WIFSTOPPED(status) 为非0 → 进程处于暂停状态
WSTOPSIG(status) 如上宏为真,使用此宏 → 取得使进程暂停的那个信号的编号。
WIFCONTINUED(status) 为真 → 进程暂停后已经继续运行
- #include <unistd.h>
- #include <stdlib.h>
- #include <stdio.h>
- #include <sys/wait.h>
-
- int main(void)
- {
- pid_t pid, wpid;
- int status;
-
- pid = fork();
-
- if(pid == -1)
- {
- perror("fork error");
- exit(-1);
- }
- else if(pid == 0)
- { //son
- printf("I'm process child, pid = %d\n", getpid());
- sleep(10);
- exit(10);
- }
- else
- {
- printf("I'm process parent, pid = %d\n", getpid());
- wpid = wait(&status); //传出参数
-
- if(WIFEXITED(status))
- { //正常退出
- printf("I'm parent, The child process %d exit normally\n", wpid);
- printf("return value: %d\n", WEXITSTATUS(status));
-
- }
- else if (WIFSIGNALED(status))
- { //异常退出
- printf("The child process exit abnormally, killed by signal %d\n", WTERMSIG(status));//获取信号编号
- }
- else
- {
- printf("other...\n");
- }
- }
-
- return 0;
- }
运行结果
waitpid函数 作用同wait,但可指定pid进程清理,可以不阻塞。
pid_t waitpid(pid_t pid, int *status, in options); 成功:返回清理掉的子进程ID;失败:-1(无子进程)
特殊参数和返回情况:
参数pid:
options:
返回值:
注意:一次wait或waitpid调用只能清理一个子进程,清理多个子进程应使用循环。
- #include <unistd.h>
- #include <stdlib.h>
- #include <stdio.h>
- #include <sys/wait.h>
-
- int main(void)
- {
- pid_t pid, wpid;
- int flg = 0;
-
- pid = fork();
-
- if(pid == -1)
- {
- perror("fork error");
- exit(-1);
- }
- else if(pid == 0) // son
- {
- printf("I'm process child, pid = %d\n", getpid());
- sleep(5);
- exit(10);
- }
- else // parent
- {
- int stat_val;
-
- do {
- //wpid = waitpid(-1, &stat_val, WNOHANG); // 回收任意子进程(相当于wait)
- wpid = waitpid(pid, &stat_val, WNOHANG); // 回收指定ID的子进程
- //wpid = wait(NULL);
- printf("---wpid = %d--------%d\n", wpid, flg++);
- if(wpid == 0)
- {
- printf("NO child exited\n");
- sleep(1);
- }
- } while (wpid == 0); //子进程不可回收
-
- if(wpid == pid)
- { //回收了指定子进程
- printf("I'm parent, I catched child process, pid = %d\n", wpid);
-
- // 进程退出状态
- if (WIFEXITED(stat_val))
- printf("Child exited with code %d\n", WEXITSTATUS(stat_val));
- else if (WIFSIGNALED(stat_val))
- printf("Child terminated abnormally, signal %d\n", WTERMSIG(stat_val));
- }
- else
- {
- printf("other...\n");
- }
- }
-
- return 0;
- }
运行结果
waitpid回收多个子进程
- #include <stdio.h>
- #include <unistd.h>
- #include <stdlib.h>
- #include <sys/wait.h>
-
- int main(int argc, char *argv[])
- {
- int n, i;
- pid_t p, q;
-
- if(argc == 2)
- {
- n = atoi(argv[1]);
- }
-
- for(i = 0; i < n; i++) // 创建个进程
- {
- p = fork();
- if(p == 0)
- {
- break;
- }
- }
-
- if(n == i) // parent
- {
- int stat_val;
-
- sleep(n);
- printf("I am parent, pid = %d\n", getpid());
- for (i = 0; i < n; i++)
- {
- q = waitpid(0, &stat_val, WNOHANG);
- printf("wait pid = %d\n", q);
-
- // 进程退出状态
- if (WIFEXITED(stat_val))
- printf("Child exited with code %d\n", WEXITSTATUS(stat_val));
- else if (WIFSIGNALED(stat_val))
- printf("Child terminated abnormally, signal %d\n", WTERMSIG(stat_val));
- }
- }
- else
- {
- sleep(i);
- printf("I'm %dth child, pid = %d\n", i+1, getpid());
- exit(i);
- }
-
- return 0;
- }
运行结果
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。