01-系统编程
一、程序和进程的区别:
window系统:
1、程序存储在硬盘中,文件格式为.exe后缀,静态的
2、进程运行在内存中,动态的
Linux系统
1、程序存储在硬盘中,文件格式为.ELF(可执行的链接文件)后缀,静态的
2、进程运行在内存中,动态的
二、进程的内存划分:
1、PM实际内存,VM虚拟内存,每一个程序加载都会分配出一个虚拟内存,虚拟内存有:内核,栈,堆,数据段(.bss,.data,.rodata),代码段(.text,.init)。且每个进程的虚拟内存都是相互独立的。
2、存储空间,定义,生命周期,作用域,特点
局部变量,栈区,定义{}里面,{}里有效,{}里有效,自动分配自动释放
全局变量,数据段中,所有函数之外定义,程序死就死,整个程序有效,程序中所有函数可见
堆空间变量,堆空间中,free之前都有效,free之前都有效,手动分配手动释放
静态变量,数据段中,用static修饰,整个程序有效,静态局部变量(当前函数中),全局变量(在当前文件中)
常量、数据段,用const修饰,整个程序有效,整个程序有效,常量无法修改。
三、进程命令
1、ps 查看当前系统的进程
2、ps -e 查看当前系统的所有进程
3、 top 查看进程状态,相当于window的任务管理器
4、kill 发送一个信号给进程
有以下可选
HUP 1 终端挂断
INT 2 中断(同 Ctrl + C)
QUIT 3 退出(同 Ctrl + \)
KILL 9 强制终止
TERM 15 终止
CONT 18 继续(与STOP相反,fg/bg命令)
STOP 19 暂停(同 Ctrl + Z)
5、pstree 查看进程关系图
四、进程状态(重点)
1、进程状态:
标准图如下:
就绪态: 时间片用完,睡眠,暂停的进程都会进入到就绪态,等待CPU的调度。
执行态: CPU调度,获得CPU的使用权,得到时间片。 处理当前进程的数据
睡眠态: 调用sleep函数或者 scanf..... 阻塞函数
暂停态: 收到STOP信号后进入暂停态
僵尸态: 进程死亡后就进入僵尸态,等待父亲收尸(回收资源) 知道儿子的死因
死亡态: 进程死亡后并且父进程回收了资源!
2、什么是时间片?
百度可知,时间片也可成为“量子”,“处理片”即分时操作系统分配给每个正在运行进程微观上的一段CPU时间。
五、fork进程创建
1、多进程有什么用?当然是为了实现多任务的执行,提高效率!
1、fork函数:pid_t fork(void);
头文件:#include <unistd.h>
参数:void
返回值:父进程返回子进程的pid
子进程返回 0
失败返回 -1
2、父子进程的资源
父子进程的内存是相互独立的,但他们的地址是一样的,创建之初他们的整个内存空间(栈,堆,数据段,代码段,标准IO等等)是一样的。
但他们的进程号PID是不一样的,因为PID是唯一的。
父子进程的数据交换:
例子如下:
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
int main()
{
// 1.创建一个文件
int fd = open("my.txt", O_RDWR | O_CREAT, 0777);
// 2.创建一个进程
pid_t pid = fork();
if (pid == 0) // 子进程
{
while (1)
{
// 读取父进程发送的数据
char buf[1024];
lseek(fd, 0, SEEK_SET); // 把光标偏移到文件头
int size = read(fd, buf, sizeof(buf));
if (size > 0)
{
printf("子进程接收到的数据:%s\n", buf);
}
sleep(2);
}
}
if (pid > 0) // 父进程
{
while (1)
{
// 向子进程发送数据
char buf[1024];
printf("请输入要发送的数据:");
scanf("%s", buf);
write(fd, buf, strlen(buf)); // 写入数据到文件中
}
}
}
3、孤儿进程(没有父进程,即父进程先死,此时子进程会托管给系统进程(这个进程是没有父进程的))
init进程的产生过程;
孤儿进程的例子:
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
int main()
{
for (int i = 0; i < 10; i++) // 循环创建 10 个进程
{
// 创建一个进程
pid_t pid = fork();
if (pid == 0)
{
printf("子进程 %d\n", getpid());
getchar(); // 阻塞等待,让子进程不死亡
}
}
printf("父进程20S后死亡\n");
sleep(20);
printf("父进程死亡\n");
return 0;
}
4、僵尸进程(父进程没有及时回收子进程的资源导致,即父进程不知道子进程的死因,导致子进程一直占用系统的内存资源)
例子:
#include <stdio.h>
#include <unistd.h>
int main()
{
for (int i = 0; i < 10; i++)
{
// 创建一个进程
pid_t pid = fork();
if (pid == 0) // 子进程
{
printf("子进程死亡 %d\n", getpid());
return 0; // 在子进程的内部return 结束的是子进程
}
}
printf("父进程正在运行,按任意键结束\n");
getchar();
}
所以为了防止僵尸进程的产生,父进程wait回收子进程的资源。
六、wait进程资源的回收
头文件: #include <sys/wait.h>
wait函数:pid_t wait(int *_Nullable wstatus); //等待任意一个进程退出
wstatus:进程退出状态
pid_t waitpid(pid_t pid, int *_Nullable wstatus, int options); //等待指定的pid进程退出
pid:等待的进程pid
wstatus: 进程退出状态
options: 等待属性,默认为 0
例子:等待任意一个进程退出
wait(NULL)
1、获取子进程的死亡原因及返回值。
例子:
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
int main()
{
// 创建一个子进程
pid_t pid = fork();
if (pid == 0)
{
int i = 0;
while (1)
{
printf("%d 子进程正在运行 %d\n", getpid(), i++);
sleep(1);
if (i == 10)
{
printf("子进程结束\n");
return 123; // 正常退出,传递给父进程
}
}
}
printf("父进程运行,等待子进程结束回收资源\n");
int wstat = 0; // 退出状态码
wait(&wstat); // 等待任意一个子进程结束,一直等待,直到子进程结束为止
// 判断进程的死亡原因
if (WIFEXITED(wstat))
{
printf("子进程正常退出\n");
// 获取子进程的退出值 return 值
printf("子进程的退出值是 %d\n", WEXITSTATUS(wstat));
}
}