Linux进程初步理解

进程理解

当一个程序没有运行的时候保存在磁盘里面,如果要运行就要加载到内存里面,内存里面有程序的代码和数据。内存里面这些代码和数据就是进程对应的代码和数据。但是这些代码和数据不是进程。

操作系统管理程序运行实际上是在管理进程,操作系统在内存中也占有一部分空间,用来管理进程。我们开机就是在把磁盘中的操作系统加载到内存里面。

课本上讲的是,进程是程序的一个执行实例,正在执行的程序
内核观点看,进程是担当系统分配资源(CPU时间,内存)的的实体

我们自己写的程序一旦运行起来,bash就会创建对应的进程,此时bash就是我们进程的父进程。
bash:shell外壳的一种,帮助我们与操作系统打交道。

如何管理进程

进程信息被放在一个叫做进程控制块的一个数据结构(struct PCB)中,可以理解为进程属性的集合,其中包括了内存指针,指向程序所在的内存地址。
struct PCB是一个链表,其中有struct PCB* next用来指向下一个结构体。操作系统对进程的管理其实就是对链表的管理。一个进程对应一个PCB

操作系统不直接管理代码和数据,而是管理进程,代码和数据只是进程的一小部分。

PCB是操作系统上的概念,具体到Linux中,这个数据结构叫做struct task_struct,这个数据结构叫做Linux进程控制块。
我们Linux中的指令也是程序,我们每执行一个指令都要加载到内存并创建进程,执行完后终止。
task_struct是Linux内核的一种数据结构,它会被装载到RAM(内存)里并且包含着进程的信息。

task_ struct内容分类:

标示符: 描述本进程的唯一标示符,用来区别其他进程。
状态: 任务状态,退出代码,退出信号等。
优先级: 相对于其他进程的优先级。
程序计数器: 程序中即将被执行的下一条指令的地址。
内存指针: 包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针
上下文数据: 进程执行时处理器的寄存器中的数据[休学例子,要加图CPU,寄存器。
I/O状态信息: 包括显示的I/O请求,分配给进程的I/O设备和被进程使用的文件列表。
记账信息: 可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等。
其他信息

进程的相关操作

ps axj:查看当前所有进程
ps axj | head -1 && ps axj | grep myprocess:&&符号表示连续执行两条命令,执行完"&&"前面的再执行后面的

获取进程id

每个进程都有唯一的进程标识符,叫做PID,PID是结构体中的一个无符号整型
用户无法自己获得进程的PID,所以操作系统提供了系统调用函数叫做get pid无参数,返回值为pid_t(整形)
可以通过man指令查找getpid所在的头文件,可以发现要包含两个头文件。

#include <stdio.h>  
#include <sys/types.h>  
#include <unistd.h>  
int main()  
{  
	pid_t id = getpid();  
	while(1)  
	{  
		printf("I am a proces, pid:%d\n", id);  
		sleep(1);                                                                                                                                                
	}                                                                
	return 0;                                                        
} 

在这里插入图片描述
在这里插入图片描述

getppid():获得当前进程的父进程pid
当我们把父进程的id也打印出来看的时候,我们发现不论我们重新打开多少次,子进程的id值在变,但是父进程的id值不变。找到ppid的值对于的名称发现,叫做bash,就是我们之前提到的外壳程序,Linux默认的shell。
在这里插入图片描述

创建子进程

fork()
fork()函数是系统给我们提供的系统调用,和getpid,getppid是一样的,fork函数可以创建一个子进程。无参数,返回值为pid_t,子进程的返回值是0,父进程的返回值为其子进程的pid,返回值为负数则创建失败。
fork以后的代码父子共享,主函数为父进程,fork创建的为子进程。

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{                                                                           
	printf("only me ,pid:%d, ppid:%d\n",getpid(),getppid());
	sleep(3);
	fork();
	printf("mypid:%d,myppid:%d\n",getpid(),getppid());
	sleep(5);
	return 0;
}

在这里插入图片描述

fork会返回两个id值,这是为什么呢?
有两个返回值是因为创建了子进程,我们在fork返回值之前就已经进行了代码共享,也就是在return之前,fork函数已经创建了子进程,后续代码,父子共享,所以有两个返回值就合情合理了。

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{                                                                           
	printf("only me ,pid:%d, ppid:%d\n",getpid(),getppid());
	sleep(3);
	pid_t id = fork();
	if(id == -1) return 1;
	if(id == 0)
	{
		while(1)
		{
			printf("id: %d,mypid:%d,myppid:%d\n",id,getpid(),getppid());
			sleep(1);
		}
	}
	else
	{
		 while(1)
		{
			printf("id: %d,mypid:%d,myppid:%d\n",id,getpid(),getppid());    
			sleep(2);
		}
	}
	return 0;
}

在这里插入图片描述
需要知道的是,当杀死父进程后子进程并不会消失。

ctrl + c:结束当前进程
kill -9 + 进程pid:结束pid对应进程

根目录下有一个文件为/proc,该文件夹包含了所有的进程信息,每形成一个进程该目录下就会形成以该进程pid命名的文件夹在这里插入图片描述

exe为当前进程的可执行程序。当前进程就是exe指定的,取绝对路径下的这个程序加载形成的。
如果我们把一个程序对应的可执行程序删掉,比如上面的exe对应的myprocess.exe删掉,可以发现,我们进程还在运行。这是为什么呢?
因为进程已经加载到了内存当中,我们代码了比较小,所以全部都在内存里面,所以会一直运行,如果是一个大型的游戏,这样做,那么跑着跑着就崩溃掉了。
进程的pcb会记录自己对应的可执行程序的路径。记录的就只是这个路径。

上面黄色的框是cwd,就是current work dir,进程当前的工作路径,每个进程启动的时候会记录下自己在哪个路径下启动,路径就存在cwd下。
我们曾经使用的 fopen 函数,创建一个文件的时候,文件创建的路径默认就是通过这个cwd存的工作路径。如果我们通过函数把进程工作路径修改掉,那么在创建的文件存储的位置就是修改后的路径。
chdir("dir"):工作路径修改函数,dir处写修改后的路径即可。

两个额外的知识点:
1.每个进程都有自己独立的缓冲区,如果一个进程打印的时候没有加“ \n ”,但是另一个进程加了,可能就看不到原来进程的打印,直到刷新对应进程的缓冲区。
2.父进程和子进程的执行是完全独立的,相互之间没有任何时序上的关系,所以不知道创建子进程后谁先运行,完全看操作系统的调度。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值