一、文件夹的操作
opendir(3)、closedir(3)、readdir(3)
二、文件重定向实现的原理
dup(2) dup2(2)
三、文件锁
fcntl(2)
四、库函数和系统调用之间的关系
五、杂项
六、进程的基础
fork(2)
补充:
如何查看进程的相关信息?
ps -aux
今天内容:
一、进程的退出
1、return和exit(3)的区别
return只是函数的返回
exit(3)是进程的结束
#include <stdlib.h>
void exit(int status);
功能:终止进程
参数:
status:退出状态码。status&0377的值给父进程。
返回值:
永远不返回。
代码参见test.c
在进程结束前,调用一些函数。需要将这些函数注册给进程。
on_exit(3)
#include <stdlib.h>
int on_exit(void (*function)(int , void *), void *arg);
功能:注册一个函数给进程,在进程终止的时候调用该函数
参数:
function:指定退出函数的名字
void (*function)(int , void *)
arg:指定退出函数的第二个参数
返回值:
0 成功
非0 错误
举例验证on_exit(3)的使用
代码参见 on_exit.c
atexit(3)
#include <stdlib.h>
int atexit(void (*function)(void));
功能:注册一个函数给进程,在进程终止的时候调用该函数
参数:
function:指定了要注册的函数的名字
返回值:
0 成功
非0 错误
举例验证,atexit函数的使用。
代码参见 atexit.c
_exit(2)
#include <unistd.h>
void _exit(int status);
功能:终止当前进程
参数:
status:退出状态码
返回值:
永远不返回。
二、进程资源的回收
在进程退出以后,父进程会回收子进程的资源。
使用wait(2)、waitpid(2)系统调用回收子进程的资源。
如果父进程早于子进程结束,那么父进程的子进程的父亲就是init进程。这种进程称为孤儿进程。
举例验证 孤儿进程
代码参见 lonely.c
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status);
功能:等待进程改变状态。
参数:
status:退出状态码的地址。子进程的退出状态存放在这块地址空间里。可以使用一些宏检测退出原因。
WIFEXITED(status) 如果正常死亡,返回真
WEXITSTATUS(status) 返回子进程的退出状态和0377的与,那个值。
WIFSIGNALED(status) 如果子进程被信号终止,返回真
WTERMSIG(status) 检测被几号信号终止。只有上个宏为真的时候,才使用。
返回值:
-1 错误
返回终止的子进程的pid
举例验证,父进程等待子进程的结束,并且回收子进程的资源。
代码参见 wait.c
pid_t waitpid(pid_t pid,int *status,int options);
功能:等待进程改变状态。
参数:
pid:
< -1: pid取绝对值,如果子进程的组id等于这个绝对值,那么这个子进程就被等待。
-1:等待任意子进程
0:等待和当前进程有同一个组id的子进程
> 0 等待子进程的pid是pid参数的子进程。
status:同wait(2)参数的使用
options:
WNOHANG:非阻塞回收。
0 阻塞回收
返回值:
-1 错误
0 没有子进程退出
回收的子进程的pid
举例验证waitpid的使用
代码参见waitpid.c
补充:
1、同步 异步
事情A和事情B,如果必须先执行完事情A,然后再执行事情B,那么说这两件事情同步
如果两件事情的执行先后不确定,那么是异步。
2、给指定进程发送信号
kill -信号编号 进程的pid
3、子进程已经终止,但是父进程还没有回收子进程的资源,这时候,子进程处于僵尸状态,称为僵尸进程。
举例僵尸进程。代码参见 zombile.c
三、在进程的虚拟地址空间里加载新的映像(image)
在子进程的虚拟地址空间里加载新的影像,需要使用系统提供的一个家族的函数。
execl(3)
#include <unistd.h>
extern char **environ;
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 execvpe(const char *file, char *const argv[],
char *const envp[]);
execve(2)
#include <unistd.h>
int execve(const char *filename, char *const argv[],\
char *const envp[]);
相同的exec
l list
v vector
p PATH
e 环境变量
返回值:
成功调用永远不返回
-1 错误 errno被设置
举例验证 在子进程的虚拟地址空间加载新的映像
代码参见 exec.c
day08$ps -o pid,ppid,pgrp,comm
PID PPID PGRP COMMAND
8725 8714 8725 bash
9652 8725 9652 ps
补充:
ls -l filename
char *const ps_argv[]={"ls","-l","filename",NULL};
bash调用fork产生子进程,在子进程地址空间里加载新的映像
execvp(ps_argv[0],ps_argv);
ps -o pid,ppid,pgrp,comm
char *const ps_argv[]={"ps","-o",\
"pid,ppid,pgrp,comm",NULL};
bash调用fork产生子进程,在子进程地址空间里加载新的映像
execvp(ps_argv[0],ps_argv);
bash有外部命令和内部命令构成
外部命令和bash不是同一个可执行程序。
内部命令是bash本身的一部分。
如何察看一个命令是内部命令还是外部命令?
type 命令
四、使用system(3)启动新的可执行程序
#include <stdlib.h>
int system(const char *command);
功能:执行一个shell命令
参数:
command:可执行命令
返回值:
-1 错误
返回command的退出状态码。
举例验证 system(3)的使用
代码参见 system.c
system和exec系列函数的区别
bash───a.out───a.out───sh───myt
bash───a.out───myt
总结:
一、进程的退出
二、进程资源的回收
三、加载新的映像
四、使用system(3)调用新的可执行程序
作业:
编写代码使用fork建立进程树。