二手商品网站的设计与建设论文营销背景包括哪些内容
文章目录
- 创建两个进程
- **2. 实现思路及源代码**
- 2.1 实现思路
- 2.1.1 `fork()` 函数
- 2.1.2 思路分析
- 2.2 源代码
- 2.2.1 源代码分析
- 2.2.2 源代码测试结果
- **3. 打印进程树**
- 3.1 tmux操作步骤
- 3.1.1 启动 `tmux`
- 3.1.2 分屏操作(Ctrl+b是在告诉系统准备输入一个快捷键)
- 3.1.3 切换窗口操作
- 3.1.4 在新分屏中运行 `pstree`
- 3.1.5 退出 `tmux`
- 3.2 打印进程树结果
- **4. 源码分析**
- 4.1 Linux0.12中涉及的主要源码
- 4.2 结合源码分析实验代码
- 4.2.1 进程创建 (`fork`)
- 4.2.2 具体进程状态转换
- **5. 实验过程中遇到的问题及解决方法**
- 问题5.1 fork_two_children.c在运行过程中过快结束,来不及打印
- 问题5.2 单窗口在休眠状态下无法打印进程树
- **6.参考链接**
- 进程概念——PCB详讲
- 【Linux系列】进程PCB控制管理详解
- [【linux系统】进程(进程PCB + 进程地址空间+进程控制)](https://blog.csdn.net/ProcedureStone/article/details/142786072?ops_request_misc=&request_id=&biz_id=102&utm_term=%E8%BF%9B%E7%A8%8BPCB&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-1-142786072.142^v102^control&spm=1018.2226.3001.4187)
创建两个进程
2. 实现思路及源代码
2.1 实现思路
2.1.1 fork()
函数
fork()
是 Linux/Unix 系统中的一个系统调用,用于创建一个新的进程。这个新的进程(称为子进程)是父进程的复制品,并从 fork()
语句之后开始执行。
fork()
返回值如下:
- 在父进程中:返回子进程的 PID。
- 在子进程中:返回 0。
- 失败时:返回 -1。
2.1.2 思路分析
由2.1.1可知,在处于子进程时,fork返回的PID=0;在处于父进程时,fork返回的PID>0;而实验需要创建两个子进程,也就是在父进程完成第一个子进程创建后,通过if判断语句,判断当前是否处于父进程(PID>0);如果处于父进程,则再次创建子进程。这样就
举个例子:假设父进程 PID 是 1000
,执行的顺序如下:
- 父进程
1000
调用fork()
,创建 子进程 1 (1001
)。 - 子进程 1 (
1001
) 进入if (pid1 == 0)
分支,打印信息后return
退出。 - **父进程
1000
继续执行pid2 = fork();
,创建 子进程 2 (1002
)。 - 子进程 2 (
1002
) 进入if (pid2 == 0)
分支,打印信息后return
退出。 - 父进程
1000
继续执行,打印父进程对应的信息。
2.2 源代码
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>int main() {pid_t pid1, pid2;pid1 = fork(); // 创建第一个子进程if (pid1 < 0) {perror("fork failed");return 1;}if (pid1 == 0) {// 子进程1printf("Child 1: PID = %d, Parent PID = %d\n", getpid(), getppid());sleep(15); // 保持进程存活return 0;} else {pid2 = fork(); // 创建第二个子进程if (pid2 < 0) {perror("fork failed");return 1;}if (pid2 == 0) {// 子进程2printf("Child 2: PID = %d, Parent PID = %d\n", getpid(), getppid());sleep(15); // 保持进程存活return 0;} else {// 父进程printf("Parent: PID = %d, Child1 PID = %d, Child2 PID = %d\n", getpid(), pid1, pid2);sleep(15); // 父进程也保持存活}}return 0;
}
2.2.1 源代码分析
pid1 = fork(); // 创建第一个子进程
-
第一个
fork()
调用后:如果返回 0(子进程):子进程执行
if (pid1 == 0)
内的代码。如果返回非 0(父进程):父进程继续执行后续代码。
-
此时,进程数量已经变成 2 个(一个是原始父进程,另一个是
pid1
产生的子进程)。
if (pid1 == 0) {// 子进程1printf("Child 1: PID = %d, Parent PID = %d\n", getpid(), getppid());sleep(15);return 0;
}
- 第一个子进程会直接 return,不会继续往下执行
fork()
相关的代码。
pid2 = fork(); // 创建第二个子进程
- 第二个
fork()
调用后:(前提条件是pid1>0,处于父进程)
如果返回 0(子进程):子进程执行 if (pid2 == 0)
内的代码。
如果返回非 0(父进程):父进程继续执行后续代码。
此时,此时,进程数量从 2 个变成 3 个:
- 原始的父进程
- 第一个子进程(
pid1
) - 第二个子进程(
pid2
)
2.2.2 源代码测试结果
s:~/os_exp/exp2_process$ ./fork_two_children
Parent: PID = 1585110, Child1 PID = 1585111, Child2 PID = 1585112
Child 1: PID = 1585111, Parent PID = 1585110
Child 2: PID = 1585112, Parent PID = 1585110
3. 打印进程树
3.1 tmux操作步骤
3.1.1 启动 tmux
tmux
3.1.2 分屏操作(Ctrl+b是在告诉系统准备输入一个快捷键)
-
垂直分屏(左右分屏):
Ctrl + b,然后按 %
-
水平分屏(上下分屏):
Ctrl + b,然后按 "
3.1.3 切换窗口操作
-
在不同的分屏间切换:
Ctrl + b,然后按 方向键(←/→/↑/↓)
3.1.4 在新分屏中运行 pstree
-
在其中一个分屏中运行:
./fork_two_children
-
切换到另一个分屏,运行:
pstree -p 父进程端口号
3.1.5 退出 tmux
-
关闭当前窗口:
exit
3.2 打印进程树结果
~/os_exp/exp2_process$ pstree -p 1585110
fork_two_childr(1585110)─┬─fork_two_childr(1585111)└─fork_two_childr(1585112)
4. 源码分析
4.1 Linux0.12中涉及的主要源码
struct task_struct {/* these are hardcoded - don't touch */long state; /* -1 unrunnable, 0 runnable, >0 stopped */long counter;long priority;long signal;struct sigaction sigaction[32];long blocked; /* bitmap of masked signals *//* various fields */int exit_code;unsigned long start_code,end_code,end_data,brk,start_stack;long pid,pgrp,session,leader;int groups[NGROUPS];/* * pointers to parent process, youngest child, younger sibling,* older sibling, respectively. (p->father can be replaced with * p->p_pptr->pid)*/struct task_struct *p_pptr, *p_cptr, *p_ysptr, *p_osptr;unsigned short uid,euid,suid;unsigned short gid,egid,sgid;unsigned long timeout,alarm;long utime,stime,cutime,cstime,start_time;struct rlimit rlim[RLIM_NLIMITS]; unsigned int flags; /* per process flags, defined below */unsigned short used_math;/* file system info */int tty; /* -1 if no tty, so it must be signed */unsigned short umask;struct m_inode * pwd;struct m_inode * root;struct m_inode * executable;struct m_inode * library;unsigned long close_on_exec;struct file * filp[NR_OPEN];/* ldt for this task 0 - zero 1 - cs 2 - ds&ss */struct desc_struct ldt[3];/* tss for this task */struct tss_struct tss;};
PCB通常包含的内容 | |||
---|---|---|---|
进程描述信息 | 进程控制和管理信息 | 资源分配清单 | 处理机相关信息 |
进程标识符(PID) | 进程当前状态 | 代码段指针 | 通用寄存器值 |
用户标识符(UID) | 进程优先级 | 数据段指针 | 地址寄存器值 |
-
在多道程序系统中,多个进程都要在CPU上运行,有时还要申请使用其他资源,由于资源的宝贵性,使得并非每个进程都能立即得到资源,从而导致进程之间的竞争(竞争是由两个以上进程以显式或隐式的方式共享资源而引起的状态)。
-
一般情况下进程有三种状态,就绪(资源,CPU),运行(资源,CPU),阻塞(资源,CPU)。
-
Linux在每个进程的task_struct结构中,定义了state域来描述进程的调度状态,共有五种,定义如下:
#define TASK_RUNNING 0 // 进程正在运行或已准备就绪。#define TASK_INTERRUPTIBLE 1 // 进程处于可中断等待状态。#define TASK_UNINTERRUPTIBLE 2 // 进程处于不可中断等待状态,主要用于I/O操作等待。#define TASK_ZOMBIE 3 // 进程处于僵死状态,已经停止运行,但父进程还没发信号。#define TASK_STOPPED 4 // 进程已停止。
4.2 结合源码分析实验代码
4.2.1 进程创建 (fork
)
在实验代码中,调用fork()
两次,创建了两个子进程。在Linux 0.12中,fork()
的执行流程如下:
-
系统调用入口:
fork()
系统调用会触发sys_fork()
函数(在kernel/system_call.s
中定义),进而调用copy_process()
(在kernel/fork.c
中)。 -
复制进程:
copy_process()
会为新进程分配PCB,即task_struct
结构,并从父进程的PCB复制大部分信息(如CPU寄存器、文件描述符等),且会为新进程分配唯一的pid
(进程号),将子进程的state
设置为TASK_RUNNING
(就绪态),表示进程可以被调度执行,同时把新创建的进程加入调度队列,等待CPU调度。
4.2.2 具体进程状态转换
(1)初始状态
- 父进程处于
TASK_RUNNING
状态,执行fork()
创建Child 1
进程。
(2)第一次fork()
Child 1
被创建,其PCB信息从父进程复制,状态为TASK_RUNNING
。Child 1
的pid
被分配,ppid
指向父进程。- 由于
fork()
的返回值不同:- 父进程继续执行,并进入下一步创建
Child 2
。 Child 1
进入自己的分支,执行printf
然后调用sleep(15)
,使其进入TASK_INTERRUPTIBLE
(睡眠状态)。
- 父进程继续执行,并进入下一步创建
(3)第二次fork()
Child 2
被创建,task_struct
再复制一次。Child 2
的pid
被分配,ppid
仍指向父进程。- 父进程继续执行,
Child 2
进入自己的分支执行printf
然后sleep(15)
,进入TASK_INTERRUPTIBLE
状态。
(4)运行状态
- 进程调度器会根据
counter
字段和优先级选择进程运行。 - **如果时间片耗尽或进程主动
sleep(15)
,状态转换为TASK_INTERRUPTIBLE**
,等待时间到达或信号唤醒。
(5)结束状态
Child 1
和Child 2
完成sleep(15)
后,调用return 0;
,退出进程。- 在Linux 0.12中,进程退出时:
- 进入
TASK_ZOMBIE
状态,等待父进程调用wait()
回收资源。 - 如果父进程没有及时
wait()
,子进程的task_struct
仍然保留在进程表中(僵尸进程)。
- 进入
- 当父进程执行完
sleep(15)
后,也会结束,最终所有进程都退出,系统回收资源。
5. 实验过程中遇到的问题及解决方法
问题5.1 fork_two_children.c在运行过程中过快结束,来不及打印
解决方法:调用sleep()
函数让进程进入休眠(sleep)状态,即在指定的秒数内暂停执行,从而支持用户进行进程树的打印操作
问题5.2 单窗口在休眠状态下无法打印进程树
解决方法:学习了tmux操作,通过打开水平窗口,并进行窗口的切换操作,从而通过 pstree -p 端口号 进行进程树的打印操作,详细操作方案可参考3.1内容