3. 进程控制
3.1 环境变量
3.1.1 获取环境变量getenv()
帮助手册:
man 3 getenv
包含头文件:
- `#include <stdlib.h>
函数原型:
char *getenv(const char *name);
参数 | 说明 |
---|---|
name | 环境变量 |
return | 成功:环境变量的值 失败:NULL |
3.1.2 修改或添加环境变量setenv()
帮助手册:
man 3 setenv
包含头文件:
- `#include <stdlib.h>
函数原型:
int unsetenv(const char *name);
参数 | 说明 |
---|---|
name | 环境变量名 |
value | 变量值 |
overwrite | 当环境变量存在时 0:不重写 非0:重写 |
return | 成功:0 失败:-1,并设置errno |
3.1.3 删除环境变量unsetenv()
帮助手册:
man 3 unsetenv
包含头文件:
- `#include <stdlib.h>
函数原型:
char *getenv(const char *name);
参数 | 说明 |
---|---|
name | 环境变量名 |
return | 成功:0 失败:-1,并设置errno |
3.2创建子进程fork()
帮助手册:
man 2 fork
包含头文件:
#include <sys/types.h>
#include <unistd.h>
函数原型:
pid_t fork(void);
刚
fork()
后:
- 父子进程相同处:
– 全局变量,data, text, 栈,堆,环境变量,用户ID,宿主目录,进程工作目录,信号处理方式…- 父子进程不相同处:
– 进程ID, fork返回值,父进程ID, 进程运行时间,闹钟(定时器),未决信号集注:父子进程间数据
读时共享,写时复制
,父子进程不共享全局变量
参数 | 说明 |
---|---|
return | 成功:子进程:0;父进程:子进程PID 失败:-1,并设置errno |
3.3 获得当前进程PIDgetpid()
帮助手册:
man 2 getpid
包含头文件:
#include <sys/types.h>
#include <unistd.h>
函数原型:
pid_t getpid(void)
参数 | 说明 |
---|---|
return | 返回当前进程的PID |
3.4 获得父进程PIDgetppid()
帮助手册:
man 2 getppid
包含头文件:
#include <sys/types.h>
#include <unistd.h>
函数原型:
pid_t getppid(void)
参数 | 说明 |
---|---|
return | 返回父进程的PID |
示例:
#include <stdio.h>
#include <unistd.h>
int main(int argc, char* argv[])
{
printf("Begin...\n");
pid_t pid = fork();
if(pid < 0)
{
perror("fork error:");
return -1;
}
if(pid == 0)
printf("This is child process, PID = %d, PPID = %d\n", getpid(), getppid());
if(pid > 0)
printf("This is parent process, pid = %d, PID = %d, PPID = %d\n", pid, getpid(), getppid());
printf("End...\n");
return 0;
}
3.5 执行一个文件
3.5.1 execl()
帮助手册:
man 2 execl
包含头文件:
#include <unistd.h>
函数原型:
int execl(const char *pathname, const char *arg, ... /* (char *) NULL */);
参数 | 说明 |
---|---|
pathname | 可执行文件路径 |
arg | 参数(注:arg[0]需要用可执行文件占位,最后需要用NULL 标识) |
return | 成功:不返回 失败:-1,并设置errno |
3.5.2 execlp()
帮助手册:
man 2 execlp
包含头文件:
#include <unistd.h>
函数原型:
int execlp(const char *file, const char *arg, ... /* (char *) NULL */);
函数说明:
- 执行程序时,使用
PATH
环境变量,执行的程序可以不用加路径
参数 | 说明 |
---|---|
file | 可执行文件路径 |
arg | 参数(注:arg[0]需要用可执行文件占位,最后需要用NULL 标识) |
return | 成功:不返回 失败:-1,并设置errno |
注:execl()
和execlp()
会将原来的代码断进行替换
#include <stdio.h>
#include <unistd.h>
int main(int argc, char* argv[])
{
execlp("ls", "ls", "-l", NULL);
perror("execlp error:");
return 0;
}
3.6 等待子进程中断或结束
- 孤儿进程:父进程运行结束,子进程被init进程领养。
- 僵尸进程:子进程运行结束,父进程没有回收子进程资源(PCB)
孤儿进程示例:
#include <stdio.h>
#include <sys/wait.h>
#include <unistd.h>
int main(int argc, char* argv[])
{
pid_t pid = fork();
if(pid == 0)
{
while(1)
{
printf("child process...ppid=%d\n", getppid());
sleep(1);
}
}
if(pid > 0)
{
sleep(3);
printf("The parent process is finished...\n");
}
return 0;
}
僵尸进程示例:
#include <stdio.h>
#include <sys/wait.h>
#include <unistd.h>
int main(int argc, char* argv[])
{
pid_t pid = fork();
if(pid == 0)
{
sleep(1);
printf("The child process is finished...\n");
}
if(pid > 0)
{
while(1)
{
printf("parent process...\n");
sleep(1);
}
}
return 0;
}
3.6.1wait()
帮助手册:
man 2 wait
包含头文件:
#include <sys/types.h>
#include <sys/wait.h>
函数原型:
pid_t wait(int *wstatus);
函数说明:
wait()
会暂时停止目前进程的执行,直到有信号来到或子进程结束。如果在调用wait()
时子进程已经结束,则wait()
会立即返回子进程结束状态值,并回收子进程的PCB
。子进程的结束状态值会由参数wstatus
返回,而子进程的进程识别码也会一块返回。如果不在意结束状态值,则参数wstatus
可以设为NULL
。子进程的结束状态值参考waitpid()
。
参数 | 说明 |
---|---|
wstatus | 传出参数(子进程的死亡原因) |
return | 成功:子进程PID 失败:-1,并设置errno |
子进程的死亡原因:
示例:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(int argc, char* argv[])
{
pid_t pid = fork();
if(pid == 0)
{
sleep(2);
printf("The child process is finished...\n");
}
if(pid > 0)
{
printf("wait...\n");
pid_t wpid = wait(NULL);
printf("The child process was dead. wpid = %d, pid = %d\n", wpid, pid);
while(1) sleep(1);
}
return 0;
}
3.6.2 waitpid()
帮助手册:
man 2 waitpid
包含头文件:
#include <sys/types.h>
#include <sys/wait.h>
函数原型:
pid_t waitpid(pid_t pid, int *wstatus, int options);
参数 | 说明 |
---|---|
pid | pid<-1 :等待进程组识别码为pid绝对值的任何子进程pid=-1 :等待任何子进程,相当于wait() pid=0 :等待进程组识别码与目前进程相同的任何子进程pid>0 :等待任何子进程识别码为pid的子进程 |
options | NULL :与wait() 相同,一会阻塞WNOHANG :如果没有子进程存在立即返回WUNTRACED :如果子进程进入暂停状态立马返回,但结束状态不予理会WCONTINUED :如果通过SIGCONT恢复了停止的子进程,也返回 |
return | 成功:子进程PID(如果设置了WNOHANG ,如果没有子进程退出返回0)失败:-1,并设置errno |
如果
wstatus
不为NULL
,wait()
和waitpid()
将状态信息存储在*wstatus
中。可以使用以下宏检查此整数:
宏 | 说明 |
---|---|
WIFEXITED(wstatus) | 如果子进程正常退出放回真 |
WEXITSTATUS(wstatus) | 返回子进程的退出状态(注:WIFEXITED 返回真可以使用) |
WIFSIGNALED(wstatus) | 如果子进程被一个信号杀死返回真 |
WTERMSIG(wstatus) | 返回杀死子进程的信号(注:WIFSIGNALED 返回真可以使用) |
WCOREDUMP(wstatus) | 如果子进程核转储返回真(注:WIFSIGNALED 返回真可以使用) |
WIFSTOPPED(wstatus) | 如果子进程处于暂停状态返回真(注:WIFSIGNALED 返回真可以使用) |
WSTOPSIG(wstatus) | 返回引发子进程暂停的信号代码(注:WIFSIGNALED 返回真可以使用) |
WIFCONTINUED(wstatus) | 如果子进程回复继续运行返回真 |
示例:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(int argc, char* argv[])
{
pid_t pid = fork();
if(pid == 0)
{
printf("\tChild dead.\n");
sleep(2);
return 1;
}
if(pid > 0)
{
printf("wait for child die!\n");
int status;
pid_t wpid = wait(NULL);
printf("wait ok, wpid = %d, pid = %d\n", wpid, pid);
if(WIFEXITED(status))
{
printf("child exit with %d\n", WEXITSTATUS(status));
}
if(WIFSIGNALED(status))
{
printf("child killed by %d\n", WTERMSIG(status));
}
while(1)
{
sleep(1);
}
}
return 0;
}
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(int argc, char* argv[])
{
pid_t pid = fork();
if(pid == 0)
{
printf("\tChild dead. pid = %d\n", getpid());
sleep(2);
return 1;
}
if(pid > 0)
{
printf("wait for child die!\n");
int ret;
while((ret = waitpid(-1, NULL, WNOHANG)) == 0);
printf("ret = %d\n", ret);
while(1)
{
sleep(1);
}
}
return 0;
}