首页 >> 大全

Linux系统编程:step1

2023-10-13 大全 33 作者:考证青年

一、程序和进程

1、程序和进程

(1)程序:二进制文件,占用的磁盘空间(未运行的,躺在磁盘中)

(2)进程:一个启动的程序(启动之后,程序就和磁盘没有关系了),所有的数据都在内存中,需要占用更多的系统资源(CPU,物理内存),例如:一个剧本(未拍摄,那可以看成程序),要把这个剧本的内容呈现出来(那就是进程),需要演员、道具,场地等(也就是电脑中的系统资源)。需要各个系统资源之间交互。

2、并发和并行

(1)并发:并不是某一个时间点的概念,是一个时间段的概念,常说的高并发服务器,说的是在一短时间内,处理的请求数(例如:1秒内处理的请求个数)。

例如:一个咖啡机(一个CPU),有100个人去打咖啡,1分钟只能打一杯咖啡,我们需要在1分钟之内让所有的人都能取到咖啡,那么采用的算法就是每人有0.6秒的时间取咖啡,虽然没打满,但是取到了咖啡,只有按照同样的方法进行下去,这就是一个高并发的例子。人相当于请求。

cpu在某一时间点只能处理一个进程,为了能并发处理,CPU会把一个时间段切割成一系列的时间碎片,分给不同的进程使用(有点像时分通信),一句话就是切割时间。

(2)并行

上图所示,由一个咖啡机变成了两台,也就是增加了一个CPU,用户看到的是并发量变大了,实际上是增加了处理器。例如:淘宝后台服务器提供服务,是通过许多的服务器进行并行提供服务。

小结:每个进程只能在某一时间段获取CPU,所有的进程都是轮循的获取CPU,一个进程不会独占CPU。

3、pcb

进程控制块( block)

作用:存在于内核当中,用来维护进程的相关信息,Linux内核PCB是结构体,也就是一个类型,里面有需要保存的信息(与C++里面面向对象编程如出一辙)。

重点部分:

二、进程控制(关键)

进程控制:讲的就是如何建立进程

程序a.out 编程进程,去shell进程中执行 ./a.out 变为子进程

Q:如何让一个父进程创建一个子进程?

A:int fork(void)

fork(叉子),子进程相当于父进程的一个拷贝,也就是子进程和父进程地址空间一样,也就是上图中除内核的其他部分完全一样。也就是用户区一样。但是内核区不一样,因为内核区中保存了PCB,而PCB中中保存的进程id是不一样的,所以,子、父进程不同的就是进程id不一样。

fork不同之处:有两个返回值,一个进程变为两个进程之后,因为拷贝,两个进程的地址空间是一样的,数据完全一样,在代码段.text中,父子进程都有一个fork,所以都有一个返回值,父进程就返回子进程的一个id,子进程就返回0,所以就有了两个返回值。

上图所示,父进程中fork之后,其实就拷贝了拥有相同代码的子进程(右边),但是是在父进程中fork的所以pid得到的是子进程的id,而在子进程中,由于没有fork,所以返回的pid是0,以此区分子进程和父进程。也就是返回值是由不同的进程出来的,拷贝之后就有两个了。

问题:

Q1:fork函数的返回值 A1:两个,pid>0 父进程的返回值,pid == 0, 子进程的返回值

Q2:子进程创建成功后,代码的执行位置?

A2:正如上面所示,子进程从非灰色部分开始执行,而父进程从头到尾执行。父进程执行到了那,子进程就从那开始执行。

Q3:父子进程的执行顺序?

A3:不一定,谁抢到CPU,谁执行。

Q4:如何区分父子进程?

A4:通过fork的返回值。

#include
#include
#include
#include
#include
#include
int main(int argc,const char* argv[])
{pid_t pid;for(int i=0;i<4;i++){printf("---------------i=%d\n",i);}pid = fork();if(pid >0){printf("parent process,pid=%d\n",getpid());}else if(pid ==0){printf("child  process,pid=%d\n",getpid());}for(int i=0;i<4;i++){printf("---------------i=%d\n",i);}return 0;
}

有上面实验的结果可知,子进程中是从fork下面的开始开始执行的。所以子进程虽然有代码段,但是不会执行。让父进程睡一会(父进程结束的太快了,已经结束了,使得子进程将init进程作为父进程了)

出现上面的原因:

2、ps和kill命令

Q:如何获取当前进程的id?

A:

#

#

#

#

#

#

int main(int argc, const char* argv[])

while(1)

(“hello,world\n”);

sleep(1);

0;

TTY :就是终端,au显示依赖终端的选项。

Q:终端对于进程来说有什么用呢?

A:没有终端就看不到输出,所以依赖终端的进程是需要和用户交互的。

|:表示管道。

kill:发信号给某个进程

只需学1-31个信号即可。

杀死某个进程:kill -9 (仅为9号信号为表示杀死信号)

3、进程间数据共享

关于fork,刚fork之后,两个地址空间用户区数据完全相同,后续各自都做了各自不同的操作,各个进程的地址空间中的数据是完全独立的,如下图所示,n=24子、父一样的,在子父进程各自对n进行操作时,是互不影响的,一个++,一个–;扩展:研究这个n = 24,但对于这个n只是进行读操作时,那么在物理内存中n是一份,也就是子父进程只是读这个n,而不改变,这样可以节省内存,也就是读时共享n;但是在进行读写操作时,因为可以子父进程可以修改它,为了子父进程互不影响,子进程就拷贝一份,也就是不是共享的。

上图所示,在父或子进程对变量进行写操作时,都会在物理内存开辟一块内存保存修改的值,而且父子变量内存不共享。

Q:父、子进程之间,是否可以通过一个全局变量进行通信?

A:不能,因为根据上面的原理,写复制,父子各自的变量物理内存不一样,所以内存不能共享。

小结:读时共享,写时复制

扩展:需要实现共享就牵涉到进程间的通信。

4、exec函数族

(1)作用

①让父子进程执行不相干的操作

②父子进程代码段一样的,而exec函数组可以替换父子空间的源代码,也就是.text段

注意:替代.text中代码的是可执行文件源代码,因为是进程,所以需要是可执行的程序。其中的ls是一个例子,它是shell中的一个可执行文件,所以当替换只有,进程就相当于执行命令ls了。鸠占鹊巢的感觉。

(2)使用

例子:

“who can here”,只执行了一次,并且是在父进程3047中,而子进程3048执行的程序是ls,所以得出结论,当子进程使用execl()时,子进程中的.text中的源代码全部被execl程序中的代码所替换,也就不能执行if(pid==0)外的内容了。

5、进程回收

(1)孤儿进程

①爹生孩子

②爹先死,孩子还活着,孩子就叫做孤儿进程

③孤儿被init进程领养,init进程变为孤儿进程的父亲。(这就是为什么写程序会出现子进程的父进程变为1,也就是原来的父进程执行的太快了,以至于原来父进程结束了,而子进程还没执行完,就成孤儿了。)

④为了释放子进程占用的系统资源

进程结束之后,能够释放用户区空间;释放不了PCB,必须由父进程释放(key)。

这里就出现了孤儿进程被ppid=1的进程领养了。

(2)僵尸进程

①孩子死了,爹还活着,爹不去释放子进程的pcb,孩子就变成 了僵尸进程。(因为只能由父进程释放)

②不是活着的进程

z:表示僵尸的意思,表示a.out进程为僵尸进程。

如何杀死僵尸进程呢?通过杀死他爹的进程就行了,也就是3516,直接杀死僵尸进程是不能消除的,相当于鞭尸。

(3)进程回收

①wait-阻塞函数(与差不多,也就是一直阻塞在哪,等待消息)

pid_t wait(int * );

作用:因为子进程需要父进程进行回收,而且在并发运行的时候,父子进程的执行顺序是不一定的,所以需要wait函数去使得父进程等待子进程结束,然后回收,最后父进程结束,造成有顺序的执行。

如上died child pid是最后执行的,表明父进程是在子进程执行完,回收它之后执行完的。

这里最后的返回值为9,表明父进程一直执行到最后,所以返回了9,也就是 正常退出

当子进程一直循环不退出时,父进程将一直等待:

有两个进程在运行,kill -9 4051 。通过信号杀死子进程,预期不会返回12,会返回9

终止了。

小结:

父进程结束的比子进程早,则会造成子进程成为孤儿进程;

父进程结束的比子进程晚,并且父进程一直不退出,则会造成子进程成为僵尸进程;(所以消灭僵尸进程是通过kill父进程达到目的)

解决方法:通过父进程中调用wait函数。

关于我们

最火推荐

小编推荐

联系我们


版权声明:本站内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 88@qq.com 举报,一经查实,本站将立刻删除。备案号:桂ICP备2021009421号
Powered By Z-BlogPHP.
复制成功
微信号:
我知道了