文章目录
在 Linux下 ./binaryfile 运行一个程序或者在 Windows下双击运行一个程序时,程序就变成了一个进程
根据冯诺依曼体系结构,程序运行起来后,程序的代码和数据就会被操作系统加载到内存,但一个程序仅仅被加载到内存,并不代表该程序就是进程
将这里的内存比作学校,程序比作人,进程比作学校的学生,如果一个人处在学校中,并不能说明这个人就是学校的人,如:学校的保安,食堂阿姨等这些并不是学生,只有那些被学校管理起来的,并且信息在学校的学生档案中的人才能被称作学生,所以只有程序被操作系统管理起来,并且程序的代码和数据的相关信息被操作系统记录了,这个程序才被称做进程
在学校中,当学生很多时,我们需要对学生进行管理,在操作系统中,当很多程序运行起来时,加载到内存中的代码便会很多,操作系统便需要对这些代码和数据进行管理
根据我们对管理进行的建模,可以知道操作系统对加载到内存中代码和数据的管理方式:先描述,在组织
先描述:为了管理程序运行后加载到内存中的代码和数据,操作系统采用了一个结构体对象 pcb,用来描述加载到内存中的代码和数据的相关属性,其中 pcb 中有一个内存指针,用来指向内存中的代码和数据
在国内的教材中 pcb(process control block) 统一称作进程,在国外有的叫做任务,Linux 下的 pcb 称做 task_struck

进程 = 内核中关于程序的相关结构体 + 程序的代码和数据
在组织:每一个加载到内存中的代码和数据操作系统都会为其创建一个 pcb 对象,因此我们可以在 pcb 对象中在加上 pcb 结构体指针,构成数据结构中的链表
Linux 下采用双链表的形式组织
当操作系统想新增一个进程时(启动一个程序),只需要创建一个 pcb,然后录入该程序的属性到 pcb 中,然后在链表中插入该 pcb
当操作系统想杀掉一个进程时(结束程序的运行),只需遍历链表,找到该进程的 pcb,然后通过内存指针释放 pcb 指向的内存中的代码和数据,在再链表中释放该 pcb 结点即可
当操作系统想查看一个进程的运行状态时(查看程序运行是否正常),只需要遍历链表,找到该进程的 pcb,然后查看状态信息即可
当操作系统想找到一个优先级别较高的进程执行时(让 CPU 运行指定程序),只需要遍历链表,找到该进程的 pcb,然后通过内存指针找到 pcb 指向的代码和数据,让 CPU 执行即可
通过先描述,再组织的方式,操作系统对进程的管理被完全的转换成了对 pcb 结构体组成的链表数据结构的增删查改
运行的可执行程序都要被操作系统转换为进程来调度以便完成特定的任务,因此当我们运行一个程序时,就称作 创建了一个进程
仅个人当前理解:软件其实就是一个在磁盘上的二进制文件,当软件运行起来后,便需要加载到内存,所以操作系统对进程的管理,便是对软件资源的管理
进程 = 内核中关于进程的相关数据结构 + 进程的代码和数据
为了操作系统管理进程,需要描述出进程的共同属性,从而产生了结构体 pcb
task_struct 的字段
pcb 和可执行程序的文件属性关系不大
如何证明程序运行起来,便成为了一个进程?
当我们创建一个进程时,操作系统就会在 /proc 目录下创建一个该进程 pid 为名的目录,该目录下存在该进程的属性(文件路径等),当进程终止时,/proc 目录中也会删除该进程 pid 为名的目录
预备知识1:
ps ajx:查看系统中所有的进程
ls /proc:查看系统中的所有进程,其中目录名为数字的表示进程的标识符 pid
[starrycat@iZ2vcer6gtjgqa43cdpeeaZ code]$ ps ajx
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
0 1 1 1 ? -1 Ss 0 3:57 /usr/lib/systemd/systemd --system --deserialize 17
0 2 0 0 ? -1 S 0 0:00 [kthreadd]
2 3 0 0 ? -1 S 0 0:08 [ksoftirqd/0]
2 5 0 0 ? -1 S< 0 0:00 [kworker/0:0H]
2 7 0 0 ? -1 S 0 0:00 [migration/0]
...
[starrycat@iZ2vcer6gtjgqa43cdpeeaZ code]$ ls /proc
1 14 21134 25405 28 350 47 539 6770 854 crypto interrupts kpagecount mtrr softirqs uptime
10 1523 21252 25512 280 36 49 557 6771 9 devices iomem kpageflags net stat version
101 15631 21262 26 29 365 5 587 7 acpi diskstats ioports loadavg pagetypeinfo swaps vmallocinfo
1019 16 22 27 296 37 50 598 787 buddyinfo dma irq locks partitions sys vmstat
...
预备知识2:
pid_t getpid(void):返回调用该函数的进程标识符 pid,需要包含头文件 <sys/types.h> 和 <unistd.h>
pid_t:有符号整形的 typedef
如果想了解更多关于 getpid 函数的内容,通过 man 2 getpid 即可查看
接下来通过代码证明程序运行起来,便成为了一个进程
在 process.c 中写好如下代码:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
while(1)
{
printf("我是一个进程了,我的 pid 是:%d\n", getpid());
sleep(1);
}
return 0;
}
证明1:
打开新的窗口,通过命令查看发现,运行的程序 process 可以查到他的进程标识符 pid
其中 ps ajx | head -1 表示获取进程的第一行属性字段(便于观看)
&& 表示当前一条指令执行成功后执行后一条指令
ps ajx | grep process | grep -v grep 表示筛选出 process 的进程,并且去除掉 grep 这个进程

过程2:
用 ctrl + c 终止程序后,便查找不到该进程了

终上所述,程序运行起来后就变成了进程
在进程中存在着父子进程的概念
pid_t getppid(void):返回调用该函数的进程的父进程标识符 pid,需要包含头文件 <sys/types.h> 和 <unistd.h>
pid_t:有符号整形的 typedef
在 process.c 中写好如下代码:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
while(1)
{
printf("我是一个进程了,我的 pid 是:%d,我的 ppid 是:%d\n", getpid(), getppid());
sleep(1);
}
return 0;
不断的执行 process 会发现子进程的进程标识符一直在增加,但是父进程的进程标识符 6771 一直不变,通过 ps 命令后可以发现 6771 这个进程标识符就是命令行解释器 bash,即命令行执行的程序都是通过创建子进程的方式去执行的,是为了避免执行的程序挂了,导致影响 bash 自己
命令行解释器 bash 其实就是在 /bin/bash 的一个二进制可执行程序,因此 bash 也是一个进程

命令行启动的所有程序,都是 bash 创建的子进程,为了防止子进程挂了,导致影响自己
命令行是如何创建子进程的呢?
pid_t fork(void):如果创建子进程成功,则给调用该函数的父进程返回子进程的 pid,给子进程返回 0,如果失败则返回 -1,需要包含头文件 <sys/types.h> 和 <unistd.h>
在 process.c 中写好如下代码:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
pid_t ret = fork();
if (ret == 0)
{
// 执行子进程
while (1)
{
printf("我是子进程,我的 pid 是:%d,我的 ppid 是:%d\n", getpid(), getppid());
sleep(1);
}
}
else if (ret > 0)
{
// 执行父进程
while (1)
{
printf("我是父进程,我的 pid 是:%d,我的 ppid 是:%d\n", getpid(), getppid());
sleep(1);
}
}
else
{}
return 0;
}
可以通过 fork 系统调用来创建子进程

fork 创建子进程的过程:操作系统为了可以管理子进程,会根据父进程的 pcb 创建子进程的 pcb,并且子进程的 pcb 会和父进程的 pcb 指向同一块代码和数据

虽然父进程创建子进程后,子进程指向父进程的代码和数据,但是不同的进程在运行时是独立的,父子进程在运行时也是独立的
kill -9 pid 功能:杀掉进程
再次运行 process 之后,在另一个窗口中输入 kill -9 父进程 pid,此时子进程任然可以正常运行

父子进程是如何做到独立的呢?
为什么一个函数会有两个返回值?
因为一个函数在执行 return 之前,函数的主题功能已经完成了,对于 fork 函数,在 return 之前已经创建好子进程了,此时便有父子进程两个执行流,于是父子进程都会执行 return 语句,也就产生了两个返回值的现象
在MRIRuby中我可以这样做:deftransferinternal_server=self.init_serverpid=forkdointernal_server.runend#Maketheserverprocessrunindependently.Process.detach(pid)internal_client=self.init_client#Dootherstuffwithconnectingtointernal_server...internal_client.post('somedata')ensure#KillserverProcess.kill('KILL',
我正在编写一个gem,我必须在其中fork两个启动两个webrick服务器的进程。我想通过基类的类方法启动这个服务器,因为应该只有这两个服务器在运行,而不是多个。在运行时,我想调用这两个服务器上的一些方法来更改变量。我的问题是,我无法通过基类的类方法访问fork的实例变量。此外,我不能在我的基类中使用线程,因为在幕后我正在使用另一个不是线程安全的库。所以我必须将每个服务器派生到它自己的进程。我用类变量试过了,比如@@server。但是当我试图通过基类访问这个变量时,它是nil。我读到在Ruby中不可能在分支之间共享类变量,对吗?那么,还有其他解决办法吗?我考虑过使用单例,但我不确定这是
我正在尝试使用以下代码通过将ffmpeg实用程序作为子进程运行并获取其输出并解析它来确定视频分辨率:IO.popen'ffmpeg-i'+path_to_filedo|ffmpegIO|#myparsegoeshereend...但是ffmpeg输出仍然连接到标准输出并且ffmepgIO.readlines是空的。ffmpeg实用程序是否需要一些特殊处理?或者还有其他方法可以获得ffmpeg输出吗?我在WinXP和FedoraLinux下测试了这段代码-结果是一样的。 最佳答案 要跟进mouviciel的评论,您需要使用类似pope
我目前正在用Ruby编写一个项目,它使用ActiveRecordgem进行数据库交互,我正在尝试使用ActiveRecord::Base.logger记录所有数据库事件具有以下代码的属性ActiveRecord::Base.logger=Logger.new(File.open('logs/database.log','a'))这适用于迁移等(出于某种原因似乎需要启用日志记录,因为它在禁用时会出现NilClass错误)但是当我尝试运行包含调用ActiveRecord对象的线程守护程序的项目时脚本失败并出现以下错误/System/Library/Frameworks/Ruby.frame
我想从rubyrake脚本运行一个可执行文件,比如foo.exe我希望将foo.exe的STDOUT和STDERR输出直接写入我正在运行rake任务的控制台.当进程完成时,我想将退出代码捕获到一个变量中。我如何实现这一目标?我一直在玩backticks、process.spawn、system但我无法获得我想要的所有行为,只有部分更新:我在Windows上,在标准命令提示符下,而不是cygwin 最佳答案 system获取您想要的STDOUT行为。它还返回true作为零退出代码,这可能很有用。$?填充了有关最后一次system调
A/ctohttp://wiki.nginx.org/CoreModule#usermaster进程曾经以root用户运行,是否可以以不同的用户运行nginxmaster进程? 最佳答案 只需以非root身份运行init脚本(即/etc/init.d/nginxstart),就可以用不同的用户运行nginxmaster进程。如果这真的是你想要做的,你将需要确保日志和pid目录(通常是/var/log/nginx&/var/run/nginx.pid)对该用户是可写的,并且您所有的listen调用都是针对大于1024的端口(因为绑定(
我有一个应用程序正在从Ruby迁移到JRuby(由于需要通过Java提供更好的Web服务安全支持)。我使用的gem之一是daemons创建后台作业。问题在于它使用fork+exec来创建后台进程,但这对JRuby来说是禁忌。那么-是否有用于创建后台作业的替代gem/wrapper?我目前的想法是只从shell脚本调用rake并让rake任务永远运行......提前致谢,克里斯。更新我们目前正在使用几个与Java线程相关的包装器,即https://github.com/jmettraux/rufus-scheduler和https://github.com/philostler/acts
在尝试实现应用auto_orient的过程之后!对于我的图片,我收到此错误:ArgumentError(noimagesinthisimagelist):app/uploaders/image_uploader.rb:36:in`fix_exif_rotation'app/controllers/posts_controller.rb:12:in`create'Carrierwave在没有进程的情况下工作正常,但在添加进程后尝试上传图像时抛出错误。流程如下:process:fix_exif_rotationdeffix_exif_rotationmanipulate!do|image|
我有一个将某些事件写入队列的Rails3应用。现在我想在服务器上创建一个服务,每x秒轮询一次队列,并按计划执行其他任务。除了创建ruby脚本并通过cron作业运行它之外,还有其他稳定的替代方案吗? 最佳答案 尽管启动基于Rails的持久任务是一种选择,但您可能希望查看更有序的系统,例如delayed_job或Starling管理您的工作量。我建议不要在cron中运行某些东西,因为启动整个Rails堆栈的开销可能很大。每隔几秒运行一次它是不切实际的,因为Rails上的启动时间通常为5-15秒,具体取决于您的硬件。不过,每天这样做几
已检查ActiveRecord、DataMapper、Sequel:有些使用全局变量(静态变量)有些需要在使用模型加载源文件之前打开数据库连接。在使用不同数据库的sinatra应用程序中使用哪种ORM更好。 最佳答案 DataMapper专为多数据库使用而设计。你可以通过像DataMapper.setup(:repository_one,"mysql://localhost/my_db_name")这样的方式设置多个存储库。DataMapper随后会跟踪所有已在哈希中设置的存储库,您可以引用该哈希并将其用于范围界定:DataMapp