问题: strace 输出中的 execve(...)究竟是什么?
进程理论状态切换
进程生命周期
Linux进程基本概念
- 进程是 Linux 任务的执行单元,也是 Linux 系统资源的分配单位
- 每个 Linux 应用程序运行后由一个或多个进程
- 一个 Linux 进程可以执行一个或多个程序
- Linux 进程有多种不同状态(即:Linux 进程有不同的活法)
Linux 进程状态剖析
- 就绪 / 运行 状态 (R) : TASK_RUNNING
阻塞状态:
- 可中断 (S) : TASK_INTERRUPTIBLE (可被内核信号唤醒)
- 不可中断 (D) : TASK_UNINTERRUPTIBLE (对实时性要求高时使用)
- 停止状态 (T) : TASK_STOP
退出状态 : TASK_DEAD
- 僵尸 (X) : EXIT_ZIMBIE (进程生命期结束,但内核中还保存着与进程相关的数据信息)
- 死亡 (Z) : EXIT_DEAD
tiansong@tiansong:~/Desktop/linux$ ps ax | grep pts/0
4826 pts/0 Ss 0:00 /bin/bash
5328 pts/0 R+ 0:00 ps ax
5329 pts/0 S+ 0:00 grep --color=auto pts/0
tiansong@tiansong:~/Desktop/linux$ sleep 120
^C 【CTRL + C】
tiansong@tiansong:~/Desktop/linux$ sleep 120 &
[1] 5423
tiansong@tiansong:~/Desktop/linux$ ps ax | grep pts/0
4826 pts/0 Ss 0:00 /bin/bash
5423 pts/0 S 0:00 sleep 120
5453 pts/0 R+ 0:00 ps ax
5454 pts/0 S+ 0:00 grep --color=auto pts/0
tiansong@tiansong:~/Desktop/linux$ kill 5423
[1]+ Terminated sleep 120
tiansong@tiansong:~/Desktop/linux$ ps ax | grep pts/0
4826 pts/0 Ss 0:00 /bin/bash
5501 pts/0 R+ 0:00 ps ax
5502 pts/0 R+ 0:00 grep --color=auto pts/0
loop.c
int main()
{
while(1);
return 0;
}
tiansong@tiansong:~/Desktop/linux$ gcc ./loop.c -o loop.out
tiansong@tiansong:~/Desktop/linux$ ./loop.out &
[1] 6510
tiansong@tiansong:~/Desktop/linux$ ps ax | grep pts/3
6021 pts/3 Ss 0:00 /bin/bash
6510 pts/3 R 0:04 ./loop.out
6522 pts/3 R+ 0:00 ps ax
6523 pts/3 S+ 0:00 grep --color=auto pts/3
tiansong@tiansong:~/Desktop/linux$ kill 6510
[1]+ Terminated ./loop.out
tiansong@tiansong:~/Desktop/linux$ ps ax | grep pts/3
6021 pts/3 Ss 0:00 /bin/bash
6543 pts/3 R+ 0:00 ps ax
6544 pts/3 S+ 0:00 grep --color=auto pts/3
Linux 进程树简介
Linux 进程必知必会
- 每个进程都有一个唯一的标识符(PID)
- 每个进程都是由一个进程创建而来(即:父进程)
#include
#include
pid_t getpid(); // 当前进程 id
pid_t getppid(); // 当前进程父进程 id
一些有趣的问题
- 问题一:第 1 个进程时什么?
- 问题二:如何创建进程
Linux 进程树
- 整个 Linux 系统的所有进程构成一个树结构
- 树根由内核自动创建,即:
IDLE (PID → 0)
系统中的第一个进程是 init / systemd (PID → 1)
- 0 号进程创建 1 号进程,1 号进程负责完成内核部分初始化工作
- 1 号进程加载执行初始化程序,演变为用户态 1 号进程
tiansong@tiansong:~/Desktop/linux$ pstree
systemd-+-ModemManager---2*[{ModemManager}]
|-NetworkManager---2*[{NetworkManager}]
|-VGAuthService
|-accounts-daemon---2*[{accounts-daemon}]
|-acpid
|-avahi-daemon---avahi-daemon
|-bluetoothd
|-colord---2*[{colord}]
|-cron
|-cups-browsed---2*[{cups-browsed}]
|-cupsd---dbus
|-dbus-daemon
|-fwupd---4*[{fwupd}]
|-gdm3-+-gdm-session-wor-+-gdm-x-session-+-Xorg---{Xorg}
| | | |-gnome-session-b-+-ssh-agent
| | | | `-2*[{gnome-session-b}]
| | | `-2*[{gdm-x-session}]
| | `-2*[{gdm-session-wor}]
| `-2*[{gdm3}]
|-gnome-keyring-d---3*[{gnome-keyring-d}]
|-irqbalance---{irqbalance}
|-2*[kerneloops]
|-networkd-dispat
|-nmbd
|-polkitd---2*[{polkitd}]
|-rsyslogd---3*[{rsyslogd}]
|-rtkit-daemon---2*[{rtkit-daemon}]
|-sh---node-+-node-+-bash
| | `-11*[{node}]
| |-node---11*[{node}]
| |-node---12*[{node}]
| `-10*[{node}]
|-smbd-+-cleanupd
| |-samba-bgqd
| `-smbd-notifyd
|-snapd---17*[{snapd}]
|-sshd---sshd---sshd---bash---sleep
|-switcheroo-cont---2*[{switcheroo-cont}]
|-systemd-+-(sd-pam)
| |-at-spi-bus-laun-+-dbus-daemon
| | `-3*[{at-spi-bus-laun}]
| |-at-spi2-registr---2*[{at-spi2-registr}]
| |-dbus-daemon
| |-dconf-service---2*[{dconf-service}]
| |-evolution-addre---5*[{evolution-addre}]
| |-evolution-calen---8*[{evolution-calen}]
| |-evolution-sourc---3*[{evolution-sourc}]
| |-gjs---10*[{gjs}]
| |-gnome-session-b-+-evolution-alarm---5*[{evolution-alarm}]
| | |-gsd-disk-utilit---2*[{gsd-disk-utilit}]
| | |-update-notifier---3*[{update-notifier}]
| | `-3*[{gnome-session-b}]
| |-gnome-session-c---{gnome-session-c}
| |-gnome-shell-+-ibus-daemon-+-ibus-dconf---3*[{ibus-dconf}]
| | | |-ibus-engine-sim---2*[{ibus-engine-sim}]
| | | |-ibus-extension----3*[{ibus-extension-}]
| | | `-2*[{ibus-daemon}]
| | `-11*[{gnome-shell}]
| |-gnome-shell-cal---5*[{gnome-shell-cal}]
| |-gnome-terminal--+-bash---pstree
| | `-4*[{gnome-terminal-}]
| |-goa-daemon---3*[{goa-daemon}]
| |-goa-identity-se---2*[{goa-identity-se}]
| |-gsd-a11y-settin---3*[{gsd-a11y-settin}]
| |-gsd-color---3*[{gsd-color}]
| |-gsd-datetime---3*[{gsd-datetime}]
| |-gsd-housekeepin---3*[{gsd-housekeepin}]
| |-gsd-keyboard---3*[{gsd-keyboard}]
| |-gsd-media-keys---3*[{gsd-media-keys}]
| |-gsd-power---3*[{gsd-power}]
| |-gsd-print-notif---2*[{gsd-print-notif}]
| |-gsd-printer---2*[{gsd-printer}]
| |-gsd-rfkill---2*[{gsd-rfkill}]
| |-gsd-screensaver---2*[{gsd-screensaver}]
| |-gsd-sharing---3*[{gsd-sharing}]
| |-gsd-smartcard---4*[{gsd-smartcard}]
| |-gsd-sound---3*[{gsd-sound}]
| |-gsd-usb-protect---3*[{gsd-usb-protect}]
| |-gsd-wacom---2*[{gsd-wacom}]
| |-gsd-wwan---3*[{gsd-wwan}]
| |-gsd-xsettings---3*[{gsd-xsettings}]
| |-gvfs-afc-volume---3*[{gvfs-afc-volume}]
| |-gvfs-goa-volume---2*[{gvfs-goa-volume}]
| |-gvfs-gphoto2-vo---2*[{gvfs-gphoto2-vo}]
| |-gvfs-mtp-volume---2*[{gvfs-mtp-volume}]
| |-gvfs-udisks2-vo---3*[{gvfs-udisks2-vo}]
| |-gvfsd-+-gvfsd-trash---2*[{gvfsd-trash}]
| | `-2*[{gvfsd}]
| |-gvfsd-fuse---5*[{gvfsd-fuse}]
| |-gvfsd-metadata---2*[{gvfsd-metadata}]
| |-ibus-portal---2*[{ibus-portal}]
| |-ibus-x11---2*[{ibus-x11}]
| |-pulseaudio---3*[{pulseaudio}]
| |-tracker-miner-f---4*[{tracker-miner-f}]
| `-xdg-permission----2*[{xdg-permission-}]
|-systemd-journal
|-systemd-logind
|-systemd-resolve
|-systemd-timesyn---{systemd-timesyn}
|-systemd-udevd
|-udisksd---4*[{udisksd}]
|-unattended-upgr---{unattended-upgr}
|-upowerd---2*[{upowerd}]
|-vmtoolsd---3*[{vmtoolsd}]
|-whoopsie---2*[{whoopsie}]
`-wpa_supplicant
// 打印当前进程 PID
tiansong@tiansong:~/Desktop/linux$ echo $$
6021
// 打印从树根到当前进程的路径,systemd(1091) 再次被加载完成更多初始化工作
tiansong@tiansong:~/Desktop/linux$ pstree -A -p -s $$
systemd(1)---systemd(1091)---gnome-terminal-(6837)---bash(6845)---pstree(7046)
tiansong@tiansong:~/Desktop/linux$ ./loop.out &
[1] 7214
tiansong@tiansong:~/Desktop/linux$ ./loop.out &
[2] 7217
tiansong@tiansong:~/Desktop/linux$ ./loop.out &
[3] 7220
tiansong@tiansong:~/Desktop/linux$ ./loop.out &
[4] 7223
tiansong@tiansong:~/Desktop/linux$ pstree -A -p -s $$
systemd(1)---systemd(1091)---gnome-terminal-(6837)---bash(6845)-+-loop.out(7254)
|-loop.out(7255)
|-loop.out(7256)
|-loop.out(7257)
`-pstree(7259)
Linux 进程创建实战
Linux 进程创建
pid_t fork(void);
- (系统调用)通过当前进程创建新进程,当前进程为父进程,新进程为子进程
int execve(const char *pathname, char *const argv[], char *const envp[]);
- (系统调用)在当前进程中执行 pathname 指定的程序代码
fork() 的工作方式
- 为子进程申请内存空间,并将父进程数据完全复制到子进程空间
- 两个进程中的程序执行位置完全一致(
fork()函数调用位置
) 不同之处:
- 父进程: fork() 返回紫禁城 PID
- 子进程: fork() 返回 0
通过 fork() 返回值判断父子进程,执行不同代码
下面程序输出什么? 为什么?
main.c
#include
#include
#include
static int g_global = 0;
int main()
{
int pid = 0;
printf("Hello Word!\n");
printf("current = %d\n", getpid());
if ((pid = fork()) > 0) {
g_global = 1;
usleep(100);
printf("child = %d\n", pid);
printf("%d g_global = %d\n", getpid(), g_global);
}
else {
g_global = 10;
printf("parent = %d\n", getppid());
printf("%d g_global = %d\n", getpid(), g_global);
}
return 0;
}
tiansong@tiansong:~/Desktop/linux$ ./main.out
Hello Word!
current = 8010
child = 8011
parent = 8010
8011 g_global = 10
8010 g_global = 1
fork 与 execve 应用
思考: 如何理解 “每个 Linux 进程可以执行一个或多个程序”?
1、 进程是一个存在于内存的实体
2、 进程由程序加载而来
3、 进程可以加载多个程序的代码
execve (...) 的工作方式
- 根据参数路径加载可程序程序(程序加载到当前进程中)
- 通过可执行程序信息构建进程数据,并写入当前进程空间
- 将程序执行位置重置到入口地址处(即:main())
execve(...)
将重置当前进程空间(代码 & 数据)而不会创建新进程
下面的程序输出什么?为什么?
test.c
#include
#include
#include
#define EXE "helloword.out"
int main()
{
char *args[] = {EXE, NULL};
printf("begin\n");
printf("pid = %d\n", getpid());
execve(EXE, args, NULL);
printf("end\n");
return 0;
}
helloword.c
#include
#include
#include
int main()
{
printf("exec = %d, %s\n",
getpid(), "Hello World!");
return 0;
}
tiansong@tiansong:~/Desktop/linux$ gcc test.c -o test.out
tiansong@tiansong:~/Desktop/linux$ gcc helloword.c -o helloword.out
tiansong@tiansong:~/Desktop/linux$ ./test.out
begin
pid = 8518
exec = 8518, Hello World!
注意:end 没有输出
test.c
#include
#include
#include
#define EXE "helloword.out"
int create_process(const char *path, char *args[])
{
int ret = fork();
if (ret == 0) {
execve(path, args, NULL);
}
return ret;
}
int main()
{
char *args[] = {EXE, NULL};
printf("begin\n");
printf("pid = %d\n", getpid());
printf("child = %d\n", create_process(EXE, args));
printf("end\n");
return 0;
}
tiansong@tiansong:~/Desktop/linux$ ./test.out
begin
pid = 8833
child = 8834
end
exec = 8834, Hello World!