Linux系统编程@进程管理(一)
课程目标:
构建一个基于主机系统的多客户即时通信/聊天室项目
参考教程
Robert Love, Linux System program
……
进程结构
进程由程序、数据和进程控制三部分组成
进程的阻塞态:由于访问设备时,没有数据输出的等待状态。
进程互斥:当有若干进程都要使用某一共享资源时,任何时刻最多允许一个进程使用,其他要使用该资源的进程必须等待,直到占用该资源者释放了该资源为止。例如串口同时只允许一个进程对齐进行访问。
临界资源:一次只允许一个进程访问的资源。
临界区:访问临界资源的那段代码称为临界区。为实现对临界资源的互斥访问,应保证诸进程互斥地进入各自的临界区。How?
进程同步:一组并发进程按一定的顺序执行的过程。具有同步关系的一组并发进程称为合作进程,合作进程间互相发送的信号称为消息或事件。
进程调度:按一定的算法,从一组待运行的进程中选出来一个占有CPU运行。
调度方式:抢占式:高优先级进程能够打断正在运行的低优先级的进程。
非抢占式:高优先级进程不能够打断低优先级的进程,只能等待正在运行的进程结束。
调用算法:先来先服务调度算法 根据就绪的先后来决定先后
断进程优先调度算法 根据进程运行时间排列,最短时间先运行
高优先级优先调度算法 注意不同的系统优先级数字高低与优先级高低规定不同,Linux是数字越小,优先级越高。Window相反。
时间片轮转法 时间片是一段规定的时间,进程在时间片中运行未结束则挂起,由其他进程继续在这么长的一段时间中运行,然后轮询。
死锁:多个进程因竞争资源而形成的一种僵局,需要依靠外力解决。
进程状态
TASK_RUNNING(运行): R 可执行状态。正在执行,在就绪队列中等待。
TASK_INTERRUPTIBLE(可中断): S 睡眠(阻塞)。如果条件满足,内核将其状态设置为运行。收到信号而被提前唤醒并投入运行。
l: 长格式输出
u: 按用户名和启动时间的顺序来显示进程
j: 用任务格式来显示进程
f: 用树形格式来显示进程
a: 显示所有用户的所有进程
x: 显示无控制终端的进程
r: 显示运行中的进程
ww: 避免详细参数被截断
$ps //列出当前shell里当前用户的进程
$ps –u yuhong //列出用户yuhong运行的所有进程
$ps –el //以详细列表方式显示运行的所有进程
$ps aux //以详细的BSD风格显示运行的所有进程
%MEM:占用的内存的使用率
VSZ : 虚拟内存大小,即一个程序完全驻留在内存的话需要占用多少内存空间
RSS: 当前实际占用了多少内存
STAT: 进程当前状态(R/S/D/Z/T)
后缀:
进程创建与终止
1、进程的创建
创建函数:
pid_t fork(void); (在父进程返回,fork()返回子进程ID,在子进程中返回,fork()返回0。当进程数达到上限或者内存不足时,可能会出错,返回值为-1,系统调用并不直接返回错误码,而是将错误码放在全局变量errno中)
各种错误情况下errno的值: 1) 进程达到上限 errno=EAGAIN
2) 系统内存不足 errno=ENOMEM
相关面试题:考察fork()与编译器对逻辑运算符的处理规则,问题:下列程序一共能够创建多少个进程(包括main进程)?
#include <stdio.h>
#include <unistd.h>
int main(int argc, char* argv[])
{
fork();
fork() && fork() || fork();
fork();
return 0;
}
答案:20
fork与文件操作面试题:在fork之前,父进程打开了一个文件。在fork之后,如果子进程移动了文件指针,父进程的文件指针有什么变化;如果子进程关闭了文件,父进程有什么变化?为什么会这样?
不同进程打开同一个文件后,进程表和文件表的关系如下图所示:
进程的所打开文件和在fork后的结构图如下所示,子进程是共享父进程的文件表项;
测试代码:
1. #include "slp.h"
2.
3. int main()
4. {
5. int fd1,fd2,fd3,nr;
6. char buff[20];
7. pid_t pid;
8. fd1 = open("data.in",O_RDWR);
9. pid = fork();
10. if(pid == 0)
11. {
12. nr = read(fd1,buff,10);
13. buff[nr]='\0';
14. printf("pid#%d content#%s#\n",getpid(),buff);
15. exit(0);
16. }
17. nr = read(fd1,buff,10);
18. buff[nr]='\0';
19. printf("pid#%d content#%s#\n",getpid(),buff);
20. return 0;
21. }
测试用例 data.in
- abcdefghijklmnopqrstuvwxyz1234567890
- EOF
测试结果:
pid#20029 content#abcdefghij#
pid#20030 content#klmnopqrst#
结果分析:
进程20029对文件的读取后的当前位置应该为data.in的k字符所在的位置,进程20030是由20029进程之后开始读取的,他读取文件内容不是从a开始,而是从k开始,说明20030共享了20029的文件表。
pid_t vfork(); 调用vfork的作用与调用fork的作用基本相同,但是vfork并不完全复制父进程的数据段,而是和父进程共享数据段。这是因为通常vfork函数是与exec函数族的函数连用,创建执行另一个程序的新进程。调用vfork时,父进程被挂起,子进程运行至调用exec函数族或调用 exit时解除这种状态。
exec函数族(会替换掉原有进程的程序代码,system会调用fork产生子进程,然后在子进程中执行程序)
#include <unistd.h>
int execl(const char* path,const char* arg1,...) //path:包含路径的程序名
//后面为程序所需的命令行参数,包括程序名,以空指针NULL或(char*)0结束.
int execlp(const char* path,const char* arg1,...) //path:不包含路径的程序名,程序从环境变量中查找
//后面为程序所需的命令行参数,包括程序名,以空指针NULL或(char*)0结束.
int execv(const char* path,char* const argv[]) //path:包含路径的程序名,不包含程序名
//将命令行参数字符串放在了一个字符串数组里面 #include <stdlib.h>
int system(const char* string) //调用fork产生子进程,由子进程来调用/bin/sh -c string来执行参数string所代表的命令
exec函数族说明
一个进程一旦调用exec类函数,它本身就“死亡”了
exec函数族实例
#include <unistd.h>
#include <stdlib.h>
main()
{
execl("/bin/ls","ls","-al","/etc/passwd",(char*)0);
execlp("ls","ls","-al","/etc/passwd",(char*)0);
char *argv[]={"ls","-al","/etc/passwd",(char*)0};
execv("/bin/ls",argv);
system("ls -al /etc/passwd");
}
获取进程ID:getpid(); getppid();
应该避免产生“孤儿进程”(孤儿进程还未结束,父进程却已经结束),解决方法:子进程托孤,或者让其父进程最后退出。
子进程托孤:init进程(PID=1)接管。
Questions:
如何实现子进程托孤?fork()例3中的子进程为何能够在父进程退出后,托孤给init进程(难道父进程退出后,自动托孤,不用额外的操作)?
fork()例3中为什么原进程会存在一个父进程?
子进程都继承了父进程哪些东西?试用代码举例。
2、Linux中的两个特殊的进程
0号进程:所有进程的祖先
swapper进程(调度进程):负责进程间的调度,内核直接控制,用户进程无法访问。
执行cpu_idle()函数
没有其他进程处于TASK_RUNNING,内核会选择0号进程运行
0号进程创建的1号进程
初始化进程在内核引导流程结束时被调用,用于初始化系统环境。初始化文件是/erc/rc*文件、/etc/inittab文件及/etc/init.d目录下的文件。初始化进程从不退出。
init进程创建和监控其他进程的活动
接管孤儿进程
3、进程的终止
1)显式的系统调用
#include <stdlib.h>
void exit(int status); //wxit是标准C中提供的函数。将关闭所用被该文件打开的文件描述符。退出前把文件缓冲区的内容写回文件 #include <unistd.h>
void _exit(int status); //调用_exit是为了关闭一些Linux下特有的退出句柄。退出后缓冲区数据丢失
这两个函数调用后,进程转化为僵尸进程。
其他用于进程终止的系统调用(需要用时再仔细研究)
#include <stdlib.h>
int atexit(void (*function)(void)); //用于注册一个不带参数也没有返回值的函数以供程序正常退出时被调用。
int on_exit(void(*function)(int, void*),void *arg); //类似atexit,但是它注册的函数具有参数,退出状态和参数arg都传递给该程序使用。
//调用成功时,返回值为0;调用失败时,返回值为-1,并将errno设置为响应值。
void abort(void); //实际是用来发送一个SIGABRT信号,使当前进程终止。 #include <assert.h>
void assert(int expression); //assert是一个宏。调用assert时,将先计算参数表达式expression的值,如果为0,则调用abort函数结束进程。
//通常用此函数来检测某些参数是否有不当情况出现,并在不当情况发生时以结束进程作为相应处理。
2)从程序结尾离开
3)被信号终止 SIGTERM(signal terminate) SIGKILL
kill [-s <信号名称或编号>][程序]
kill [-l <信号编号>]
若不加<信息编号>选项,则-l参数会列出全部的信息名称。
//强行中止(杀掉)一个进程pid为324的进程:
#kill -9 324
#free
Questions:
进程管理中信号有哪些,以及编号都是什么,如何使用?
4)被内核杀掉 Segmentation violation
当进程出现异常时,会被内核杀掉。
进程终止内核会传送一个SIGCHLD(signal child)信号给它的父进程
若一个子进程在终止时整体消失,父进程将无法取回任何的信息
若子进程先于它的父进程结束,则内核应该让子进程进入僵尸进程的状态,等待父进程来打听它的状态,状态打听后,僵尸进程才会正式结束。
僵尸进程的内核数据结构
僵尸进程只会保留最小的骨架:进程的PID,退出状态,运行时间
僵尸进程的避免:
i 父进程通过wait和waitpid等函数等待子进程结束(导致父进程立刻阻塞自己,直到有一个子进程退出)。
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status); wait(&status) =>waitpid(-1,&status,0)
返回值:1.结束的子进程pid 2.-1,如果没有子进程
status(两个字节):1.高字节:子进程exit时设置的代码,低字节为0 2.如果子进程的退出是因为收到信号,低字节为信号的编码
有时会见到wait函数的参数是NULL,表示父进程并不关心子进程的状态,只是等待子进程结束,并获得子进程信息,防止其成为孤儿进程或僵死进程。
pid_t waitpid(pid_t pid, int *status, int options);
pid取值:
Options可以是以下几个常数中的一个或多个
#define _USE_BSD
#include <sys/types.h>
#include <sys/resource.h>
#include <sys/wait.h> pid_t wait3(int *status, int options, struct rusage *rusage );
pid_t wait4(pid_t pid,int *status, int options, struct rusage *rusage);
Waitpid出错时的error返回值以及原因
If waitpid() is not successful, errno usually indicates one of the following errors. Under some conditions, errno could indicate an error other than those listed here.
[ECHILD] | Calling process has no remaining child processes on which wait operation can be performed. |
[EINVAL] | An invalid parameter was found.
A parameter passed to this function is not valid. |
[EFAULT] | The address used for an argument is not correct.
In attempting to use an argument in a call, the system detected an address that is not valid. While attempting to access a parameter passed to this function, the system detected an address that is not valid. |
[EINTR] | Interrupted function call. |
[EOPNOTSUPP] | Operation not supported.
The operation, though supported in general, is not supported for the requested object or the requested arguments. |
[EUNKNOWN] | Unknown system state.
The operation failed because of an unknown system state. See any messages in the job log and correct any errors that are indicated, then retry the operation. |
用法:
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
参数:
signum:信号编码。
handler:新的信号处理句柄。
返回说明:
成功执行时,返回以前的信号处理句柄。失败返回SIG_ERR。
状态标志:
信号:
三种方式执行多任务处理:轮询、中断、DMA(与中断的区别)
Questions:
为什么两次fork可以将孙进程托孤给init进程?
handler句柄是什么东西?
信号处理句柄可能是用户指定的函 数,SIG_IGN 或 SIG_DFL。
4.system函数
可以使用system函数在自己的程序中使用操作系统提供的各种命令(实质上system的实现是fork exec waitpid函数实现的)。
#include <stdlib.h>
int system(const char *cmdstring);
system函数是否有效的测试方法
设置cmdstring为NULL,然后调用system,如果有效则返回非NULL指针,无效则返回0.
To be continued...
Linux系统编程@进程管理(一)的更多相关文章
- Linux系统编程@进程管理(二)
1.创建守护进程(Deamon) 守护进程的概念与作用 后台服务程序 – 系统服务,进程名字往往以’d’结尾,生存周期比较长(系统装入时启动,关闭时候终止.系统装入两种启动方式:1从启动脚本.etc/ ...
- Linux系统编程@进程通信(一)
进程间通信概述 需要进程通信的原因: 数据传输 资源共享 通知事件 进程控制 Linux进程间通信(IPC)发展由来 Unix进程间通信 基于System V进程间通信(System V:UNIX系统 ...
- linux系统编程-进程
进程 现实生活中 在很多的场景中的事情都是同时进行的,比如开车的时候 手和脚共同来驾驶汽车,再比如唱歌跳舞也是同时进行的: 如下是一段视频,迈克杰克逊的一段视频: http://v.youku.com ...
- Linux系统分区 进程管理 软件包安装
对于一块新的磁盘来说,系统能够使用需要有分区.格式化文件系统.挂载等主要操作,下面通过命令的方式对一块磁盘进行操作. 一. Linux系统分区 1.1 在虚拟机开机前选择虚拟机配置,添加一个新的SCS ...
- [Linux] Linux系统(进程管理)
进程:当我们运行程序时,Linux会为程序创建一个特殊的环境,包含程序运行的所有资源,这个环境就称为进程 前台进程:一般我们使用一些命令,都属于前台进程,直接输出结果到显示器 后台进程:在命令的末尾加 ...
- [linux] C语言Linux系统编程进程基本概念
1.如果说文件是unix系统最重要的抽象概念,那么进程仅次于文件.进程是执行中的目标代码:活动的.生存的.运行的程序. 除了目标代码进程还包含数据.资源.状态以及虚拟化的计算机. 2.进程体系: 每一 ...
- Linux系统编程——进程替换:exec 函数族
在 Windows 平台下,我们能够通过双击运行可运行程序,让这个可运行程序成为一个进程.而在 Linux 平台.我们能够通过 ./ 运行,让一个可运行程序成为一个进程. 可是.假设我们本来就执行着一 ...
- Linux系统编程—进程间同步
我们知道,线程间同步有多种方式,比如:信号量.互斥量.读写锁,等等.那进程间如何实现同步呢?本文介绍两种方式:互斥量和文件锁. 互斥量mutex 我们已经知道了互斥量可以用于在线程间同步,但实际上,互 ...
- Linux系统编程-----进程fork()
在开始之前,我们先来了解一些基本的概念: 1. 程序, 没有在运行的可执行文件 进程, 运行中的程序 2. 进程调度的方法: 按时间片轮转 先来先服务 短时间优先 按优先级别 3. 进程的状态: 就绪 ...
随机推荐
- Qt5.11+opencv3.4的配置安装
系统:Windows 10 64位 前期准备: 1.CMake下载安装 下载地址:https://cmake.org/download/ 选择msi安装文件,按照提示一步一步按照就好 可以参考:htt ...
- Codeforces 898D - Alarm Clock
传送门:http://codeforces.com/contest/898/problem/D 有n个闹钟,第i(1≤i≤n)个闹钟将在第ai(1≤ai≤106)分钟鸣响,鸣响时间为一分钟.当在连续的 ...
- 【[Offer收割]编程练习赛10 B】出勤记录II
[题目链接]:http://hihocoder.com/problemset/problem/1482 [题意] [题解] 递推题. 每次增加3个字符中的一个;然后根据下面这个数组递推; 递推方式看程 ...
- mybatis源码阅读-SqlSessionFactory和SqlSession(三)
说明 读了3遍:https://my.oschina.net/zudajun/blog/665956 现在统一整理成笔记 并跟着源码一行一行调试 统一整理起来 SqlSession 接口定义 publ ...
- HDU 4273
计算凸包重心到各面的最短距离. 若知道重心,按四面体用体积法即可求出高. 关键在于,多面体重心的求法.这必须把多面体分割成多个四面体来求.下面从多边形的重心说起. 一般来用,对于一个多边形(p0,p1 ...
- Python标准库:内置函数range(stop) range(start, stop[, step])
本函数是产生一系列序列的数组,返回迭代子.參数stop是终止的数字:參数start是指明開始数列開始值:參数step是数列之间的差值. 因此这个函数就是产生以start为起点.以stop为终点,以st ...
- 3.2 re--正則表達式操作(Regular expression operations)
本模块提供了正則表達式的匹配操作,它的功能跟Perl语言里的功能一样. 不管是Unicode字符串还是单字节8位组成的字符串,都能够使用模式匹配和字符串查找的功能. 只是要注意的是Unicode字符串 ...
- android制作闪动的红心
先上一张效果图吧: 说说这个东西的来源吧.今天突然想到笛卡尔心形图,想去看看能不能画个心出来,可是看到一篇不错的文章,那篇文章罗列了非常多关于心形的函数方程,这可把我高兴坏了,于是我选取了一个比較好看 ...
- 解题报告 之 HDU5303 Delicious Apples
解题报告 之 HDU5303 Delicious Apples Description There are n apple trees planted along a cyclic road, whi ...
- h5-9 canvas
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...