系统编程01-进程(fork、wait、exit、vfork、exec)

目录

一、linux系统编程学习大纲(进程重理论 线程重实操)

二、进程的概念

 1.什么是程序?什么是进程?

2.在linux下,如何开启一个新的进程?

3.当进程开启之后,系统会为进程分配什么资源?

4.关于进程的命令

三、进程的状态(填空选择题)

1.进程从诞生到死亡会经历哪些状态?

2.什么是僵尸态?

3.总结:

四、进程的函数接口。

1.如何在一个正在运行的进程中产生一个子进程?  -> fork()  -> man 2 fork

2.在一个进程中查看自己的PID号以及父进程的PID号。

五、僵尸进程(重难点)与孤儿进程

1.僵尸进程的测试程序

2.孤儿进程的测试程序

总结: 孤儿进程是在失去父进程的时候马上寻找继父,祖先进程init就会成为该子进程的父进程,回收该子进程的资源。 僵尸进程父进程还在,子进程先退出;但是子进程资源没有被回收,危险。 四、解决僵尸态问题

1.父进程还在,主动回收资源。  -> wait()  -> man 2 wait

2.父进程退出后,子进程会寻找继父帮自己回收资源。

3.父进程比子进程先退出,此时子进程就变成了孤儿进程,孤儿进程会马上找继父,孤儿进程退出之后由继父回收资源。

 五、进程的退出。  -> exit() /_Exit() / _exit()

1.exit()

2.Exit() / _exit()--------不会清洗缓冲区,直接退出  -> man 2 _exit

说明: wait(&state)的state的值要和exit(status)一起使用,才可以查看state的状态值。    3.缓冲区问题。

4.退出状态。  -> 父进程监听子进程的退出状态,如果子进程正常退出在,则输出一个ok,如果子进程异常退出,则输出一个error。

5.exit()与return语句区别?

六、从内存角度分析父子进程资源问题

七、产生子进程的另一个函数vfork

八、exec函数簇(功能类似于system)

作业


一、linux系统编程学习大纲(进程重理论 线程重实操)

1、进程的概念,进程诞生与死亡,进程函数接口,进程意义。
2、进程之间的通信方式:无名管道、有名管道、信号、消息队列、共享内存、信号量
3、linux信号的信号集概念,信号集函数接口,信号集作用,如何给信号设置阻塞状态?
4、线程的概念,线程与进程有什么区别?线程诞生与死亡,线程函数接口。
5、线程的同步互斥方式:有名信号量、无名信号量、互斥锁、读写锁
6、处理空闲线程方法:条件变量。
7、线程池  -> 为了能够同时处理多个任务。

二、进程的概念

 1.什么是程序?什么是进程?

程序:一堆待执行的代码。  gcc hello.c -o hello 静态的文本数据
进程:当程序被CPU加载时,根据每一行代码做出相应的效果,形成动态的过程,那么这个过程就是进程。  其实说白了,进程就是一个正在执行的程序。 

2.在linux下,如何开启一个新的进程?

 直接在linux下,执行一个程序,就会开启相应的进程。
例如: ./hello   -> 开启一个名字为hello的进程。

3.当进程开启之后,系统会为进程分配什么资源?

 1)会分配进程对应内存空间
int x  -> 运行程序之后,就会在栈区申请4个字节(当x是局部变量的时候)。
2)进程在系统内核中如何进行表示呢
学生管理系统---》每个学生使用结构体进行表示和管理
linux系统---------》每个进程使用结构体进行表示和管理
当进程开启之后,会为这个进程分配一个任务结构体,这个任务结构体就是用于描述这个进程的。
也就是说,进程在内核中是以结构体struct task_struct{} 进行表示的。这个结构体也被称之为进程控制块。
结构体:进程ID号、信号、文件、资源....
/usr/src/linux-headers-3.5.0-23/include/linux/sched.h    ----第1229行
gec@ubuntu:/usr/src/linux-headers-4.15.0-142/include/linux$ vi sched.h +560   ----600行左右

练习:在自己的ubuntu里面搜索结构体struct task_struct{}的大小

4.关于进程的命令

 1)查看整个Linux系统进程之间关系的命令
gec@ubuntu:~$ pstree
//所有的进程的祖先进程都是init,由操作系统镜像启动文件产生
systemd─┬─ManagementAgent───6*[{ManagementAgent}]
        ├─ModemManager─┬─{gdbus}
        │              └─{gmain}
        ├─NetworkManager─┬─dhclient
        │                ├─dnsmasq
        │                ├─{gdbus}
        │                └─{gmain}
        ├─VGAuthService
        ├─accounts-daemon─┬─{gdbus}
        │                 └─{gmain}
        ├─acpid
        ├─agetty
        ├─avahi-daemon───avahi-daemon
        ├─bluetoothd
        ├─colord─┬─{gdbus}
        │        └─{gmain}
        ├─cron
说明:        
 可以看到,最开始的系统进程叫systemd,这个进程的诞生比较特别,
其身份信息在系统启动前就已经存在于系统分区之中,在系统启动时直接复制到内存。
        
2)查看进程ID号  ----》ps -ef(静态)常用
gec@ubuntu:~$ ps -ef
用户名   进程ID  父进程 cpu占用  进程启动时间      进程持续时间 进程名
UID         PID   PPID  C STIME TTY          TIME CMD
root          1      0      0 18:29 ?        00:00:02 /sbin/init auto noprompt
root          2      0      0 18:29 ?        00:00:00 [kthreadd]
root          4      2      0 18:29 ?        00:00:00 [kworker/0:0H]
root          6      2      0 18:29 ?        00:00:00 [mm_percpu_wq]
root          7      2      0 18:29 ?        00:00:00 [ksoftirqd/0]
root          8      2      0 18:29 ?        00:00:00 [rcu_sched]

3)查看进程CPU的占用率 ---》top (动态)
gec@ubuntu:~$ top
top - 06:43:17 up 1 min,  2 users,  load average: 0.06, 0.02, 0.01
PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
1001 root      20   0 87580  22m 6716 S  1.0  2.3   0:00.84 Xorg
1 root      20   0  3668 2004 1284 S  0.3  0.2   0:00.61 init
2307 gec       20   0 90992  14m  10m S  0.3  1.5   0:00.27 gnome-terminal
2 root      20   0     0    0    0 S  0.0  0.0   0:00.00 kthreadd
3 root      20   0     0    0    0 S  0.0  0.0   0:00.05 ksoftirqd/0
4 root      20   0     0    0    0 S  0.0  0.0   0:00.10 kworker/0:0
5 root      20   0     0    0    0 S  0.0  0.0   0:00.23 kworker/u:0
练习1:
  写一个进程每隔一秒打印一句helloworld和次数,执行10秒;用指令观察ps -ef来观察(或者动态查看)
进程运行时的进程ID和父进程ID;当进程结束之后再观察它的进程ID和父进程ID

#include <stdio.h>
#include <unistd.h>

int main(int argc,char **argv)
{
	
	printf("main process\n");
	
	//产生了一个子进程
	pid_t id;
	id = fork();	
	if(id == -1) //fork失败
	{
		perror("fork fail");
		return -1;
	}
	else if(id > 0) //父进程
	{
		while(1) //此while(1)在父进程中单独运行
		{
			sleep(2);
			printf("hello\n");
			//printf("hello 我好大儿的ID号:%d 自己的ID号:%d\n",id,getpid());
		}
	}
	else if(id == 0) //子进程
	{
		while(1)//此while(1)在子进程中单独运行
		{
			sleep(1);
			printf("apple\n");
			//printf("apple 我爹的ID号:%d 自己的id号:%d\n",getppid(),getpid());
		}
	}
	
	//两个进程都会打印
	printf("after fork\n");
	
	return 0;
}

三、进程的状态(填空选择题)

1.进程从诞生到死亡会经历哪些状态?

就绪态   TASK_RUNNING   等待CPU资源  不占用CPU资源,不运行代码。
运行态   TASK_RUNNING   占用CPU资源,运行代码。(执行态)
暂停态   TASK_STOPPED   占用CPU资源,不运行代码。 可以到就绪态。
睡眠态   占用CPU资源,运行代码。   可以到就绪态。
    TASK_INTERRUPTIBLE       响应信号 ----》浅度睡眠  pause()--->一直等待下一信号
    TASK_UNINTERRUPTIBLE  不响应信号 ----》深度睡眠
僵尸态   EXIT_ZOMBIE    占用CPU资源,不运行代码。不可以到运行态。进程退出的时候,就一定会变成僵尸态
死亡态     EXIT_DEAD            不占用CPU资源,不运行代码。进程退出的时候,如果有人去帮自己回收资源,那么僵尸态就会变为死亡态

2.什么是僵尸态?

进程结束时,就从运行态变成僵尸态,所谓僵尸态,就是代表这个进程所占用的CPU资源和自身的任务结构体没有被释放,这个状态的进程就是僵尸态进程。

3.总结:

1)进程在暂停态时,收到继续的信号时,是切换到就绪态,而不是运行态。(重点)
2)程序的main函数执行return 0就会导致进程的退出,一定会变成僵尸态。
3)进程不可以没有父进程,也不能同时拥有两个父进程。
4)祖先进程一定要帮其他的进程回收资源。
5)孤儿进程特征就是失去父进程时,会马上寻找继父,而不是等到孤儿进程变成僵尸态再找。(孤儿进程和僵尸进程的区别)

四、进程的函数接口。

单进程程序  -> 只能一行行代码去执行。
多进程程序  -> 同时执行两行代码  -> 产生一个子进程,帮自己处理另外一件事情。


1.如何在一个正在运行的进程中产生一个子进程?  -> fork()  -> man 2 fork

 #include <unistd.h>
pid_t fork(void);
函数作用
    创建一个进程,将父进程的资源复制一份,申请一片新的资源给子进程,他们的资源都是独立的
返回值
    成功: (一次调用,两次返回)
        >0       父进程            但是返回的ID号表示的是子进程的ID号
        =0       子进程
失败
    返回  -1
    
说明:
  如果进程ID最大值没有达到系统进程数的上限,子进程比父进程ID数值大;(通常情况)
但是如果进程ID达到上限,系统会分配之前分配但是已经退出的进程ID给新进程;
这样有可能出现子进程ID数值比父进程小。

例题1: 在进程内部创建一个新的子进程,看看会不会同时做两件事情。
#include<stdio.h>
#include <unistd.h>
int main()
{
    /这是单进程的程序/
    printf("main process\n");
    /*产生一个子进程*/
    fork();
    
    /* 一个父进程,一个子进程 */
    /* 接下来的代码,父进程会执行一遍,子进程也会执行一遍 */
    printf("after fork\n");
    
    return 0;
}
结果分析:
结果1:父进程先运行,子进程后运行。
gec@ubuntu:/mnt/hgfs/就业班/07系统编程/01/1-code$ ./a.out
main process  -> 原来的进程打印出来的
after fork-> 父进程打印出来
gec@ubuntu:/mnt/hgfs/就业班/07系统编程/01/1-code$ after fork -> 子进程打印出来
注意:只有父进程退出,才会出现命令行,子进程退出是不会出现命令行。

结果2:子进程先运行,父进程后运行。
gec@ubuntu:/mnt/hgfs/就业班/07系统编程/01/1-code$ ./a.out
main process  -> 原来的进程打印出来的
after fork -> 子进程打印出来
after fork-> 父进程打印出来
gec@ubuntu:/mnt/hgfs/就业班/07系统编程/01/1-code$
例题2: 想确保子进程先运行,做法: 就是让父进程先睡眠。
-> 子进程不用睡眠,父进程需要睡眠。  -> 父子进程任务不一样。  -> 思路: 通过返回值判断。

#include<stdio.h>
#include <unistd.h>

int main()
{
    /*单进程的程序*/
    printf("main....\n");
    pid_t id = fork(); //创建一个子进程
    if(id == -1) //失败
    {
        perror("fork error");
        return -1;
    }
    else if(id > 0) //父进程 ,但是返回的ID号 是 子进程的ID号
    {
        // while(1)
        // {
        //  printf("我是你爹.....,我的儿子身份ID是:%d\n",id);
        //  sleep(1);
        // }
         sleep(1); 
        printf("我是你爹.....,我的儿子身份ID是:%d\n",id);
    }
    else if(id == 0)//子进程
    {
        // while(1)
        // {
        //  printf
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值