Linux系统编程—进程(作业+答案)
⽂章⽬录
程序和进程
程序,是指编译好的⼆进制⽂件,在磁盘上,不占⽤系统资源(cpu、内存、打开的⽂件、设备、锁…)
进程,是⼀个抽象的概念,与操作系统原理联系紧密。进程是活跃的程序,占⽤系统资源。在内存中执⾏。(程序运⾏起来,产⽣⼀个进程)
程序 → 剧本(纸) 进程 → 戏(舞台、演员、灯光、道具…)
同⼀个剧本可以在多个舞台同时上演。同样,同⼀个程序也可以加载为不同的进程(彼此之间互不影响)
如:同时开两个终端。各⾃都有⼀个bash但彼此ID不同。
并发
并发,在操作系统中,⼀个时间段中有多个进程都处于已启动运⾏到运⾏完毕之间的状态。但,任⼀个时刻点上仍只有⼀个进程在运⾏。
例如,当下,我们使⽤计算机时可以边听⾳乐边聊天边上⽹。 若笼统的将他们均看做⼀个进程的话,为什么可以同时运⾏呢,因为并发。
单道程序设计
所有进程⼀个⼀个排对执⾏。若A阻塞,B只能等待,即使CPU处于空闲状态。⽽在⼈机交互时阻塞的出现时必然的。所有这种模型在系统资源利⽤上及其不合理,在计算机发展历史上存在不久,⼤部分便被淘汰了。
多道程序设计
在计算机内存中同时存放⼏道相互独⽴的程序,它们在管理程序控制之下,相互穿插的运⾏。多道程序设计必须有硬件基础作为保证。
时钟中断即为多道程序设计模型的理论基础。 并发时,任意进程在执⾏期间都不希望放弃cpu。因此系统需要⼀种强制让进程让出cpu资源的⼿段。时钟中断有硬件基础作为保障,对进程⽽⾔不可抗拒。 操作系统中的中断处理函数,来负责调度程序执⾏。
在多道程序设计模型中,多个进程轮流使⽤CPU (分时复⽤CPU资源)。⽽当下常见CPU为纳秒级,1秒可以执⾏⼤约10亿条指令。由于⼈眼的反应速度是毫秒级,所以看似同时在运⾏。
1s = 1000ms, 1ms = 1000us, 1us = 1000ns 1000000000
实质上,并发是宏观并⾏,微观串⾏!
-----推动了计算机蓬勃发展,将⼈类引⼊了多媒体时代。
CPU和MMU
进程控制块PCB
我们知道,每个进程在内核中都有⼀个进程控制块(PCB)来维护进程相关的信息,Linux内核的进程控制块是task_struct结构体。
/usr/src/linux-headers-3.16.0-30/include/linux/sched.h⽂件中可以查看struct task_struct 结构体定义。其内部成员有很多,我们重点掌握以下部分即可:
进程id。系统中每个进程有唯⼀的id,在C语⾔中⽤pid_t类型表⽰,其实就是⼀个⾮负整数。
进程的状态,有就绪、运⾏、挂起、停⽌等状态。
进程切换时需要保存和恢复的⼀些CPU寄存器。
描述虚拟地址空间的信息。
描述控制终端的信息。
当前⼯作⽬录(Current Working Directory)。
umask掩码。
⽂件描述符表,包含很多指向file结构体的指针。
和信号相关的信息。
⽤户id和组id。
会话(Session)和进程组。
进程可以使⽤的资源上限(Resource Limit)。
linux内核设计与实现 pdf
进程状态
进程基本的状态有5种。分别为初始态,就绪态,运⾏态,挂起态与终⽌态。其中初始态为进程准备阶段,常与就绪态结合来看。
环境变量:
环境变量,是指在操作系统中⽤来指定操作系统运⾏环境的⼀些参数。通常具备以下特征:
① 字符串(本质) ② 有统⼀的格式:名=值[:值] ③ 值⽤来描述进程环境信息。
存储形式:与命令⾏参数类似。char *[]数组,数组名environ,内部存储字符串,NULL作为哨兵结尾。
使⽤形式:与命令⾏参数类似。
加载位置:与命令⾏参数类似。位于⽤户区,⾼于stack的起始位置。
引⼊环境变量表:须声明环境变量。extern char ** environ;
练习:打印当前进程的所有环境变量。
【environ.c】
常见环境变量
按照惯例,环境变量字符串都是name=value这样的形式,⼤多数name由⼤写字母加下划线组成,⼀般把name的部分叫做环境变量,value的部分则是环境变量的值。环境变量定义了进程的运⾏环境,
⼀些⽐较重要的环境变量的含义如下:
PATH
可执⾏⽂件的搜索路径。ls命令也是⼀个程序,执⾏它不需要提供完整的路径名/bin/ls,然⽽通常我们执⾏当前⽬录下的程序a.out却需要提供完整的路径名./a.out,这是因为PATH环境变量的值⾥⾯包含了ls命令所在的⽬录/bin,却不包含a.out所在的⽬录。PATH环境变量的值可以包含多个⽬录,⽤:号隔开。在Shell中⽤echo命令可以查看这个环境变量的值:
$ echo $PATH
SHELL
当前Shell,它的值通常是/bin/bash。
TERM
当前终端类型,在图形界⾯终端下它的值通常是xterm,终端类型决定了⼀些程序的输出显⽰⽅式,⽐如图形界⾯终端可以显⽰汉字,⽽字符终端⼀般不⾏。
LANG
语⾔和locale,决定了字符编码以及时间、货币等信息的显⽰格式。
HOME
当前⽤户主⽬录的路径,很多程序需要在主⽬录下保存配置⽂件,使得每个⽤户在运⾏该程序时都有⾃⼰的⼀套配置。
getenv函数
获取环境变量值
char *getenv(const char *name); 成功:返回环境变量的值;失败:NULL (name不存在)
练习:编程实现getenv函数。
【getenv.c】
setenv函数
设置环境变量的值
int setenv(const char *name, const char *value, int overwrite); 成功:0;失败:-1
参数overwrite取值: 1:覆盖原环境变量
0:不覆盖。(该参数常⽤于设置新环境变量,如:ABC = haha-day-night)
unsetenv函数
删除环境变量name的定义
int unsetenv(const char *name); 成功:0;失败:-1
注意事项:name不存在仍返回0(成功),当name命名为"ABC="时则会出错。
进程控制
fork函数
创建⼀个⼦进程。
pid_t fork(void); 失败返回-1;成功返回:① ⽗进程返回⼦进程的ID(⾮负) ②⼦进程返回 0
pid_t类型表⽰进程ID,但为了表⽰-1,它是有符号整型。(0不是有效进程ID,init最⼩,为1)
注意返回值,不是fork函数能返回两个值,⽽是fork后,fork函数变为两个,⽗⼦需【各⾃】返回⼀个。
循环创建n个⼦进程
⼀次fork函数调⽤可以创建⼀个⼦进程。那么创建N个⼦进程应该怎样实现呢?
简单想,for(i = 0; i < n; i++) { fork() } 即可。但这样创建的是N个⼦进程吗?
从上图我们可以很清晰的看到,当n为3时候,循环创建了(2^n)-1个⼦进程,⽽不是N的⼦进程。需要在循环的过程,保证⼦进程不再执⾏fork ,因此当(fork() == 0)时,⼦进程应该⽴即break;才正确。
练习:通过命令⾏参数指定创建进程的个数,每个进程休眠1S打印⾃⼰是第⼏个被创建的进程。
如:第1个⼦进程休眠0秒打印:“我是第1个⼦进程”;第2个进程休眠1秒打印:“我是第2个⼦进程”;第3个进程休眠2秒打印:“我是第3个⼦进程”。 【fork1.c】
通过该练习掌握框架:循环创建n个⼦进程,使⽤循环因⼦i对创建的⼦进程加以区分。
getpid函数
获取当前进程ID
pid_t getpid(void);
getppid函数
区分⼀个函数是“系统函数”还是“库函数”依据:
② 是否访问内核数据结构
② 是否访问外部硬件资源 ⼆者有任⼀ → 系统函数;⼆者均⽆ → 库函数
getuid函数
获取当前进程实际⽤户ID
uid_t getuid(void);
获取当前进程有效⽤户ID
uid_t geteuid(void);
getgid函数
gid_t getgid(void);
获取当前进程有效⽤户组ID
gid_t getegid(void);
进程共享
⽗⼦进程之间在fork后。有哪些相同,那些相异之处呢?刚fork之后:获取当前进程的⽗进程ID  pid_t getppid(void);
获取当前进程使⽤⽤户组ID

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。