2.Linux进程
# 1. 进程的几个概念
每个进程执行其逻辑流的一部分,然后在其他进程轮流执行时被抢占(暂时挂起)。进程与其他进程轮流执行的过程称为多任务处理。进程执行其流的一部分的每个时间段称为时间片。因此,多任务也被称为时间切片。任何逻辑流在时间上与另一个流重叠的进程都称为并发进程,这两个进程称为并发运行。
操作系统内核使用一种更高级别的异常控制流(称为上下文切换)来实现多任务。在进程执行过程中的某些时刻,内核可以决定抢占当前进程并重新启动先前抢占的进程。这个决定称为调度,由内核中称为调度器的部分来处理。该机制(1)保存了当前进程的上下文,(2)恢复一些以前抢占的进程保存的上下文,(3)将控制权传递给这个新恢复的进程。上下文数据:进程执行时处理器寄存器中的数据
系统中的每个程序都在某个进程的上下文中运行。上下文由程序正确运行所需的状态组成。该状态包括:
- 存储在内存中的程序代码和数据、用户及内核堆栈
- 通用寄存器、浮点寄存器、程序计数器(指向程序执行的下一条指令地址)、状态寄存器:记录临时数据以供调度时恢复。
- 环境变量、文件描述符集和各种内核数据结构,如描述地址空间的页表、包含当前进程信息的进程表,以及一个文件表,其中包含有关进程已打开的文件的信息。
运行一段c程序:
#include <stdio.h>
#include <unistd.h>
int main()
{
while(1)
{
printf("hello world\n");
sleep(1);
}
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
查看:
[test@VM-12-4-centos ~]$ ps axj | head -1 && ps axj | grep main|grep -v grep
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
1 11255 11255 2381 ? -1 S 1001 0:36 ./main
25802 26785 26785 25802 pts/0 26785 S+ 1001 0:00 ./main
[test@VM-12-4-centos ~]$ ls -al /proc/26785/ # 查看proc下的目录文件,对应的数字是进程的PID
total 0
dr-xr-xr-x 9 test test 0 May 30 01:08 .
dr-xr-xr-x 105 root root 0 Dec 18 12:46 ..
dr-xr-xr-x 2 test test 0 May 30 01:09 attr
-rw-r--r-- 1 test test 0 May 30 01:09 autogroup
-r-------- 1 test test 0 May 30 01:09 auxv
-r--r--r-- 1 test test 0 May 30 01:09 cgroup
--w------- 1 test test 0 May 30 01:09 clear_refs
-r--r--r-- 1 test test 0 May 30 01:08 cmdline # 启动当前进程使用的命令
-rw-r--r-- 1 test test 0 May 30 01:09 comm
-rw-r--r-- 1 test test 0 May 30 01:09 coredump_filter
-r--r--r-- 1 test test 0 May 30 01:09 cpuset
lrwxrwxrwx 1 test test 0 May 30 01:09 cwd -> /home/test #进程所在的目录
-r-------- 1 test test 0 May 30 01:09 environ # 环境变量
lrwxrwxrwx 1 test test 0 May 30 01:08 exe -> /home/test/main # 进程所执行的二进制文件
dr-x------ 2 test test 0 May 30 01:08 fd #文件描述符
dr-x------ 2 test test 0 May 30 01:09 fdinfo
-rw-r--r-- 1 test test 0 May 30 01:09 gid_map
-r-------- 1 test test 0 May 30 01:09 io
-r--r--r-- 1 test test 0 May 30 01:09 limits
-rw-r--r-- 1 test test 0 May 30 01:09 loginuid
dr-x------ 2 test test 0 May 30 01:09 map_files
-r--r--r-- 1 test test 0 May 30 01:09 maps # 动态库在虚拟地址空间中的映射
-rw------- 1 test test 0 May 30 01:09 mem
-r--r--r-- 1 test test 0 May 30 01:09 mountinfo
-r--r--r-- 1 test test 0 May 30 01:09 mounts
-r-------- 1 test test 0 May 30 01:09 mountstats
dr-xr-xr-x 5 test test 0 May 30 01:09 net
dr-x--x--x 2 test test 0 May 30 01:09 ns
-r--r--r-- 1 test test 0 May 30 01:09 numa_maps
-rw-r--r-- 1 test test 0 May 30 01:09 oom_adj
-r--r--r-- 1 test test 0 May 30 01:09 oom_score
-rw-r--r-- 1 test test 0 May 30 01:09 oom_score_adj
-r--r--r-- 1 test test 0 May 30 01:09 pagemap
-r-------- 1 test test 0 May 30 01:09 patch_state
-r--r--r-- 1 test test 0 May 30 01:09 personality
-rw-r--r-- 1 test test 0 May 30 01:09 projid_map
lrwxrwxrwx 1 test test 0 May 30 01:09 root -> /
-rw-r--r-- 1 test test 0 May 30 01:09 sched
-r--r--r-- 1 test test 0 May 30 01:09 schedstat
-r--r--r-- 1 test test 0 May 30 01:09 sessionid
-rw-r--r-- 1 test test 0 May 30 01:09 setgroups
-r--r--r-- 1 test test 0 May 30 01:09 smaps
-r--r--r-- 1 test test 0 May 30 01:09 stack
-r--r--r-- 1 test test 0 May 30 01:08 stat # 记录了当前进程运行的状态:R、S、D、T、t、X、Z
#R(running) :进程处在运行队列,可以被cpu调度
#S(sleeping)\D(disk sleep): 进程处在等待队列(挂起阻塞),等待条件满足后回到运行队列(唤醒)
#S状态可中断;
#t 调试时的状态
#D状态不可中断,处于被保护的状态,如给磁盘写入时不需要cpu资源,但是写入完成后进程需要获取是否成功,若此进程被中断则无法获得结果。
#T(stopped):
#Z(zombie):进程死亡前先进入僵尸状态,由操作系统检测死亡原因并写入进程控制块;Z状态长时间存在会造成内存泄漏
#X(dead):进程死亡,操作系统回收进程资源,释放内存空间
-r--r--r-- 1 test test 0 May 30 01:09 statm
-r--r--r-- 1 test test 0 May 30 01:08 status # 记录了umask\state\pid\ppid\uid\gid等信息
-r--r--r-- 1 test test 0 May 30 01:09 syscall
dr-xr-xr-x 3 test test 0 May 30 01:09 task
-r--r--r-- 1 test test 0 May 30 01:09 timers
-rw-r--r-- 1 test test 0 May 30 01:09 uid_map
-r--r--r-- 1 test test 0 May 30 01:09 wchan
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
我们可以将进程视为处于以下三种状态之一:
- 运行:进程要么在CPU上执行,要么等待执行,最终将被调度。
int main() { while(1); return 0; }
1
2
3
4
5[test@VM-12-4-centos first]$ ps axj | grep main # 此进程无IO操作,无需挂起等待,一直处在运行队列 13095 14424 14424 13095 pts/1 14424 R+ 1001 0:30 ./main
1
2- 暂停挂起:进程的执行已挂起,将不会安排。进程因接收到SIGSTOP、SIGTSTP、SIGTTIN或SIGTTOU信号而停止,并保持停止状态,直到接收到SIGCONT信号,此时将再次运行。(信号是软件中断的一种形式)。当进程有IO操作时,进程可能大部时间处于S状态
int main() { while(1) printf("hello world"); //IO操作 return 0; }
1
2
3
4
5
6[test@VM-12-4-centos first]$ ps axj | grep main 13095 15938 15938 13095 pts/1 15938 S+ 1001 0:00 ./main [test@VM-12-4-centos first]$ ps axj | grep main # 发送19(SIGSTOP)号信号后进入 T状态 13095 16369 16369 13095 pts/1 13095 T 1001 0:00 ./main [test@VM-12-4-centos first]$ ps axj | grep main # 发送18(SIGCONT)号信号后进入 S状态 13095 16369 16369 13095 pts/1 13095 S 1001 0:00 ./main #此S不带+,后台运行,无法通过Ctrl+C中止
1
2
3
4
5
6- 终止:进程将永久停止。进程因以下三个原因之一而终止:(1)接收到其默认操作是终止进程的信号;(2) 从主程序返回(return)(3)调用退出功能(exit)。父进程在运行,子进程异常退出,若父进程没有回收子进程的资源,则会出现僵尸进程。
# 2. fork创建子进程
- 每个进程都有一个唯一的正(非零)进程ID(PID)。getPID函数返回调用进程的PID。getppid函数返回其父进程(即创建调用进程的进程)的PID
- fork函数只调用了一次,但返回了两次:一次在调用进程(父进程)中,一次在新创建的子进程中。在父进程中,fork返回子进程的PID。在子进程中,fork返回值0(创建失败返回<0)。由于子进程的PID始终为非零,因此返回值提供了一种明确的方式来判断程序是在父进程还是在子进程中执行。
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main()
{
pid_t id = fork();
if(id == 0)
printf("child pid:%d, ppid:%d\n",getpid(),getppid());
else
printf("father pid:%d, ppid:%d\n",getpid(),getppid());
sleep(1);
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
[test@VM-12-4-centos first]$ ./main # 在bash中运行的命令都是bash创建的子进程
father pid:18225, ppid:17072 # father的父进程是bash
child pid:18226, ppid:18225 # 可以看到子进程的ppid正是父进程的pid
[test@VM-12-4-centos first]$ ps axj | head -1 && ps axj | grep 17072 | grep -v 'grep'
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
3901 17072 17072 17072 pts/8 18630 Ss 1001 0:00 -bash
17072 18630 18630 17072 pts/8 18630 R+ 1001 0:00 ps axj
2
3
4
5
6
7
8
新创建的子进程与父进程几乎相同,但并不完全相同。子进程获取父进程用户级虚拟地址空间的相同(但单独,进程具有独立性)副本,包括文本(程序)、数据和bss段、堆和用户堆栈。子进程还可以获得父进程打开的任何文件描述符的相同副本,这意味着子进程可以读取和写入调用fork时在父进程中打开的任何文件。
子进程写入时,子进程通过写时拷贝实现进程的独立性:不写时共享,写入时系统分配新的物理地址给子进程然后修改子进程的页表。例如子进程返回值与父进程不同时,则会单独创建status的存储内存。
#include <stdio.h>
#include <unistd.h>
int Var = 6;
int main()
{
pid_t id = fork();
if(id > 0)
{
while(1)
{
Var = 10;
printf("Father -> Var:%d, &Var:%p\n", Var, &Var);
sleep(1);
}
}
else if(id == 0)
{
while(1)
{
sleep(1); //fork父子进程执行没有先后顺序,保证father先执行
printf("Child -> Var:%d, &Var:%p\n", Var, &Var);
}
}
else
{
printf("fork error\n");
}
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
以上代码的输出结果为:
[test@VM-12-4-centos Fork]$ ./main
Father -> Var:10, &Var:0x60104c
Father -> Var:10, &Var:0x60104c
Child -> Var:6, &Var:0x60104c
2
3
4
可以看到:父子进程在打印同一个全局变量时,由于子进程拷贝了父进程的虚拟地址空间的内容,因此对于全局变量Var他们有着相同的虚拟地址。但是当子进程修改Var时,操作系统重新在物理内存中分配了一段空间并修改了子进程的页表,将Var的虚拟地址重新映射到这个新的物理内存。所以结果是,他们打印出相同的地址,但是变量值却不同。
# 3. 地址空间
- 地址空间的底部四分之三是为用户程序保留的,包括通常的文本、数据、堆和堆栈段。地址空间的顶部四分之一是为内核保留的,这部分地址空间包含内核所使用的代码、数据和堆栈,当应用程序执行系统调用时,会操作这一部分内存。
- 虚拟地址是地址空间上进行区域划分的起始位置。
- 用户在向操作系统申请空间时,系统会在地址空间上修改区域的起始位置(进而修改了空间),但是不会立即向物理地址中申请。当用户对这个空间作读写操作时,才会把这个空间开辟出来:基于缺页中断的物理内存申请。
- 使用地址空间实现了进程间的隔离,降低进程自身内存非法访问的概率;CPU可以将进程看成统一的4GB空间进行区域划分,可以通过页表连接在物理地址上不连续的内存;方便实现内存管理算法实现内存的最优占用。
全局变量自动初始化?不同编译器表现不同,全局变量没有初始化值时,编译不会在磁盘分配空间,只在程序加载时会分配空间


# 4. 进程的退出
# 4.1 基本概念
进程退出的有三种方式:
return
返回: 只有main函数的return才会返回给操作系统;函数调用中的return会返回给main函数。exit(status)
: 退出码会直接返回给操作系统,无论是在函数中还是在main中。_exit(status)
: 强制终止进程,不进行后续的工作,如刷新(printf)用户缓冲区
int fun()
{
eixt(12);
}
int main()
{
fun();
return 0;
}
2
3
4
5
6
7
8
9
注意:return
和eixt
退出时,都会刷新C语言缓冲区的数据到内核。_exit
则是强制终止,不会有刷新缓冲区等后续的操作
进程退出会出现三种情况:
- 程序运行完毕(正常终止)、结果正确 —— 一般退出码为0
echo $?
- 程序运行完毕、结果不正确 —— 退出码非0
- 进程异常终止 —— 异常信号 (opens new window)
- 程序运行完毕(正常终止)、结果正确 —— 一般退出码为0
在C语言库中,可以获取进程的退出码来得知进程退出的原因。
#include <stdio.h>
#include <string.h>
int main()
{
for(int i = 0; i < 140; i++)
printf("%d : %s\n", i, strerror(i));
return 0;
}
2
3
4
5
6
7
8
[test@VM-12-4-centos first]$ ./main # 部分截取
0 : Success
1 : Operation not permitted
2 : No such file or directory
3 : No such process
4 : Interrupted system call
5 : Input/output error
6 : No such device or address
7 : Argument list too long
8 : Exec format error
9 : Bad file descriptor
10 : No child processes
[test@VM-12-4-centos first]$ echo $? # 打印前一个进程退出的退出码
0
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 4.2 父进程等待子进程退出
# 4.2.1 waitpid
- 父进程创建子进程后,需要等待子进程的退出,等待有阻塞和非阻塞两种方式。
- 父进程等待子进程退出可以保证父进程在子进程之后退出,父进程可以获取子进程的退出信息;
- 若子进程退出,父进程不等待回收子进程资源,则会产生僵尸进程(无法通过kill -9 杀掉),造成内存泄漏。 —— 先于父进程退出的子进程程序已经不再运行,但是因为需要保存退出原因,因此资源没有完全释放;僵尸进程不会自动退出释放资源,也不会被kill命令再次杀死
若要回收子进程资源,使用函数waitpid()
pid_t waitpid(pid_t pid, int *status, int options);
1参数说明:
pid_t pid
: pid = -1:等待任意子进程 ; pid > 0:等待进程id与pid相等的子进程int *status
: 进程返回的信息int options
: WNOHANG:非阻塞等待;0默认行为阻塞等待- 返回值:wait成功,返回那个退出子进程的PID;wait失败,返回-1;如果设置了WNOHANG和具体等待的pid进程,但是那个进程的状态还没有改变,则返回0(为0可以继续指向父进程的代码)。
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
int main()
{
pid_t id = fork();
if(id < 0){ //fork失败
printf("fork error\n");
exit(-1);
}
else if(id == 0) // 子进程
{
printf("Child -> pid:%d, ppid:%d\n", getpid(), getppid());
sleep(5); //子进程5秒后退出
exit(66);
}
//父进程
int count = 0;
while(1)
{
printf("Father -> pid:%d, ppid:%d\n", getpid(), getppid());
sleep(1);
printf("%d\n",count++);
if(count > 5) //5秒后让父进程等待子进程退出
{
pid_t ret = waitpid(id, NULL, 0); //阻塞等待
}
// pid_t ret = waitpid(id, NULL, WNOHANG); // 非阻塞,需要while轮询
// if(ret == 0) {
// //子进程没有退出,但是waitpid等待是成功的,需要父进程继续等待
// 父进程可继续进行自己的其他代码
// }else if(ret > 0) {
// //子进程成功退出,waitpid也成功
// }else {
// //等待失败
// }
}
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
[test@VM-12-4-centos Zombie]$ ./main # 部分截取
Father -> pid:30814, ppid:3884
Child -> pid:30815, ppid:30814
0
Father -> pid:30814, ppid:3884
1
Father -> pid:30814, ppid:3884
# 运行脚本: while :;do ps axj | head -1 && ps axj | grep main |grep -v grep;echo -e "\n"; sleep 1;done
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
3884 30814 30814 3884 pts/5 30814 S+ 1001 0:00 ./main
30814 30815 30814 3884 pts/5 30814 S+ 1001 0:00 ./main
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
3884 30814 30814 3884 pts/5 30814 S+ 1001 0:00 ./main
30814 30815 30814 3884 pts/5 30814 Z+ 1001 0:00 [main] <defunct> # 子进程变成了僵尸进程
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
3884 30814 30814 3884 pts/5 30814 S+ 1001 0:00 ./main # 子进程资源被回收 dead
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
3884 30814 30814 3884 pts/5 30814 S+ 1001 0:00 ./main
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 4.2.2 退出状态status
**status:**32个bit,只使用了低16位,高位不用。获取退出码的过程由父进程完成。
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
int main()
{
pid_t id = fork();
int count = 0;
int StatusInfo = 0;
if(id > 0)
{
printf("Father -> pid:%d, ppid:%d\n", getpid(), getppid());
sleep(1);
pid_t ret = waitpid(id, &StatusInfo, 0); //不再执行,阻塞等待子进程结束
//0处设置WNOHANG选项,非阻塞等待
printf("\nChild has been retrieved!\n");
printf("Exit code of the child process:%d\n", (StatusInfo>>8) & 0xFF);
printf("Signals received by child processes:%d\n", StatusInfo & 0x7F);
if(WIFEXITED(StatusInfo)) //使用宏检测低7位,!(status&0x7F);判断进程是否为正常退出。1表示正常
{
printf("WIFEXITED exit with:%d\n", WEXITSTATUS(StatusInfo)); //获取退出码
}
}
else if(id == 0)
{
printf("Child -> pid:%d, ppid:%d\n", getpid(), getppid());
sleep(100); //子进程100秒后退出
exit(30); //子进程退出码为30
}
else
printf("fork error\n");
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
[test@VM-12-4-centos status]$ ./main
Father -> pid:26388, ppid:26121
Child -> pid:26389, ppid:26388
Child has been retrieved!
Exit code of the child process:30
Signals received by child processes:0
WIFEXITED exit with:30
2
3
4
5
6
7
8
# 5. 其他
# 5.1 获取环境变量
- 环境变量具有全局属性,:父进程的环境变量可以被子进程继承。
# printf("Home: %s\n",getenv("HOME"));
[test@VM-12-4-centos first]$ ./main
Home: /home/test
2
3
# 5.2 孤儿进程
- 父进程退出,子进程状态从S+ 变成 S。子进程会被1号进程领养,即其父进程(PPID)变为1(操作系统进程)
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
int main()
{
pid_t id = fork();
if(id == 0)
while(1); //子进程死循环
sleep(1);
exit(-1);// 父进程1秒后退出
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[test@VM-12-4-centos first]$ while :;do ps axj | head -1 && ps axj | grep main |grep -v grep;echo -e "\n"; sleep 1;done
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
13971 17285 17285 13971 pts/0 17285 S+ 1001 0:00 ./main
17285 17286 17285 13971 pts/0 17285 R+ 1001 0:00 ./main
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND # 子进程被1号进程回收
1 17286 17285 13971 pts/0 13971 R 1001 0:01 ./main
2
3
4
5
6
7
8
# 5.3进程的优先级
[Http@ecs-kc1-large-2-linux ~]$ ps -l # 查询
F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD
0 S 1000 29389 29388 0 80 0 - 1741 do_wai pts/3 00:00:00 bash
2
3
UID 执行者
PID进程
PPID父进程
PRI:进程优先级,数值越小优先级越高
NI:nice值,修正优先级 -20~19共40个级别。PRI(NEW) = PRI + NI。top + R + 进程pid + value 可以调整优先级。不建议手动修改,一般由操作系统决定
# 6进程的替换
进程不变,将磁盘中要执行进程的代码和数据替换物理内存中老进程的代码和数据。
程序是代码和数据的集合;程序可以作为对象模块存在于磁盘上,也可以作为地址空间中的段存在。进程是正在执行的程序的特定实例;程序总是在某个进程的上下文中运行。进程替换函数在当前进程的上下文中加载并运行新程序。虽然它会覆盖当前进程的地址空间,但不会创建新进程。新程序仍然具有相同的PID,并且它继承了调用进程替换函数时打开的所有文件描述符。
进程替换函数有如下几个:
//C标准库提供的接口,#include <unistd.h>
int execl(const char *path, const char *arg0, ... /*, (char *)0 */);
int execle(const char *path, const char *arg0, ... /*, (char *)0, char *const envp[]*/);
int execlp(const char *file, const char *arg0, ... /*, (char *)0 */);
int execv(const char *path, char *const argv[]);
int execve(const char *path, char *const argv[], char *const envp[]);//这个是由操作系统提供的;其他由C库函数提供
int execvp(const char *file, char *const argv[]);
2
3
4
5
6
7
8
进程替换不需要返回值检测,成功执行替换的程序;失败则执行原来程序
其中各个函数的名称可以按包含的字母解剖为:
- l(list) :传参使用可变参数列表
- v(vector):传参使用数组
- p(path):传参指定程序路径
- e(environment):指定进程运行的环境变量
具体例子:
//1 path:二进制文件所在的绝对路径;args:可变参数列表,以NULL结尾
execl("usr/bin/ls","ls","-a","-l",NULL);
//2 可以传入用户自定义的环境变量,这个函数替换了原来进程的环境变量;指针数组需要以NULL结尾
char *env[] = {
"Modified Variable",
NULL
};
execle("./TestExecle", "TestExecle", NULL, env);
//3 只需给定二进制文件名就可以了(ls),会自动搜索环境变量找到ls的path。
execlp("ls", "ls", "-a", "-l",NULL);
//4
char *argv[] = {"ls", "-a", "-l", NULL};
execv("usr/bin/ls",argv);
//5 将list用数组表示即可
char *argv[] = {"TestExecle", NULL};
execve("./TestExecle", argv, env);
//6
execvp("ls",argv);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
下面给个关于execle
的具体的实操:
// 新建源程序,文件名为:TestExecle.c
#include <stdio.h>
int main(int argc, char *argv[], char *env[])
{
printf("I am TestExecle program\n");
for(int i = 0; env[i]; i++)
{
printf("%s\n",env[i]);
}
return 0;
}
// 新建源程序,文件名为:main.c
#include <stdio.h>
int main()
{
char *env[] = {
"Modified Variable",
NULL
};
//extern char **environ;
//execle("./TestExecle", "TestExecle", NULL, environ); 完整打印也可这样操作
execle("./TestExecle", "TestExecle", NULL, env);
printf("hello world\n"); //程序替换之后,后续的代码不会执行
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
.PHONY:all
all:main TestExecle
main:main.c
gcc -o $@ $^ -std=c99
TestExecle:TestExecle.c
gcc -o $@ $^ -std=c99
.PHONY:clean
clean:
rm -f main TestExecle
2
3
4
5
6
7
8
9
10
11
12
[test@VM-12-4-centos Execl]$ make
gcc -o main main.c -std=c99
gcc -o TestExecle TestExecle.c -std=c99
[test@VM-12-4-centos Execl]$ ./main # main将自定义的环境变量传给了TestExecle;通过main函数运行结果可以验证
I am TestExecle program
Modified Variable
2
3
4
5
6
# 内建命令
exec*在执行usr/bin
下的程序时可以替换成功,但执行cd..
时,pwd
得到的仍然是当前目录。此时需要调用系统的函数实现,如chdir("dir_path")