Linux系统编程【进程控制】(5)
【摘要】 16. 孤儿进程父进程运行结束,但子进程还在运行(未运行结束)的子进程就称为孤儿进程(Orphan Process)。每当出现一个孤儿进程的时候,内核就把孤儿进程的父进程设置为 init ,而 init 进程会循环地 wait() 它的已经退出的子进程。这样,当一个孤儿进程凄凉地结束了其生命周期的时候,init 进程就会代表党和政府出面处理它的一切善后工作。因此孤儿进程并不会有什么危害。 ...
16. 孤儿进程
父进程运行结束,但子进程还在运行(未运行结束)的子进程就称为孤儿进程(Orphan Process)。
每当出现一个孤儿进程的时候,内核就把孤儿进程的父进程设置为 init ,而 init 进程会循环地 wait() 它的已经退出的子进程。这样,当一个孤儿进程凄凉地结束了其生命周期的时候,init 进程就会代表党和政府出面处理它的一切善后工作。
因此孤儿进程并不会有什么危害。
17. 僵尸进程
进程终止,父进程尚未回收,子进程残留资源(PCB)存放于内核中,变成僵尸(Zombie)进程。
这样就会导致一个问题,如果进程不调用wait() 或 waitpid() 的话, 那么保留的那段信息就不会释放,其进程号就会一直被占用,但是系统所能使用的进程号是有限的,如果大量的产生僵尸进程,将因为没有可用的进程号而导致系统不能产生新的进程,此即为僵尸进程的危害,应当避免。
pid_t pid = -1;
//创建一个子进程
pid = fork();
//执行子进程
if(pid == 0)
{
for(int i=0;i<5;i++)
{
printf("子进程偷懒了%d次\n",i);
sleep(1);
}
printf("子进程被发现了,先退出了...\n");
//子进程退出
exit(0);
}
sleep(100);
printf("父进程偷懒结束 父进程退出...\n");
return 0;
使用killall a.out
杀死
18. 进程替换
概述
在 Windows 平台下,我们可以通过双击运行可执行程序,让这个可执行程序成为一个进程;而在 Linux 平台,我们可以通过 ./ 运行,让一个可执行程序成为一个进程。
但是,如果我们本来就运行着一个程序(进程),我们如何在这个进程内部启动一个外部程序,由内核将这个外部程序读入内存,使其执行起来成为一个进程呢?这里我们通过 exec 函数族实现。
exec 函数族,顾名思义,就是一簇函数,在 Linux 中,并不存在 exec() 函数,exec 指的是一组函数,一共有 6 个:
#include <unistd.h>
extern char **environ;
int execl(const char *path, const char *arg, .../* (char *) NULL */);
int execlp(const char *file, const char *arg, ... /* (char *) NULL */);
int execle(const char *path, const char *arg, .../*, (char *) NULL, char * const envp[] */);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[], char *const envp[]);
int execve(const char *filename, char *const argv[], char *const envp[]);
其中只有 execve() 是真正意义上的系统调用,其它都是在此基础上经过包装的库函数。
exec 函数族的作用是根据指定的文件名或目录名找到可执行文件,并用它来取代调用进程的内容,换句话说,就是在调用进程内部执行一个可执行文件。
进程调用一种 exec 函数时,该进程完全由新程序替换,而新程序则从其 main 函数开始执行。因为调用 exec 并不创建新进程,所以前后的进程 ID (当然还有父进程号、进程组号、当前工作目录……)并未改变。exec 只是用另一个新程序替换了当前进程的正文、数据、堆和栈段(进程替换)。
exec 函数族使用说明
exec 函数族的 6 个函数看起来似乎很复杂,但实际上无论是作用还是用法都非常相似,只有很微小的差别。
补充说明:
l(list) | 参数地址列表,以空指针结尾 |
---|---|
v(vector) | 存有各参数地址的指针数组的地址 |
p(path) | 按 PATH 环境变量指定的目录搜索可执行文件 |
e(environment) | 存有环境变量字符串地址的指针数组的地址 |
exec 函数族与一般的函数不同,exec 函数族中的函数执行成功后不会返回,而且,exec 函数族下面的代码执行不到。只有调用失败了,它们才会返回 -1,失败后从原程序的调用点接着往下执行。
execlp函数
示例代码:
#include<stdio.h>
#include<unistd.h>
//进程替换
int main()
{
printf("hello 挨踢程序员\n");
//arg0 arg1 arg2....argn
//argo 一般是可执行文件名 argn必须是NULL
execlp("ls","ls","-l","/home/test",NULL);//等价于ls -l /home/test
printf("hello world\n");
return 0;
}
运行结果:
最后一句的“hello world”没有被打印,为什么?画图分析一下:
execl函数
和execlp相似,它们就第一个参数不同,一个是可执行文件名,另一个是执行文件的路径
int execl(const char *path, const char *arg, .../* (char *) NULL */);
execv/execvp函数
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
execle/execvpe函数
int execle(const char *path, const char *arg, .../*, (char *) NULL, char * const envp[] */)
int execvpe(const char *file, char *const argv[], char *const envp[]);
envp:环境变量
用法:
#define _GNU_SOURCE
#include<string.h>
#include<stdio.h>
#include<unistd.h>
char *envp[] = {"ADDR=BEIJING",NULL};
//最后一个参数是环境变量指针数组
execle("/bin/ls","ls","ls","-l","home/test",null,envp);
//第一个参数是可执行文件
//第二个参数是参数列表 指针数组
//第三个参数是环境变量列表 指针数组
execvpe("ls",argv,envp);
运行结果:
19. 源码包安装(扩展)
第一步:生成Makefile和检测当前环境
xcc@machine:~/tools/valgrind-3.13.0 sudo ./configure
第二步: 编译源码 生成可执行文件
xcc@machine:~/tools/valgrind-3.13.0$ sudo make -j4
第三步: 安装
xcc@machine:~/tools/valgrind-3.13.0$ sudo make install
**第四步:**测试
xcc@machine:~/tools/valgrind-3.13.0$ valgrind
valgrind: no program specified
valgrind: Use --help for more information.
20. 作业
1)编写测试程序,测试fork之前打开文件,父子进程之间是否共享文件
提示:
- 子进程write
- 父进程read
2)多进程编程
父进程fork三个子进程:
- 一个调用ps命令
- 一个调用自定义应用程序
- 一个调用会出现段错误的程序
父进程回收三个子进程(waitpid),并且打印三个子进程的退出状态
3) 文件操作
统计出指定目录中普通文件的个数.(递归)
4)实现ps aux | grep bash功能
提示:fork(), pipe(), dup2(), execlp()
21.答案
3) 文件操作
统计出指定目录中普通文件的个数.(递归)
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <dirent.h>
#include <sys/types.h>
int get_file_num(char* root)
{
int total = 0;
DIR* dir = NULL;
// 打开目录
dir = opendir(root);
// 循环从目录中读文件
char path[1024];
// 定义记录xiang指针
struct dirent* ptr = NULL;
while( (ptr = readdir(dir)) != NULL)
{
// 跳过. he ..
if(strcmp(ptr->d_name, ".") == 0 || strcmp(ptr->d_name, "..") == 0)
{
continue;
}
// 判断是不是目录
if(ptr->d_type == DT_DIR)
{
///home/deng/share
sprintf(path, "%s/%s", root, ptr->d_name);
// 递归读目录
total += get_file_num(path);
}
// 如果是普通文件
if(ptr->d_type == DT_REG)
{
total ++;
}
}
closedir(dir);
return total;
}
int main(int argc, char* argv[])
{
if(argc < 2)
{
printf("./a.out path");
exit(1);
}
int total = get_file_num(argv[1]);
printf("%s has regfile number: %d\n", argv[1], total);
return 0;
}
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)