https://note.youdao.com/ynoteshare1/index.html?id=2d379469cae5626e134316632e1386b4&type=note
一、exec族函数及实战
which ls 查找命令路径
exec函数族的作用是根据指定的文件名找到可执行文件,并用它来取代调用进程的内容,换句话说,就是在调用进程内部执行一个可执行文件。这里的可执行文件既可以是二进制文件,也可以是任何Linux下可执行的脚本文件。
可以直接把一个编译好的可执行程序直接加载运行。
使用exec族函数后,我们的父子进程是这样的:
子进程需要运行的程序被单独编写,生成一个a
主进程是父进程,fork创建子进程后在子进程中用exec来执行a,达到各自执行,同步运行
1、exec族函数包含如下函数:
#include <unistd.h>
扫描二维码关注公众号,回复:
6036132 查看本文章
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[]); |
2、函数使用示例:
总体:file.c
#include <unistd.h> #include <sys/types.h> #include <sys/wait.h> #include<stdio.h> #include<stdlib.h> int main(int argc,char *argv[]) { pid_t ret = -1; pid_t pid = -1; int status = -1; // pid = fork(); //创建子进程,返回两个pid,一个是父进程pid=0,一个是子进程pid>0 if(pid == 0) { //子进程 printf("child ready\n"); printf("child pid = %d\n",getpid()); /*(1)使用execl和execlp分别运行ls -l -a*/ //execl("/bin/ls", "ls", "-l", "-a", NULL); //execl("exec", "aaa", "bbb", NULL); //execlp("ls", "ls", "-l", "-a", NULL); /*(2)使用execv和execvp运行ls*/ char *const arg[] = {"ls", "-l", "-a", NULL}; //execv("/bin/ls", arg); execvp("ls", arg); /*(3)使用execle运行自己写的程序*/ //char *const envp[] = {"name=scorpio","host=192.168.6.200", NULL}; //execle("./exec", "exec", NULL, envp); return 0; } else if(pid > 0) { sleep(1); //父进程 printf("parent ready\n"); printf("父进程pid=%d\n",pid); //ret = waitpid(pid,&status,0); //ret = waitpid(pid,&status,0); ret = waitpid(pid,&status,WNOHANG); printf("回收子进程,子进程pid=%d\n",ret); printf("是否正常退出:%d\n",WIFEXITED(status) ); printf("是否非正常退出:%d\n",WIFSIGNALED(status) ); printf("正常终止的进程返回值:%d\n",WEXITSTATUS(status) ); } else { //错误 perror("fork"); } printf("Code Access~\n"); return 0; } |
exec.c
#include <stdio.h> int main(int argc, char **argv, char **env) { printf("hello\n"); int i = 0; for(i = 0; argv[i] != NULL; i++) { printf("argv[%d]==%s\n", i,argv[i]); } /* i=0; while(env[i] != NULL) { printf("env[%d]==%s\n", i,env[i]); i++; } */ return 0; } |
(1)使用execl运行ls -l -a
execl("/bin/ls", "ls", "-l", "-a", NULL); |
(2)使用execv运行ls
char *const arg[] = {"ls", "-l", "-a", NULL}; execv("/bin/ls", arg); |
打印情况和(1)一样。
(3)使用execl运行自己写的程序
//execl("/bin/ls", "ls", "-l", "-a", NULL); execl("exec", "aaa", "bbb", NULL); |
在exec.c文件中写入 printf("hello\n");
#include <stdio.h> int main(int argc, char **argv) { printf("hello\n"); int i = 0; for(i = 0; argv[i] != NULL; i++) { printf("argv[%d]==%s\n", i,argv[i]); } return 0; } |
(4)execlp和execvp
(1)加p和不加p的区别是:不加p时需要全部路径+文件名,如果找不到就报错了。
加了p之后会多帮我们到PATH所指定的路径下去找一下。不需要全路径
execl("ls", "ls", "-l", "-a", NULL); execlp("ls", "ls", "-l", "-a", NULL); |
(5)execle和execvpe
char *const envp[] = {"name=scorpio","host=192.168.6.200", NULL}; execle("./exec", "exec", NULL, envp); |
总结:
execl 使用全部路径 相对路径或绝对路径 |
execlp 使用相对路径 没有path参数,直接找$PATH对应的目录 |
xecle ,+e是环境变量 ,,e就是environment环境变量的意思,和xec的区别:执行可执行程序时会多传一个环境变量的字符串数组给待执行的程序 |
- execl与execv区别在于参数传递方式不同,execl将参数存放在一个列表中,execv将参数存放在一个字符串数组中。
- execlp和execvp增加了文件查找的路径,优先查找path参数的文件,找不到则到环境变量PATH中去查找。
- execle和execvpe增加了给可执行程序传递环境变量的字符串数组。
二、进程状态和system函数
1、进程的5种状态
(1)就绪态。这个进程当前所有运行条件就绪,只要得到了CPU时间就能直接运行。
(2)运行态。就绪态时得到了CPU就进入运行态开始运行。
(3)僵尸态。进程已经结束但是父进程还没来得及回收
(4)等待态(浅度睡眠&深度睡眠):并非是等待被运行,而是等一定的条件,然后进入就绪态 。分为“浅度睡眠”“深度睡眠”。 其中浅度睡眠可被唤醒。 深度睡眠只能等待条件到达。
(5)暂停态。暂停并不是进程的终止,只是被被人(信号)暂停了,还可以恢复的。
2、进程各种状态之间的转换图
Linux的进程调度的目的是为了提高资源利用率,系统吞吐量。
一个进程的调度时间是有限的。多个进程同时运行的环境下,一旦规定的时间内单个进程没有结束,就意味着时间片用完,随后就进入就绪状态,重新等待着运行。
3、system函数简介
(1)system函数 = fork+exec
(2)原子操作。原子操作意思就是整个操作一旦开始就会不被打断的执行完。原子操作的好处就是不会被人打断(不会引来竞争状态),坏处是自己单独连续占用CPU时间太长影响系统整体实时性,因此应该尽量避免不必要的原子操作,就算不得不原子操作也应该尽量原子操作的时间缩短。
(3)使用system调用ls
功能:system()函数调用“/bin/sh -c command”执行特定的命令,阻塞当前进程直到command命令执行完毕
原型:
int system(const char *command);
使用方法:system("ls -l");等同于打印ls -l信息。 |
三、进程关系
(转载部分:https://www.cnblogs.com/zengyiwen/p/5755191.html)
进程是操作系统的一个核心概念。每个进程都有自己唯一的标识:进程ID,也有自己的生命周期。一个典型的进程的生命周期如图4-1所示。
- (1)无关系
当然不是完全的没关系。每一个进程都是0进程fork一步步得到的,但是这里的“无关系” 。是说的不符合以下几种形式的 进程关系
- (2)父子进程关系
进程都有父进程,父进程也有父进程,这就形成了一个以init进程为根的家族树。除此以外,进程还有其他层次关系:进程、进程组和会话。
进程组和会话在进程之间形成了两级的层次:进程组是一组相关进程的集合,会话是一组相关进程组的集合。
- (3)进程组(group)由若干进程构成一个进程组
是一个或多个进程的集合。可以接收来自同一终端的各种信号。
每个进程组有一个唯一的进程组ID,每个进程组都可以有一个组长进程
进程组ID = 组长ID
- (4)会话(session)会话就是进程组的组
是一个或者多个进程组的集合,一个会话可以有一个控制终端
建立与控制终端连接的会话首进程被称为控制进程。首进程(bash)
一个会话中的几个进程组可被分为一个前台进程组和多个后台进程组。
因此,一个会话中,应该包括:控制进程(会话首进程),一个前台进程组和任意后台进程组