厦门的网站,手机优化软件排名,wordpress获取用户名,英语网站开发的背景文章目录 一.进程的概念进程控制块#xff08;PCB#xff09; 二.进程查看通过指令查看进程通过proc目录查看进程的cwd和exe获取进程pid和ppid通过fork()创建子进程 一.进程的概念
进程是一个运行起来的程序#xff0c;而程序是存放在磁盘的#xff0c;cpu要想执行程序的指… 文章目录 一.进程的概念进程控制块PCB 二.进程查看通过指令查看进程通过proc目录查看进程的cwd和exe获取进程pid和ppid通过fork()创建子进程 一.进程的概念
进程是一个运行起来的程序而程序是存放在磁盘的cpu要想执行程序的指令需要先将程序加载到内存中。 课本概念进程是被加载到内存运行的程序。 内核观点担当分配系统资源CPU时间内存的实体。 操作系统中有着大量的进程操作系统作为管理者管理的其实是大量进程相关的数据那么如何管理这些数据呢 先描述再组织 当二进制代码直接加载到内存时操作系统为了更好地管理加载的程序创建了描述该进程的数据结构。这样操作系统只用看这个数据结构不用管各种复杂多样的二进制代码并且将它们组织起来进行管理。
进程控制块PCB
这个数据结构叫PCBprocess control block进程信息被放在其中可以理解为进程属性的集合在linux的PCB是task_struct。
struct task_struct {volatile long state; /* -1 unrunnable, 0 runnable, 0 stopped */struct thread_info *thread_info;atomic_t usage;unsigned long flags; /* per process flags, defined below */unsigned long ptrace;int lock_depth; /* Lock depth */int prio, static_prio;struct list_head run_list;prio_array_t *array;//.....
}当有一个程序被加载到内存时操作系统会为该进程在内存中创建一个task_struct类型的对象并将该进程放入双链表等其他结构中。这样操作系统对进程的管理就变为操作系统对PCB的管理再变为操作系统对双链表等结构的增删查改等操作。
由此可以总结进程 内核数据结构PCB等 可执行程序代码数据
二.进程查看
通过指令查看进程
为了让进程能够一直运行方便观察写一个死循环程序让其每隔1秒钟打印一句话。
#include stdio.h
#include unistd.hint main()
{while(1){printf(Its a process.\n);sleep(1);}return 0;
}随后运行它此时该程序变成了一个进程
接着就可以用ps指令查看进程信息同时配合grep进行抓取
ps ajx | grep myprocess得到以下结果
可以看到系统中关于myprocess的进程一共有两个第一行是我们写的运行的程序第二行是grep命令进行抓取的进程。展示了各种信息PPID、PID、PGID等等这些就是PCB的一部分。 注意:task_struct是内核数据结构查看进程信息读取该数据必须要通过系统调用。
通过proc目录查看
proc是一个目录里面存放当前系统实时的 进程信息。 ls /proc
这里的数字就是进程的PID由于此时已经将myprocess进程停止此目录并没有找到名为167647的目录。 但是仔细看却有165058这是刚才myprocess的父进程ID即PPID通过指令可以知道该进程其实就是bash
再次运行myprocess并且通过指令得到其PID进入该文件夹可以发现进程的数据显式存在文件中。 进程的cwd和exe
查看该目录详细信息有两个文件很瞩目 cwd: Current Work Directory 指出该进程当前工作路径 exe: 指出该进程可执行程序的磁盘文件 修改程序添加一个fopen函数
#include stdio.h
#include unistd.hint main()
{FILE* fp fopen(1.txt, w); // 若不存在就创建while (1) {printf(Its a process.\n);sleep(1);}
}这恰好就是cwd链接的目录说明fopen使用了查看cwd的系统调用。 再看exe此时进程运行中直接删除其链接在磁盘中的文件发现进程没有终止停止进程再运行显然就会失败了。
运行程序本质就是将其从磁盘拷贝至内存中进程与其磁盘上对应程序没有直接关系。
获取进程pid和ppid
可以直接通过系统调用getpid()和getppid()得到当前进程的pid和ppid父进程的pid返回值为pid_t类型底层就是整数。
运行以下代码
#include stdio.h
#include unistd.hint main()
{while (1){printf(Its a process.\t);printf(pid:%d, ppid:%d\n,getpid(), getppid());sleep(1);}return 0;
}可以看到打印出当前进程的pid和ppid。
通过ps axj | head -1; ps axj | grep 184670进行验证当前进程是./myprocess且其父进程是bash。 通过fork()创建子进程
通过man指令查看fork()函数细节
fork()函数可以创建子进程创建成功后父子进程代码共享。 若成功创建子进程的pid返回给父进程0返回给子进程 若失败-1返回给父进程没有子进程。
代码共享可以通过以下代码得到验证
#include stdio.h
#include unistd.h
#include sys/types.hint main()
{printf(before\n);fork();printf(Hello, pid:%d\n, getpid());
}在fork()之前的代码只执行了一次之后的代码执行了两次这两次分别是两个进程执行的。 创建父子进程是为了做不同的事情一般是通过if/else来进行分流达到的这恰恰用到了fork()有两个返回值的特点下面的代码若是初见一定会迷惑。
#include stdio.h
#include unistd.h
#include sys/types.hint main()
{pid_t id fork();// id: 0-子进程 0-父进程if (id 0){while(1){printf(child process, pid: %d, ppid: %d, getpid(), getppid());sleep(1);}}else{while(1){printf(father process, pid: %d, ppid: %d, getpid(), getppid());sleep(1);}}
}
利用父子进程fork()返回值不同达到两个死循环都在不断执行的效果
通过指令查看确实两个进程是父子进程关系 下面来简要分析上面的情况具体细节会在之后进程地址空间部分详谈。 为什么两个死循环会同时执行❓ 上节讲过进程 内核数据结构PCB等 可执行程序代码数据。通过fork()创建子进程肯定也要给子进程创建一个独立的task_struct而其代码和数据指向了父进程接下来的代码和数据。子进程的大部分属性值也是由父进程拷贝而来修改前地址不会改变。 在CPU角度它不会管谁是父进程谁是子进程会在操作系统的管理下并发执行。在我们的视角下两个死循环同时执行了。 为什么fork()返回值如此设计❓ 父与子的关系是一对一或者一对多的。这样的关系导致父找子并不容易所以创建子进程成功后需要把子进程的pid返回给父进程方便父进程控制子进程。 而子找父是很容易的通过系统调用getppid()即可。 为什么fork()会返回两次值❓ fork()之前只有父进程即只有父进程才能调用fork()。fork()内部在return之前肯定已经将子进程创建成功又子进程和父进程在创建成功后代码共享那么子进程和父进程都会执行return这条语句这也就是为什么fork()会返回两次值。 同一个变量id怎么会既大于0又等于0❓ 进程之间具有独立性一个进程崩溃了不会影响另一个进程。这里的id是父子进程的共享数据若父子进程对共享数据有写操作这时操作系统会将该数据拷贝两份这就是写时拷贝。那么此时虽然这是同一个变量名但实际上表示的是不同的值那么id出现两种情况也就不足为奇了实际在底层的空间根本就不是一个。