1 进程启动和终止
- c 程序启动过程
- 内核启动特色例程
- 启动例程
- 在进程的main函数执行之前内核会启动
- 该例程放置在/lib/libc.so.***中
- 编译器在编译时会将启动例程编译进可执行文件中
- 启动例程作用
- 搜集命令行的参数传递给main函数中的argc和argv
- 搜集环境信息构建环境表并传给main函数
- 登记进程的终止函数
- 进程终止
- 正常终止
- 从main函数返回
- 调用exit(标准c库函数)
- 调用_exit或_Exit(系统调用)
- 最后一个线程从其启动例程返回
- 最后一个线程调用pthread_exit
- 异常终止
- 调用abort
- 接受到一个信号并终止
- 最后一个线程对取消请求做处理响应
- 进程返回
- 通常程序运行成功返回0,否则返回非0
- 在shell中可以查看进程返回值(echo $?)
- 进程启动和终止过程
- 终止函数登记
2 进程状态、进程创建和进程分类
2.1 进程查看和进程状态
- 查看当前进程
ps
- 查看所有进程
ps -ef
- 查看进程状态
ps -aux
- 进程状态stat
2.2 进程调度和进程状态变化
- 进程调度
- 进程状态变化关系
2.3 进程标识
2.4 创建进程
int example2()
{
printf("pid: %d\n", getpid());
pid_t pid;
pid = fork();
if (pid < 0) {
perror("fork error\n");
} else if (pid > 0) {
printf("I am parent process,pid is %d,ppid is %d, fork return is %d\n", getpid(), getppid(), pid);
} else {
printf("I am child process,pid is %d,ppid is %d, fork return is %d\n", getpid(), getppid(), pid);
}
printf("pid=%d\n", getpid());
sleep(1);
return 0;
}
int example3()
{
printf("current pid: %d\n", getpid());
pid_t pid;
pid = fork();
if (pid < 0) {
perror("fork error\n");
} else if (pid > 0) {
for (int i = 0; i < 10; i++) {
printf("I am parent process,pid is %d,ppid is %d, fork return is %d\n", getpid(), getppid(), pid);
sleep(1);
}
} else {
for (int i = 0; i < 10; i++) {
printf("I am child process,pid is %d,ppid is %d, fork return is %d\n", getpid(), getppid(), pid);
sleep(1);
}
}
return 0;
}
- 父进程fork子进程,子进程会继承父进程的一些信息。数据段、堆、栈是复制过去的,物理内存各自有各自的空间。
int g_v = 30;
int example4()
{
int a_v = 30;
static int s_v = 30;
printf("current pid: %d\n", getpid());
pid_t pid;
pid = fork();
if (pid < 0) {
perror("fork error\n");
} else if (pid > 0) {
g_v = 40;
a_v = 40;
s_v = 40;
printf("I am parent process,pid is %d,ppid is %d, fork return is %d\n", getpid(), getppid(), pid);
printf("g_v: %p, a_v: %p, s_v: %p\n", &g_v, &a_v, &s_v);
} else {
g_v = 50;
a_v = 50;
s_v = 50;
printf("I am child process,pid is %d,ppid is %d, fork return is %d\n", getpid(), getppid(), pid);
printf("g_v: %p, a_v: %p, s_v: %p\n", &g_v, &a_v, &s_v);
}
printf("pid: %d\n", getpid());
return 0;
}
可以观察到,父子进程的数据段、堆、栈中的内容被修改,而内存地址是相同的,可以知道fork出的子进程会复制父进程的虚拟内存,但不会复制物理内存。如下图,可直观描述父子进程的内存关系。
int example5(int argc, char* argv[])
{
if (argc < 2) {
fprintf(stderr, "usage: %s file [exit|_exit|return]\n", argv[0]);
exit(1);
}
int fd = open(argv[1], O_WRONLY);
if (fd < 2) {
perror("open error!\n");
exit(1);
}
pid_t pid;
pid = fork();
if (pid < 0) {
perror("fork error\n");
} else if (pid > 0) {
if (lseek(fd, 0L, SEEK_END) < 0) {
perror("lseek error!");
exit(1);
}
} else {
char* str = "hello world\n";
ssize_t size = strlen(str) * sizeof(char);
sleep(3);
if (write(fd, str, size) != size) {
perror("write error!\n");
exit(1);
}
}
printf("pid: %d finish \n", getpid());
close(fd);
return 0;
}
- 如果连续fork() n次,会产生2的n次方个子进程
2.6 进程链和进程扇
int example6(int argc, char* argv[])
{
int counter = 0;
if (argc < 2) {
counter = 2;
} else {
counter = atoi(argv[1]);
}
pid_t pid;
for (int i = 1; i < counter; i++) {
pid = fork();
if (pid < 0) {
perror("fork error!\n");
} else if (pid > 0) {
break;
}
}
printf("pid: %d ,ppid: %d\n", getpid(), getppid());
while (1) {
sleep(1);
}
return 0;
}
int example7(int argc, char* argv[])
{
int counter = 0;
if (argc < 2) {
counter = 2;
} else {
counter = atoi(argv[1]);
}
pid_t pid;
for (int i = 1; i < counter; i++) {
pid = fork();
if (pid < 0) {
perror("fork error!\n");
} else if (pid == 0) {
break;
}
}
printf("pid: %d ,ppid: %d\n", getpid(), getppid());
while (1) {
sleep(1);
}
return 0;
}
ps -ef | grep 进程名
pstree
2.7 守护进程、孤儿进程、僵尸进程
- 守护进程
- 孤儿进程:
父进程结束,子进程就变成了孤儿进程,会由1号进程(init进程)领养。 - 僵尸进程:
子进程退出了,但是父进程没有用wait或waitpid去获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中,这种进程称为僵尸进程。
3 进程的相关系统调用
3.1 wait函数
void out_status(const int status)
{
if (WIFEXITED(status)) {
printf("normal exit: %d\n", WEXITSTATUS(status));
} else if (WIFSIGNALED(status)) {
printf("abnormal exit: %d\n", WTERMSIG(status));
} else if (WIFSTOPPED(status)) {
printf("stop exit: %d\n", WSTOPSIG(status));
} else {
printf("unknow exit\n");
}
}
int example8()
{
int status;
pid_t pid;
if ((pid = fork()) < 0) {
perror("fork error\n");
exit(1);
} else if (pid == 0) {
printf("pid: %d, ppid: %d\n", getpid(), getppid());
exit(3);
}
wait(&status);
out_status(status);
printf("-------------------------------------------------------------------------\n");
if ((pid = fork()) < 0) {
perror("fork error\n");
exit(1);
} else if (pid == 0) {
printf("pid: %d, ppid: %d\n", getpid(), getppid());
int i = 3, j = 0;
int k = i / j;
}
wait(&status);
out_status(status);
printf("-------------------------------------------------------------------------\n");
if ((pid = fork()) < 0) {
perror("fork error\n");
exit(1);
} else if (pid == 0) {
printf("pid: %d, ppid: %d\n", getpid(), getppid());
pause();
}
do {
pid = waitpid(pid, &status, WNOHANG | WUNTRACED);
if (pid == 0)
sleep(1);
} while (pid == 0);
out_status(status);
printf("-------------------------------------------------------------------------\n");
return 0;
}
3.2 exec函数
int example9()
{
const char* const cmd1 = "cat";
const char* const cmd2 = "/bin/cat";
const char* const argv1 = "/etc/passwd";
const char* const argv2 = "/etc/group";
pid_t pid;
if ((pid = fork()) < 0) {
perror("fork error\n");
exit(1);
} else if (pid == 0) {
if (execl(cmd2, cmd1, argv1, argv2, nullptr) < 0) {
perror("execl error!\n");
exit(1);
}
printf("after execl...\n");
}
sleep(1);
printf("----------------------------------------------------------------------\n");
char* argv[4] = { (char*)cmd1, (char*)argv1, (char*)argv2, NULL };
if ((pid = fork()) < 0) {
perror("fork error\n");
exit(1);
} else if (pid == 0) {
if (execvp(cmd2, argv) < 0) {
perror("execl error!\n");
exit(1);
}
printf("after execl...\n");
}
sleep(1);
printf("----------------------------------------------------------------------\n");
wait(nullptr);
return 0;
}
3.3 system函数
|