进程的概念
进程类型
进程状态
进程状态图
进程信息查询和相关命令
查看进程信息
ps —查看系统进程
top —查看进程动态信息
/proc —查看进程详细信息
进程相关命令
nice —按用户指定的优先级运行程序
renice —改变正在运行进程的优先级
jobs —查看后台进程
bg —将挂起的进程在后台运行
fg —把后台运行的程序放到前台运行
进程相关函数
创建进程
#include <unistd.h>
pid_t fork(void);
创建新的进程,失败时返回-1
成功时父进程返回子进程的进程号,子进程返回0
通过fork的返回值区分父进程和子进程
示例
id_t pid;
if ((pid = fork()) < 0) {
perror(“fork”);
return -1;
}
else if (pid == 0) {
printf(“child process : my pid is %d\n”, getpid());
}
else {
printf(“parent process : my pid is %d\n”, getpid());
}
父子进程
- 子进程继承了父进程的内容
- 父子进程有独立的地址空间,互不影响
- 若父进程先结束
- 子进程成为孤儿进程,被init进程收养
- 子进程变成后台进程
- 若子进程先结束
- 父进程如果没有及时回收,子进程变成僵尸进程
进程结束
#include <stdlib.h>
#include <unistd.h>
void exit(int status);
void _exit(int status);
结束当前的进程并将status返回
exit结束进程时会刷新(流)缓冲区
exec函数族
用fork函数创建新进程后,经常会在新进程中调用exec函数去执行另外一个程序。当进程调用exec函数时,该进程被完全替换为新程序。调
用exec函数并不创建新进程,所以前后进程的ID并没有改变。
功能:
在调用进程内部执行一个可执行文件。可执行文件既可以是二进制文件,也可以是任何Linux下可执行的脚本文件。
函数族:
execl, execlp, execle, execv, execvp, execvpe
-
进程调用exec函数族执行某个程序
-
进程当前内容被指定的程序替换
-
实现让父子进程执行不同的程序
父进程创建子进程
子进程调用exec函数族
父进程不受影响
进程–execl/execlp
execl和execlp的区别在于第一个参数,execl指定路径位置、execlp在PATH里面查找
#include <unistd.h>
int execl(const char *path, const char *arg, …);
int execlp(const char *file, const char *arg, …);
成功时执行指定的程序;失败时返回EOF
path 执行的程序名称,包含路径
arg… 传递给执行的程序的参数列表
file 执行的程序的名称,在PATH中查找
执行ls命令,显示/etc目录下所有文件的详细信息
if (execl(“/bin/ls”, “ls”, “-a”, “-l”, “/etc”, NULL) < 0) {
perror(“execl”);
}
if (execlp(“ls”, “ls”, “-a”, “-l”, “/etc”, NULL) < 0) {
perror(“execlp”);
}
进程–execv/execvp
execv和execl区别:把execl第一个参数后面的参数放入数组中
#include <unistd.h>
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
成功时执行指定的程序;失败时返回EOF
arg… 封装成指针数组的形式
执行ls命令,显示/etc目录下所有文件的详细信息
char *arg[] = {“ls”, “-a”, “-l”, “/etc”, NULL};
if (execv(“/bin/ls”, arg) < 0) {
perror(“execv”);
}
if (execvp(“ls”, arg) < 0) {
perror(“execvp”);
}
进程–system()函数
system()会调用fork()产生子进程,由子进程来调用/bin/sh-c string来执行参数string字符串所代表的命令,此命令执行完后随即返回原调用的进程。在调用system()期间SIGCHLD 信号会被暂时搁置,SIGINT和SIGQUIT 信号则会被忽略。
与exec的区别
1、system()和exec()都可以执行进程外的命令,system是在原进程上开辟了一个新的进程,但是exec是用新进程(命令)覆盖了原有的进程
2、system()和exec()都有能产生返回值,system的返回值并不影响原有进程,但是exec的返回值影响了原进程
3.exec函数执行完后会结束子进程,system则是阻塞调用结束后返回子进程
#include <stdlib.h>
int system(const char *command);
成功时返回命令command的返回值;失败时返回EOF
当前进程等待command执行结束后才继续执行
范例:
#include<stdlib.h>
main()
{
system("ls -al /etc/passwd /etc/shadow");
char tmp[];
sprintf(tmp,"/bin/mount -t vfat %s /mnt/usb",dev);
system(tmp);
}
僵尸进程处理方法
如何找出僵尸进程?
打开终端并输入下面命令:
ps aux | grep Z
会列出进程表中所有僵尸进程的详细内容。
*方法一:wait/waitpid函数:pid_t wait(int status)
父进程在调用wait函数之后就可以将自己阻塞,由wait自动分析是否当前进程的某个子进程已经退出,如果让它找到了这样一个已经变成僵尸的子进程,wait就会收集这个子进程的信息,并把它彻底销毁后返回;如果没有找到这样一个子进程,wait就会一直阻塞在这里,直到有一个出现为止。其中的参数status用来保存被收集进程退出时的一些状态,它是一个指向int类型的指针。但如果我们对这个子进程是如何死掉的毫不在意,只想把这个僵尸进程消灭掉,(事实上绝大多数情况下,我们都会这样想),我们就可以设定这个参数为NULL,就象下面这样:
pid = wait(NULL);
在原代码中的父进程块中也只需要添加 wait(NULL),再运行的时候就会发现僵死进程已经被处理掉了。但是,这么做的缺点也很明显。在阻塞的过程中,父进程停止了自己的运行。在实际应用中我们不可能为了处理一个僵死进程而令父进程一直wait。并且一个wait函数只能处理一个僵死进程,作用十分有限。
方法二:将父进程中对SIGCHLD信号的处理函数设为SIG_IGN(忽略)
需要信号的头文件:signal.h。只需要在父进程块中加入一行代码:signal(SIGCHLD,SIG_IGN);就可以不产生僵死进程。调用这个signal函数就定义了父进程对子进程结束后返回的SIGCHLD信号的响应方式:忽略。
方法三:原理是将子进程成为孤儿进程,从而其的父进程变为init进程,通过init进程可以处理僵尸进程。在一个子进程中在创建一个子进程,让父进程去waitpid僵尸进程。