一、进程回收资源空间函数
1、wait()
1)需要包含头文件
#include <sys/types.h>
#include <sys/wait.h>
2)解释说明
pid_t wait(int *wstatus); |
功能:阻塞等待回收子进程的资源空间 参数: wstatus :保存子进程消亡状态的变量地址 若为NULL:不保存子进程退出的状态 返回值: 成功:返回回收到的子进程的PID号 失败:-1 wait(NULL); |
可以在子进程结束时,通过 return 或 exit 来实现给 wait 函数的传参操作。
2、waitpid()
1)需包含头文件
#include <sys/types.h>
#include <sys/wait.h>
2)解释说明
pid_t waitpid(pid_t pid, int *status, int options); |
功能:回收指定进程的资源。和wait功能相似,比wait更灵活 参数: pid:回收范围 <-1 回收指定进程组内的任意子进程 (-100.等待GID=100的进程组中的任意子进程) -1 回收任意子进程,组内外 0 回收和当前调用waitpid一个组的所有子进程,组内 > 0 回收指定ID的子进程 status:子进程退出时候的状态, 如果不关注退出状态用NULL; options 选项: 0 表示回收过程会阻塞等待 WNOHANG 表示非阻塞模式回收资源。 返回值: 成功 返回接收资源的子进程pid 失败 -1 设定为非阻塞且没有回收到子进程返回0 |
3、wait函数与waitpid函数的宏
1)WIFEXITED(status)
判断子进程是否正常终止,若正常终止返回非零 (真) ,否则返回0 (假)。
2)WEXITSTATUS(status)
正常终止时,获取子进程的退出状态码,即子进程退出时返回的值。
3)WIFSIGNALED(status)
判断子进程是否被信号终止 (如被 kill 命令发送信号终止),若被信号终止返回非零 (真),否则返回0 (假)。
4)WTERMSIG(status)
获取导致子进程终止的信号的编号。
5)WIFSTOPPED(status)
判断子进程是否被信号暂停,若子进程被暂停返回非零 (真),否则返回0 (假)。
*注:仅在使用 waitpid 且指定 WUNTRACED 选项时才可能为真。
6)WSTOPSIG(status)
获取导致子进程暂停的信号编号。
4、子进程资源空间回收策略
1)wait 阻塞回收:一般情况下,父进程专门负责资源回收;
2)waitpid 非阻塞方式回收:搭配轮询方式回收。
3)不回收:子进程的任务需要一直执行
4)异步回收:当子进程结束时通知父进程进行回收
5、例题
1)wait() 函数
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>
int main(int argc, const char *argv[])
{
pid_t pid = fork(); //创建子进程
if (pid > 0) //父进程
{
int wstatus;
pid_t pidtmp = wait(&wstatus);
printf("pidtmp = %d\n", pidtmp);
if (WIFEXITED(wstatus))
{
printf("%d over normall, state : %d\n", pidtmp, WEXITSTATUS(wstatus));
}
else if (WIFSIGNALED(wstatus))
{
printf("%d ober by signal,signal num = %d\n", pidtmp, WTERMSIG(wstatus));
}
while (1)
{
printf("I am father : pid= %d\n", getpid());
sleep(1);
}
}
else if (0 == pid) //子进程
{
int i = 20;
while (i--)
{
printf("I am son : pid = %d\n", getpid());
sleep(1);
}
exit(9); //终止信号为 9 号 SIGKILL
//return 9;
}
else
{
perror("fork error");
}
return 0;
}
2)waitpid() 函数
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>
int main(int argc, const char *argv[])
{
pid_t pid = fork();
if (pid > 0)
{
while (1)
{
int wstatus;
//pid_t pidtmp = waitpid(-1, &wstatus, 0); ==>wait(&wstatus)
pid_t pidtmp = waitpid(-1, &wstatus, WNOHANG);
printf("pidtmp = %d\n", pidtmp);
if (0 != pidtmp)
{
if (WIFEXITED(wstatus))
{
printf("%d over normall, state : %d\n",pidtmp,WEXITSTATUS(wstatus));
}
else if (WIFSIGNALED(wstatus))
{
printf("%d ober by signal,signa is= %d\n",pidtmp,WTERMSIG(wstatus));
}
}
printf("I am father : pid= %d\n", getpid());
sleep(1);
}
}
//轮询
else if (0 == pid)
{
int i = 20;
while (i--)
{
printf("I am son : pid = %d\n", getpid());
sleep(1);
}
exit(9);
//return 9;
}
else
{
perror("fork error");
}
return 0;
}
二、exec 函数族
1、概念
功能:在一个进程里面执行另外一个文件(可执行文件)。
本质:将文本去的指令代码替换成exec要执行的文件的指令。
2、分类
exec 函数族分为 execl、execlp、execle、execv、execvp、execvpe六种,下面对这六种中的函数部分进行说明。
1)execl()
int execl(const char *path, const char *arg, ... , /* (char *) NULL */); |
参数: 省略号部分填写与 arg 同类的参数,直至填写完毕 |
2)execlp()
int execlp(const char *file, const char *arg, ... , /* (char *) NULL */); |
功能:从PATH指定的系统路径下寻找该可执行文件 * 不可执行自己编写的可执行文件。 |
3)execv()
execv() 与 execl() 作用一样,只是 execv() 被用于绝对路径中的替换执行。
int execv(const char *path, char *const argv[]); |
参数: psth:绝对路径 char *arg[] = {"ls", "-l", NULL); |
4)exec 说明
l:list 列表
p:path 路径 (系统路径)
v:vector 容器
e:env 环境变量
5)举例说明
(1)上述函数的使用格式实例
#include <stdio.h>
#include <unistd.h>
int main(int argc, int *argv[])
{
printf("exec : pid = %d\n", getpid());
execl("./hello", "./hello", NULL);
execl("/bin/ls", "ls", "-l", NULL);
char *arg[] = {"is", "-l", NULL};
execv("/bin/ls", arg);
execlp("ls", "ls", "-l", NULL);
execvp("ls", arg);
return 0;
}
(2)使用 exec函数族 实现 Linux 中 system 函数的功能。
核心思想:
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
int my_system(char *buff)
{
char *arg[10] = {NULL};
int i = 0;
char cmd[512] = {0};
strcpy(cmd, buff);
arg[i] = strtok(cmd, " ");
while (arg[i] != NULL)
{
i++;
arg[i] = strtok(NULL, " ");
}
pid_t pid = fork();
if (pid > 0) //父进程
{
wait(NULL);
}
else if (0 == pid) //子进程
{
execvp(arg[0], arg);
}
return 0;
}
int main(int argc, const char *argv[])
{
my_system("ls -l");
return 0;
}
三、Linux 下的命令
1. 内核内置命令:cd
2. 外部命令:ls、rm、 touch、whereis 命令
3. 别名:例如:ll <---> (ls -l)
四、const 四种存放位置的解析
下述说明中假定指针名为 p。
1、const 类型 *指针名
含义:指向常量字符的指针。
说明:指针 p 本身可以被修改,可以指向其他地址,但 p 指向的字符内容 (*p) 不能被修改 (被 const 限定) 。例如:
const char *p = "hello"; |
p = "word"; //正确(指针可以指向新地址) |
//*p = 'H'; //错误(不能修改指向的内容) |
2、类型 *const 变量名
含义:指向字符的常量指针。
说明:指针 p 本身是常量,不能被修改,不能指向其他地址,但 p 指向的字符内容 (*p) 可以被修改。例如:
char str[] = "hello"; |
char *const p = str; |
// p = "world"; // 错误(指针本身不能指向新地址) |
*p = 'H'; //合法(可以修改指向的内容) |
3、const 类型 变量名
含义:修饰普通变量,变量的值不能修改,本身是只读的。
说明:const 修饰后的变量的值在初始化之后,不允许通过赋值等操作修改。例如:
const char p = 'A'; |
p = 'B'; //错误(不能修改变量的值) |
4、const 类型 *const 指针名
含义:指向常量字符的常量指针。
说明:指针 p 本身是常量,不能被修改,不能指向其他地址,同时 p 指向的字符内容 (*p) 也不能被修改。例如:
const char *const p = "hello"; |
p = "world"; //错误(指针本身不能修改) |
*p = 'H'; //错误(指向的内容也不能修改) |
五、minshell 功能的实现
1、流程图
2、程序代码
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
int my_system(char *buff)
{
if(buff == NULL || strlen(buff) == 0)
{
return 0;
}
char *arg[10] = {NULL};
int i = 0;
char cmd[1024] = {0};
strcpy(cmd, buff);
arg[i] = strtok(cmd, " ");
while (arg[i] != NULL)
{
printf("arg[%d] = %s\n", i, arg[i]);
i++;
arg[i] = strtok(NULL, " ");
}
pid_t pid = fork();
if (pid > 0) //父进程
{
wait(NULL);
}
else if (0 == pid) //子进程
{
execvp(arg[0], arg);
perror("execvp failed");
exit(1);
}
else
{
perror("fork failed");
return -1;
}
return 0;
}
int main(int argc, const char *argv[])
{
while(1)
{
char buff[1024] = {0};
getcwd(buff, sizeof(buff));
printf("zmy@ubuntu:%s:", buff);
char p[1024] = {0};
fgets(p, sizeof(p), stdin);
p[strcspn(p, "\n")] = '\0';
if(strcmp(p, "q") == 0)
{
break;
}
my_system(p);
}
return 0;
}
3、运行结构示意图
【END】