1.创建子进程
由于fork()函数给子进程和父进程返回的pid不同,我们可以根据fork的返回值来对父进程和子进程区分开来
如果返回的id是<0,则创建子进程失败,如果id==0,则说明此进程是子进程,剩下的为父进程.可以写出代码加以验证.
#include<stdio.h> 2 #include<unistd.h> 3 #include<sys/types.h> 4 int main() 5 { 6 pid_t id=fork(); 7 if(id<0) 8 { 9 perror("fork fail"); 10 11 12 13 } 14 else if(id==0) 15 { 16 while(1) 17 { printf("我是子进程 ,pid= %d ppid= %d\n",getpid(),g etppid()); 18 sleep(1); 19 20 21 }} 22 else{ 23 while(1) 24 {printf("我是父进程,pid=%d ppid=%d\n",getpid(),getppid()); sleep(1); 26 27 28 29 30 31 } 32 33 34 35 36 37 38 39 40 }}
创建makefile,用于代码的编译
jingchengobj:jingcheng.c gcc -o jingchengobj jingcheng.c .PHONY:clean clean: rm -f jingchengobj
为了方便观察,我们复制ssh渠道
一份代码中两个while(1)同时进行,fork之后有两个不同的执行流
问题1:为什么给子进程返回0,给父进程返回子进程的pid?
因为对应一个父进程可以有好多个子进程,为了区分不同的子进程,来返回给父进程状态,所以给父进程返回自己的pid,而好多子进程只有一个父进程,不用区分,所以给子进程返回0;
问题2:为什么有两个返回值?
问题3:父子进程创建出来,哪个进程会先进行呢??
cpu在执行父进程,可能跑几ms,就会将父进程放到运行队列的尾巴上,可能是父进程需要向磁盘写数据,但是磁盘的队列正在执行别的,只能将父进程链接到cpu的运行队列尾巴上,等待磁盘队列空闲.所以不确谁的运行先后这个是由操作系统调度器确定的.
2.进程状态
先认识几个状态
1.创建:进程刚创建出来
2.运行:task_struct在cpu的运行队列中排队,就叫做运行态
3.阻塞:等待非cpu资源就绪,就称为阻塞状态.
阻塞解释:系统中存在着各种资源,网卡,磁盘,显卡等其他设备,其他设备也会有各自对应的运行队列,当有一个好几个经常分别要向磁盘上写不同的数据,此时的磁盘队列被占用,因为要别的经常写数据,所以会将该进程链接到cpu运行队列尾等待磁盘队列不被占用时,在将进程从运行队列尾移动到头,这个进程状态叫阻塞.
比方说我写了一个c程序,需要printf到显示屏上,我们需要等待显示队列,如果c程序写了一个scanf,我们不给输入,卡住等待键盘就绪(非cpu资源就绪)
4.挂起:
5.挂起阻塞:和cpu没关系,此时在等待非cpu资源同时,且内存不足,需要将有些进程换到swap分区去,这种进程状态叫做挂起阻塞
当os频繁的将进程的数据和代码换入换出,会影响效率,进程太多,os压力大,宕机了.
看看Linux内核源代码怎么说
为了弄明白正在运行的进程是什么意思,我们需要知道进程的不同状态。一个进程可以有几个状态(在
Linux内核里,进程有时候也叫做任务)。
下面的状态在kernel源代码里定义
static const char * const task_state_array[] = { "R (running)", /* 0 */ "S (sleeping)", /* 1 */ "D (disk sleep)", /* 2 */ "T (stopped)", /* 4 */ "t (tracing stop)", /* 8 */ "X (dead)", /* 16 */ "Z (zombie)", /* 32 */ };
S睡眠状态: 等待某种资源(可中断睡眠),阻塞状态
代码演示S状态
while(1) { printf("进程正在执行,该进程的pid=%d\n",getpid()); sleep(1); }
利用命令行局部变量进行循环执行ps命令以便查看其进程状态:
while :; do ps axj | head -1 && ps axj | grep jingchengobj | grep -v grep;sleep 1 ; echo "-----------------------"; done
此时进程在向显示屏上打印东西,等待非cpu资源就绪(显示屏)
此时可以发现S状态后面有个+号,有加号说明是在前台进程,会占用bash的命令行,当我们在命令行输入其他指令时会无效,当然我们可以将其能变成后台进程,就不会占用我们的命令行了。
前台进程改后台进程
./jingchengobj &
S状态可以通过kill指令来中断
R状态: 并不意味着进程一定在运行中,它表明进程要么是在运行中要么在运行队列里
代码演示R状态
while(1) { // printf("进程正在执行,该进程的pid=%d\n",getpid()); // sleep(1); }
当注释掉while循环里面的,就不会占用其他非cpu。
D磁盘休眠状态(Disk sleep)有时候也叫不可中断睡眠状态(uninterruptible sleep),在这个状态的进程通常会等待IO的结束。
T停止状态(stopped): 可以通过发送 SIGSTOP 信号给进程来停止(T)进程。这个被暂停的进程可以通过发送 SIGCONT 信号让进程继续运行。
kill -l //指令是展示kill指令所有的选项 • 1
停止状态和睡眠状态的区别是,停止状态没有等待其他非cpu资源。
当我们运行该程序时,给个kill 19号信号来暂停进程
此时kill给个18号命令继续进程
t进程状态还有一种就是在gdb调试中,在某一行打上断点,然后运行该程序,遇到断点,停止程序,也是t状态
-g生成debug模式
X死亡状态(dead):这个状态只是一个返回状态,你不会在任务列表里看到这个状态
当一个进程结束之后,由于操作系统此时要处理好多个退出进程,还来不及来处理的这个退出的进程处于死亡状态(瞬时性特别强)不好演示
z状态:僵尸状态:进程已死,但是要将进程状态给父进程,还没给父进程,此时不允许被os释放,就叫做僵尸状态
举个例子,当使用fork创建子进程,设置子进程睡眠一会,然后退出,父进程必须等待子进程将自己的状态给父进程,此时子进程所处的状态叫做僵尸状态。
代码演示:
#include<stdio.h> #include<unistd.h> #include<sys/types.h> #include<stdlib.h> int main() { pid_t id=fork(); if(id<0) { perror("fork fail"); } else if(id==0) { while(1) { printf("我是子进程 ,pid= %d ppid= %d\n",getpid(),g etppid()); sleep(3); break; } exit(0);} else{ while(1) {printf("我是父进程,pid=%d ppid=%d\n",getpid(),getppid() ); sleep(1); } }}