目录
[1. 查看进程](#1. 查看进程)
[2. 通过系统调用创建进程-fork初识](#2. 通过系统调用创建进程-fork初识)
前言
你有没有想过在使用Linux操作系统时,后台运行的程序是如何管理的?在Linux中,进程是一个非常重要的概念。本文将介绍如何查看当前运行的进程,并且讨论如何使用fork创建新的进程。通过了解这些内容,你将更好地理解Linux系统中的进程管理。
1. 查看进程
在理解进程状态之前,我们要先学会查看进程,进程的信息可以通过 /proc 系统文件夹查看
bash
ls /proc
可以看到有很多以数字命名的文件夹,这些数字其实就是进程的PID:
进行实验的代码:
cpp
#include <stdio.h>
#include <unistd.h>
#include<sys/types.h>
int main()
{
pid_t id = getpid();
pid_t fid = getppid();
while(1)
{
printf("Hello world! pid: %d ppid: %d\n",id,fid);
sleep(1);
}
return 0;
}
指令:
bash
ls /proc/【PID】 -ld
进一步查看进程详细信息:
bash
ls /proc/【PID】 -l
里边的文件都是进程的属性,这里有两个显眼的属性cwd和exe
- exe:进程可执行程序在磁盘中对应的位置
- cwd(current working directory):进程的当前工作目录
进程在运行时,它的可执行程序会被加载到内存当中,在进程运行的情况下我们依然可以删除它的可执行程序 。删除之后再次查看运行中进程属性就会发现exe属性被标红且高亮闪烁
在C语言中我们对文件进行操作:
cpp
fopen("file.txt" , " w");
这里也解释了,在没有这个文件时为什么会默认在程序的当前目录下创建。因为那是进程的工作目录;我们也可以通过修改进程工作目录的方式,改变创建文件的默认路径。
我们可以调用chdir这个系统调用接口来改变进程的工作目录,以下时chdir的相关说明文档:
使用时直接指定新路径即可,成功返回0,失败返回-1.
实验代码:
cpp
int main()
{
printf("self pid: %d\n", getpid());
chdir("/home/test");
FILE *fp = fopen("test.txt", "w");
if(fp == NULL) return 1;
fclose(fp);
printf("新建文件完成\n");
sleep(50); //50秒后结束运行,预留充足实际去查询进程
}
在程序运行结束之前,查看进程的属性时发现,进程的工作目录被修改为了:/home/test
程序运行结束之后发现test.txt文件出现在/home/test目录下。
2. 通过系统调用创建进程-fork初识
查看fork操作手册:
bash
man fork
退出时输入q即可
man指令前边文章提到过,查看命令手册页的命令,在面对一个新的指令或者未知的指令我们都可以使用man查看,面对一些系统调用接口时也可以使用。
在查看时说明文档或许很长,我们如果是需要快速上手使用,可以主要看3部分:
- synopsis (所属头文件、返回值类型)
- description(接口基本功能)
- return value (具体返回值)
fork基本信息:
- 作用:创建一个子进程,
- 返回类型:pid_t
- 返回值:创建成功返回子进程的PID给父进程,返回0给子进程,失败返回-1给父进程
我们可以实验一下:
实验代码:
cpp
#include <stdio.h>
#include <unistd.h>
#include<sys/types.h>
int main()
{
printf("before fork: I am a prcess, pid: %d, ppid: %d\n", getpid(), getppid());
pid_t id = fork();
printf("after fork: I am a prcess, pid: %d, ppid: %d, return id: %d\n", getpid(), getppid(), id);
sleep(2);
return 0;
}
运行结果:
fork之后,运行了两次,原进程和子进程,fork之后代码共享,子进程会继承父进程大部分属性,子进程从fork之后执行。
原进程的父进程12333是谁? 其实是bash命令行解释器。
指令查看:
bash
ps ajx | grep pid
- 为什么父进程返回子进程PID,子进程返回0 ?
父进程与子进程的关系是1:n的关系,子进程有有唯一的父进程,而父进程为了辨别每个子进程就需要通过进程唯一的标识PID。
- fork函数为什么会返回两次?
fork函数功能是创建一个新的进程(子进程),子进程会继承父进程大部分属性,父进程调用fork函数执行到return时,子进程已经被创建,此时子进程继承了父进程状态,也是执行到return位置,父进程return一次,子进程return一次,所以它会执行两次。
- id一个变量怎么可能同时大于0和等于0?
进程在设计之初就被要求相互独立,互不影响,下面是一个测试样例:
父进程和子进程一起执行,使用kill指令发送信号杀死父进程,子进程不受影响依然可以运行。
测试使用的指令:
bash
while :; do ps ajx | head -1 && ps ajx | grep myprocess | grep -v grep; sleep 1; done
杀死进程:
bash
kill -9 PID
主要在进程运行界面杀死进程不能用ctrl c(会同时终止两个进程)。
使用的代码
cpp
#include <stdio.h>
#include <unistd.h>
#include<sys/types.h>
int main()
{
printf("before fork: I am a prcess, pid: %d, ppid: %d\n", getpid(), getppid());
sleep(5);
printf("开始创建进程\n");
sleep(1);
pid_t id = fork();
if(id < 0) return 1;
else if(id == 0)
{
// 子进程
while(1){
printf("after fork, 子进程: I am a prcess, pid: %d, ppid: %d, return id: %d\n", getpid(), getppid(), id);
sleep(1);
}
}
else{
// 父进程
while(1){
printf("after fork, 父进程: I am a prcess, pid: %d, ppid: %d, return id: %d\n", getpid(), getppid(), id);
sleep(1);
}
}
sleep(2);
return 0;
}
操作系统在设计进程之初就必须要考虑到进程相互独立这一点。
前边我们提到fork之后代码和数据共享,如果某一个进程修改了数据会不会对另一个进程造成影响?
不会,操作系统为了使进程互不影响,某个进程(父进程或子进程)在修改数据时,操作系统会进行**写时拷贝,**进程修改数据时OS会单独开一块空间将数据复制一份交给(修改的数据)进程,修改数据也就是对新拷贝的数据进行修改。Linux中可以用同一个变量名,表示不同的内存空间。
总结
进程是Linux操作系统中一个非常重要的概念,对于系统的管理和性能至关重要。通过本文的介绍,希望你对于Linux进程有了更深入的了解。在实际应用中,进程管理涉及到更多的细节和技巧。以上便是本文全部内容,希望对你有所帮助,感谢阅读!