版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、1、下面先介绍实验的准备工作:1)准备好EduKit-IV实验平台一套,Mini2410-IV核心子板一套,5V/2A 电源适配器一个,交叉串口线一个(在PC机上),交叉网线一个。2)在测试台摆放好EduKit-IV 实验平台,小心打开EduKit-IV实验平台上盖,注意防止箱体向后倾倒(最好箱体上盖后面靠着其他物品)。3)检查EduKit-IV实验平台出厂跳线,注意Mini2410-IV核心子板上的跳线为闭合状态,电源拨动开关拨向向下端的断开状态。4)连接5V/2A 电源适配器到EduKit-IV实验平台的电源接口(电源插座供电为220V市电)。5)连接交叉串口线于PC机的串口端和EduKi
2、t-IV实验平台的COM2端。6)连接交叉网线线于PC机端网卡和EduKit-IV实验平台主板网卡接口。7)将EduKit-IV实验平台的电源的拨动开关拨向向上端的加电状态,给实验平台上电。可以看到POWER区的三个红色电源指示灯会亮:1V8_LED、3V3_LED、5V0_LED。如果有任意电源指示灯不亮,请立刻关闭电源,检查电路故障。2、每次实验开始需要做的工作1)在ubuntu中的终端中输入minicom回车,然后给实验平台上电。实验平台启动完成后此终端即通过minicom可以对实验平台进行操作。此时在此终端中通过输入ifconfig命令来查看实验平台的网络配置情况。此时会显示eth0和
3、eth1两个网络相关信息,如果不知道当前网口对应的是eth0还是eth1,最简单的办法就是轻轻拔掉实验平台上的网线,终端中会显示eth* link down字样,*可能是0或1。之后在进行第4)IP地址等的设置时,设置成语eth*的IP在同一网段。2)点击ubuntu 菜单栏右边的网络图标“”,弹出以下界面,如图1所示: 图1 网络设置界面图 2 激活网络设置界面3)在界面中点击解锁按钮,弹出对话框要求输入当前用户密码,正确输入后,将激活该菜单,如图2所示:4)选中“有线连接”,点击属性按钮,在弹出的界面中按照图3所示设置网络IP(其中IP 地址可以任意设置,必须为跟实验平台主板网卡IP 在同
4、一网段,实验平台的IP地址可通过ifconfig命令查询):图3 设置网络IP图4 设置正确后的网络5)设置完成后点击确定按钮退出,则网络设置界面将显示如图4所示。l 其中:2)-5)步也可以用下列两条命令实现n ifconfig eth* 192.192. 192.190 netmask n route add default gw 6)此时运行ubuntu的另一个终端,在终端中输入ifconfig命令查看当前网络设置。7)虚拟机(VM)-设置(Setting)-选择NetWork Adapter-网络连接-桥接。图4 虚拟机桥连接的设置3
5、、linux系统中的export命令的作用exports是设置NFS共享文件的Linux export命令功能说明:设置或显示环境变量。语法:export -fnp变量名称=变量设置值补充说明:在shell中执行程序时,shell会提供一组环境变量。export可新增,修改或删除环境变量,供后续执行的程序使用。export的效力仅局限于此登录操作l -f 代表变量名称中为函数名称。l -n 删除指定的变量。变量实际上并未删除,只是不会输出到后续指令的执行环境中。l -p 列出所有的shell赋予程序的环境变量。一个变量创建时,它不会自动地为在它
6、之后创建的shell进程所知。而命令export可以向后面的shell传递变量的值。当一个shell脚本调用并执行时,它不会自动得到原为脚本(调用者)里定义的变量的访问权,除非这些变量已经被显式地设置为可用。export命令可以用于传递一个或多个变量的值到任何后继脚本。登录到系统后,系统将启动一个用户shell。在这个shell中,可以使用shell命令或声明变量,也可以创建并运行shell脚本程序。运行shell脚本程序时,系统将创建一个子shell。此时,系统中将有两个shell,一个是登录时系统启动的shell,另一个是系统为运行脚本程序创建的shell。当一个脚本程序运行完毕,它的脚本
7、shell将终止,可以返回到执行该脚本之前的shell。从这种意义上来说,用户可以有许多shell,每个shell都是由某个shell(称为父shell)派生的。在子shell中定义的变量只在该子shell内有效。如果在一个shell脚本程序中定义了一个变量,当该脚本程序运行时,这个定义的变量只是该脚本程序内的一个局部变量,其他的shell不能引用它,要使某个变量的值可以在其他shell中被改变,可以使用export命令对已定义的变量进行输出。export命令将使系统在创建每一个新的shell时定义这个变量的一个拷贝。这个过程称之为变量输出。#echo $PKG_PATH
8、在任何时候建立的变量都只是当前shell的局部变量,所以不能被shell运行的其他命令或shell程序所利用。 export ,是把你设置的环境变量传给当前SHELL的子SHELL,使其在子SHELL中有效。4、linux中source、bash命令区别1)source命令用法:source FileName作用:在当前bash环境下读取并执行FileName中的命令。该filename文件可以无"执行权限" 注:该命令通常用命令“.”来替代。 如:source .bash_profile &
9、#160; . .bash_profile两者等效。 source(或点)命令通常用于重新执行刚修改的初始化文档。 source命令(从C Shell而来)是bash shell的内置命令。 点命令,就是个点符号,(从Bourne Shell而来)。 source的程序主体是bash,脚本中的$0变量的值是bash,而且由于作用于当前bash环境,脚本中set的变量将直接起效2) sh, bash的命令用法: s
10、h/bash FileName 作用:打开一个子shell来读取并执行FileName中命令。该filename文件可以无"执行权限" 注:运行一个shell脚本时会启动另一个命令解释器. 每个shell脚本有效地运行在父shell(parent shell)的一个子进程里。这个父shell是指在一个控制终端或在一个xterm窗口中给你命令指示符的进程。shell脚本也可以启动他自已的子进程。这些子shell(即子进程)使脚本并行地,有效率地地同时运行脚本内的多
11、个子任务。 在ubuntu中sh只是bash的一个链接。 由于是在子shell中执行,脚本设置的变量不会影响当前shell。3) ./的命令用法: ./FileName 作用:打开一个子shell来读取并执行FileName中命令。该filename文件需要"执行权限" 注:运行一个shell脚本时会启动另一个命令解释器. 每个shell脚本有效地运行在父shell(par
12、ent shell)的一个子进程里。这个父shell是指在一个控制终端或在一个xterm窗口中给你命令指示符的进程。shell脚本也可以启动他自已的子进程。这些子shell(即子进程)使脚本并行地,有效率地地同时运行脚本内的多个子任务。 由于是在子shell中执行,脚本设置的变量不会影响当前shell。5、makefile文件中的一些说明#CFLAGS: C编译器的选项,无默认值#GCC提供的代码优化功能非常强大,它通过编译选项#-On来控制优化代码的生成,其中n是一个代表优化级别的整数。一般取值范围是从0变化到2或3,其编译速度较慢,占用的内存较多。#-Wall:
13、允许发出gcc提供的所有有用的报警信息#$: 目标文件的完整名称6、实验源码的默认路径WORKDIR="/usr/local/src/EduKit-IV" / EduKit-IV平台的工作路径EXPDIR="/home/example" / 实验生成映像存放路径TFTPDIR="$EXPDIR/tftp" / tftp服务共享目录NFSDIR="$EXPDIR/nfs" / nfs服务共享目录KERNELDIR="$WORKDIR/Mini2410/bsp/linux-2.6.14" / 实验内
14、核包路径VIVIDIR="$WORKDIR/Mini2410/bsp/vivi-0.1.4" / vivi实验源码路径ROOTBASEDIR="$WORKDIR/Mini2410/bsp/rootfs-eduk4-base" / 基础根文件系统路径ROOTTSPDIR="$WORKDIR/Mini2410/bsp/rootfs-eduk4-tsp" / 带TSP的QT文件系统路径ROOTMOUSEDIR="$WORKDIR/Mini2410/bsp/rootfs-eduk4-mouse" /带mouse的QT文件系
15、统路径SIMPLEDIR="$WORKDIR/Mini2410/simple" / 实验例程路径实验指导书中的原代码在:ubuntu8镜像中的:/usr/local/src/EduKit-IV/Mini2410/simple第七章基于Mini2410 的应用程序开发本章节主要介绍嵌入式Linux 应用程序的编写,内容涵盖HelloWorld 入门操作,文件操作,进程和线程控制,计时器实验,tcp、udp 和webserver 实验等,通过本章节的学习,可以了解Linux下基础应用程序的开发方法。7. 1 HelloWorld 运行实验7. 1. 1 实验目的
16、6; 掌握嵌入式应用程序的编写、编译和运行。7. 1. 2 实验设备 硬件:EduKit-IV 嵌入式教学实验平台、Mini2410 核心子板、PC 机; 软件:Windows 2000/NT/XP、Ubuntu 8.04、其他嵌入式软件包。7. 1. 3 实验内容 编写、编译并运行HelloWorld 程序。7. 1. 4 实验步骤 编译:1)单击菜单应用程序->附件->终端,打开终端:图7-1-1 终端运行界面2)在终端中输入以下命令行设置开发所需的环境变量:$ source /usr/local/
17、src/EduKit-IV/Mini2410/set_env_linux.sh$ source /usr/crosstool/gcc-3.4.5-glibc-2.3.6/arm-linux/path.sh3)进入7.1-helloWorld 实验例程目录:$ cd $SIMPLEDIR/7.1-helloworld4)编译并拷贝HelloWorld 程序:$ make$ make install$ make clean并自动拷贝hello 到/home/example/tftp/。 运行:1)准备好EduKit-IV + Mini2410-IV 平台,注意Mini2410-
18、IV 板上的跳线为闭合状态,且确保Mini2410-IV 板载Linux 映像为实验映像(请参照下册开篇:实验环境构建),连接好交叉串口线于板载COM2 和PC 端串口,连接好交叉网线与板载主板网卡接口和PC 端网口。2)在终端执行以下命令打开minicom 串口终端:$ sudo minicom图7-1-2 启动minicom 终端3)给实验平台加电,进入Linux 系统,可以看到minicom 终端的启动打印信息。4)启动完成后,在minicom 下执行以下命令将hello 下载到tmp 目录下:# cd /tmp# tftp -g 90 -r./hello -l
19、./hello附:请用交叉网线连接主机和实验平台,注意实验平台上的网卡是打开的,并核对主机网卡的ip 地址是否为90,设置方法可参照上册第六章节6.5。5)给hello 添加可执行权限:# chmod 777 hello6)运行hello:# ./helloHelloWorld!HelloWorld!HelloWorld!HelloWorld!HelloWorld!7. 1. 5 参考程序#include <stdio.h>/* Function name: main()* Descriptions : printf "HelloWorld&qu
20、ot; on srceen.* Input : NONE* Output : NONE* Created by : Chenxibing* Created Date : 2005-12-29*-* Modified by :* Modified Date:*-*/int main(int argc, char *argv)int i;for (i=0; i<5; i+)printf("HelloWorld!n");return 0;7. 1. 6 练习题利用所学知识自己编写一个Linux 下简单的应用程序。7. 2 文件操作实验7. 2. 1 实验目的
21、6; 学习和掌握文件操作具体过程。7. 2. 2 实验设备 硬件:EduKit-IV 嵌入式教学实验平台、Mini2410 核心子板、PC 机; 软件:Windows 2000/NT/XP、Ubuntu 8.04、其他嵌入式软件包。7. 2. 3 实验内容 利用系统提供的文件操作函数,打开、读写、关闭文件,并创建一个新的文件夹。7. 2. 4 实验原理标准I/O 库以及其他的头文件,提供了一个到底层I/O 系统调用的一个万能接口,这个库并不是ANSI 标准C 的一部分,但是这个库却提供了许多复杂的函数用来处理格式化输出以及描述输入,同
22、时也会小心的处理设备所要求的缓冲区。在许多方式上,可以用使用低层文件描述符的方式来使用这个库。当需要打开文件建立访问路径时,会返回一个值,并会作为一个调用其他I/O 库函数的参数。这个与低层文件描述符等同的被称之类流(stream),并且是作为一个指向结构的指针FILE*来实现的。当一个程序启动时会自动打开三个文件流。他们是stdin,stdout,stderr。这些是在stdio.h 中定义,分别代表标准输入、标准输出和标准错误输出。相对的,他们分别与低层的文件描述符0,1,2 相对应。 fopenfopen库函数是低层的open 系统调用的模拟。我们主要将它用于文件或是
23、终端输入与输出。然而在我们需要显示的控制设备的地方,我们最好是使用低层的系统调用,因为它们可以消除由库所造成的潜在的不良因素,如输入/输出缓冲区。其语法格式如下:#include <stdio.h>FILE *fopen(const char *filename, const char *mode);fopen 打开由filename 参数所指定的文件,并建立一个与其相关的流。mode 参数指出如何来打开这个文件,它可以是下列字符串中的一个:“r”或“rb”:以只读方式打开;“w”或“wb”:以只写方式打开;“a”或“ab”:以读方式打开,添加到文件的结尾处;“r+”或“rb+”或
24、“r+b”:打开更新(读和写);“w+”或“wb+”或“w+b”:打开更新,将其长度变为零;“a+”或“ab+”或“a+b”:打开更新,添加到文件结尾处;b 表明这个文件是二进制文件而不是文本文件。在这里我们要注意,与MS-DOS 不同,Unix 和Linux 并不会在文本文件与二进制文件之间进行区别。Unix 与Linux 将所有文件看成是一样的,尤其是二进制文件。另外要注意的一点就是mode参数必须是一个字符串,而不是一个字符。我们要总是使用“r”,而绝不可以是r。如果函数调用成功,fopen 会返回一个非空的文件指针。如果失败,它会返回NULL,这是在stdio.h中定义的。И
25、766; freadfread 库函数可以用来从一个文件流中读取数据。由stream 流中读取的数据将会放在由prt 所指定的数据缓冲区中。fread 和fwrite 都处理数据记录。这些是由块的尺寸size,读取的次数nitems来指定要传送的记录块的。如果成功则返回值为实际读入到数据缓冲区中的块数,而不是字节数。在文件的结尾处,也许会返回少于nitems 的值,包括零。其语法格式如下:#include <stdio.h>size_t fread(void *ptr, size_t size, size_t nitems, FILE *stream);与所有要写入到缓冲区中的标准
26、I/O 函数一样,程序员要负责分配数据空间以及检查错误。 fwritefwrite 调用一个与fread 相类似的函数接口,它将会从指定的数据区读取数据记录并写入到输出流中。它的返回值为成功写入的记录数。其语法格式如下:#include <stdio.h>size_t fwrite (const void *ptr, size_t size, size_t nitems, FILE *stream);在这里我们要注意就是我们并不推荐在使用结构数据时使用fread 与fwrite。一部分的原因就是因为用fwrite 写入的文件潜在的存在着在不同的机器间不兼容的问题
27、。 fclosefclose 库函数关闭指定的文件流,并将所有未写入的数据写入文件中。使用fclose 是相当重要的,因为stdio 库会缓存数据。如果程序需要确定已经完整的写入了所有的数据,这时就应调用fclose。然而,当一个程序正常结束时,fclose 就自动调用,从而关闭所有仍然打开的文件流。当然,在这样的情况下,我们就没有机会来检查由fclose 报告的错误。与文件描述符所有的限制一样,可用的流数目也是有限制的。实际的限制是FOPEN_MAX,这是在stdio.h 中定义,而且至少为8 个。其语法格式如下:#include <stdio.h>int f
28、close(FILE *stream); mkdir可以使用mkdir 和rmdir 来创建和移除目录。其语法如下:#include <sys/stat.h>int mkdir(const char *path, mode_t mode);mkdir 系统调用可以用来创建目录,而这是与mkdir 程序相等同的。mkdir 以path 为名字创建一个新的目录。目录的权限是由参数mode 来指定的,而这也与open 系统调用中的O_CREAT 的选项是一样的,而且这也是要受到umask 的影响。7. 2. 5 实验步骤 编译:1)单击菜单应用程序
29、->附件->终端,打开终端。2)在终端中输入以下命令行设置开发所需的环境变量:$ source /usr/local/src/EduKit-IV/Mini2410/set_env_linux.sh$ source /usr/crosstool/gcc-3.4.5-glibc-2.3.6/arm-linux/path.sh3)进入file 工作目录:$ cd $SIMPLEDIR/7.2-file4)编译并拷贝File 程序:$ make$ make install$ make clean并自动拷贝file 到/home/example/tftp/。 运行:1)准
30、备好EduKit-IV + Mini2410-IV 平台,注意Mini2410-IV 板上的跳线为闭合状态,且确保Mini2410-IV 板载Linux 映像为实验映像(请参照下册开篇:实验环境构建),连接好交叉串口线于板载COM2 和PC 端串口,连接好交叉网线与板载主板网卡接口和PC 端网口。2)在终端执行以下命令打开minicom 串口终端:$ sudo minicom3)给实验平台加电,进入Linux 系统,可以看到minicom 终端的启动打印信息。4)启动完成后,在minicom 下执行以下命令将file 下载到tmp 目录下:# cd /tmp# tftp -g 192.192.
31、192.190 -r./file -l./file附:请用交叉网线连接主机和实验平台,注意实验平台上的网卡是打开的,并核对主机网卡的ip 地址是否为90,设置方法可参照上册第六章节6.5。5)给file 添加可执行权限:# chmod 777 file6)运行file,并查看文件内容:# ./file17 byte data has written to linux.txtFolder linux created success!# lsfile linux linux.txt# cat linux.txtHello ARM Linux!7. 2. 6 参考程序#in
32、clude <stdio.h>#include <fcntl.h>int main(void)FILE *fp;int num;int folder;char a = "Hello ARM Linux!"fp = fopen("/tmp/Linux.txt","w+"); / 打开文件/tmp/Linux.txtif(fp = NULL)printf("nFail to open Linux.txt!n");exit(-1);num = fwrite(a, sizeof(a), 1, fp)
33、; / 将字符串"Hello ARM Linux!"写入到文件中printf("%d byte data has written to Linux.txtn", num*sizeof(a);folder = mkdir("/tmp/Linux", 1); / 创建目录"/tmp/Linux"if(folder = -1)printf("n Fail to create folder Linux!nIt has existed or the path is error!n");exit(-1);p
34、rintf("Folder Linux created success!n");close(fp); / 关闭文件return 0;7. 2. 7 练习题利用所学的知识自己建立一个目录,在该目录中创建一个文本文件,并对该文本文件进行读写操作。7. 3 进程控制实验7. 3. 1 实验目的 通过实验理解Linux 下进程的概念; 通过该实验掌握进程的编程方法。7. 3. 2 实验设备 硬件:EduKit-IV 嵌入式教学实验平台、Mini2410 核心子板、PC 机; 软件:Windows 200
35、0/NT/XP、Ubuntu 8.04、其他嵌入式软件包。7. 3. 3 实验内容 编写一个程序,父进程创建子进程,并得到相关信息并打印之后退出。7. 3. 4 实验原理1. Linux 进程基础知识进程作为构成系统的基本细胞,不仅是系统内部独立运行的实体,而且是独立竞争资源的基本实体。了解进程的本质,对于理解、描述和设计操作系统有着极为重要的意义。了解进程的活动、状态,也有利于编制复杂程序。首先我们看看进程的定义,进程是一个具有独立功能的程序关于某个数据集合的一次可以并发执行的运行活动,是处于活动状态的计算机程序。1)进程状态和状态转换现在我们来看看,进程在生存周期中的各
36、种状态及状态的转换。下面是Linux 系统的进程状态模型的各种状态: 用户状态:进程在用户状态下运行的状态; 内核状态:进程在内核状态下运行的状态; 内存中就绪:进程没有执行,但处于就绪状态,只要内核调度它,就可以执行; 内存中睡眠:进程正在睡眠并且进程存储在内存中,没有被交换到SWAP 设备; 就绪且换出:进程处于就绪状态,但是必须把它换入内存,内核才能再次调度它进行运行; 睡眠且换出:进程正在睡眠,且被换出内存; 被抢先:进程从内核状态返回用户状态时,
37、内核抢先于它,做了上下文切换,调度了另一个进程。原先这个进程就处于被抢先状态; 创建状态:进程刚被创建。该进程存在,但既不是就绪状态,也不是睡眠状态。这个状态是除了进程0 以外的所有进程的最初状态; 僵死状态(zombie):进程调用exit 结束,进程不再存在,但在进程表项中仍有纪录,该纪录可由父进程收集。现在我们从进程的创建到退出来看看进程的状态转化。需要说明的是,进程在它的生命周期里并不一定要经历所有的状态。首先父进程通过系统调用fork()来创建子进程,调用fork()时,子进程首先处于创建态,fork()调用为子进程配置好内核数据结构和子进程私
38、有数据结构后,子进程就要进入就绪态3 或5,即在内存中就绪,或者因为内存不够,而导致在SWAP 设备中就绪。假设进程在内存中就绪,这时子进程就可以被内核调度程序调度上CPU 运行。内核调度该进程进入内核状态,再由内核状态返回用户状态执行。该进程在用户状态运行一定时间后,又会被调度程序所调度而进入内核状态,由此转入就绪态。有时进程在用户状态运行时,也会因为需要内核服务,使用系统调用而进入内核状态,服务完毕,会由内核状态转回用户状态。要注意的是,进程在从内核状态向用户状态返回时可能被抢占,进入状态7,这是由于有优先级更高的进程急需使用CPU,不能等到下一次调度时机,从而造成抢占。进程还会因为请求的
39、资源不能得到满足,进入睡眠状态,直到它请求的资源被释放,才会被内核唤醒而进入就绪态。如果进程在内存中睡眠时,内存不足,当进程睡眠时间达到一个阀值,进程会被SWAP 出内存,使得进程在SWAP 设备上睡眠。这种状况同样可能发生在就绪的进程上。进程调用exit 系统调用,将使得进程进入内核状态,执行exit 调用,进入僵死状态而结束。以上就是进程状态转换的简单描述。进程的上下文是由用户级上下文、寄存器上下文以及系统级上下文组成。主要内容是该进程用户空间内容、寄存器内容以及与该进程有关的内核数据结构。当系统收到一个中断,执行系统调用或内核做上下文切换时,就会保存进程的上下文。一个进程是在它的上下文中
40、运行的,若要调度进程,就要进行上下文切换。内核在四种情况下允许发生上下文切换: 当进程自己进入睡眠时; 当进程执行完系统调用要返回用户状态,但发现该进程不是最有资格运行的进程时; 当内核完成中断处理后要返回用户状态,但发现该进程不是最有资格运行的进程时; 当进程退出(执行系统调用exit 后)时。有时内核要求必须终止当前的执行,立即从先前保存的上下文处执行。这可由setjmp 和longjmp实现,setjmp 将保存的上下文存入进程自身的数据空间(u 区)中,并继续在当前的上下文中执行,一旦碰到了longjmp,
41、内核就从该进程的u 区,取出先前保存的上下文,并恢复该进程的上下文为原先保存的。这时内核将使得进程从setjmp 处执行,并给setjmp 返回1。进程因等待资源或其他原因,进入睡眠态是通过内核的sleep 算法。该算法与本章后面要讲到的sleep 函数是两个概念。算法sleep 记录进程原先的处理机优先级,置进程为睡眠态,将进程放入睡眠队列,记录睡眠的原因,给该进程进行上下文切换。内核通过算法wakeup 来唤醒进程,如某资源被释放,则唤醒所有因等待该资源而进入睡眠的进程。如果进程睡眠在一个可以接收软中断信号(signal)的级别上,则进程的睡眠可由软中断信号的到来而被唤醒。2)进程控制现在
42、我们开始讲述一下进程的控制,主要介绍内核对fork、exec、wait、exit 的处理过程,为学习这些调用打下概念上的基础,并介绍系统启动(boot)的过程以及进程init 的作用。在Linux 系统中,用户创建一个进程的唯一方法就是使用系统调用fork。内核为完成系统调用fork 要进行几步操作。第一步,为新进程在进程表中分配一个表项。系统对一个用户可以同时运行的进程数是有限制的,对超级用户没有该限制,但也不能超过进程表的最大表项的数目。第二步,给子进程一个唯一的进程标识号(PID)。该进程标识号其实就是该表项在进程表中的索引号。第三步,复制一个父进程的进程表项的副本给子进程。内核初始化子
43、进程的进程表项时,是从父进程处拷贝的。所以子进程拥有与父进程一样的uid、euid、gid、用于计算优先权的nice 值、当前目录、当前根、用户文件描述符表等。第四步,把与父进程相连的文件表和索引节点表的引用数加1。这些文件自动地与该子进程相连。第五步,内核为子进程创建用户级上下文。内核为子进程的u 区及辅助页表分配内存,并复制父进程的区内容。这样生成的是进程的静态部分。第六步,生成进程的动态部分,内核复制父进程的上下文的第一层,即寄存器上下文和内核栈,内核再为子进程虚设一个上下文层,这是为了子进程能“恢复”它的上下文。这时,该调用会对父进程返回子进程的pid,对子进程返回0。Linux 系统
44、的系统调用exit,是进程用来终止执行时调用的。进程发出该调用,内核就会释放该进程所占的资源,释放进程上下文所占的内存空间,保留进程表项,将进程表项中纪录进程状态的字段设为僵死状态。内核在进程收到不可捕捉的信号时,会从内核内部调用exit,使得进程退出。父进程通过wait 得到其子进程的进程表项中纪录的计时数据,并释放进程表项。最后,内核使得进程1(init 进程)接收终止执行的进程的所有子进程。如果有子进程僵死,就向init 进程发出一个SIGCHLD 的软中断信号。一个进程通过调用wait 来与它的子进程同步,如果发出调用的进程没有子进程则返回一个错误,如果找到一个僵死的子进程就取子进程的
45、PID 及退出时提供给父进程的参数。如果有子进程,但没有僵死的子进程,发出调用的进程就睡眠在一个可中断的级别上,直到收到一个子进程僵死(SIGCLD)的信号或其他信号。进程控制的另一个主要内容就是对其他程序引用。该功能是通过系统调用exec 来实现的,该调用将一个可执行的程序文件读入,代替发出调用的进程执行。内核读入程序文件的正文,清除原先进程的数据区,清除原先用户软中断信号处理函数的地址,当exec 调用返回时,进程执行新的正文。一个系统启动的过程,也称作是自举的过程。该过程因机器的不同而有所差异。但该过程的目的对所有机器都相同:将操作系统装入内存并开始执行。计算机先由硬件将引导块的内容读到
46、内存并执行,自举块的程序将内核从文件系统中装入内存,并将控制转入内核的入口,内核开始运行。内核首先初始化它的数据结构,并将根文件系统安装到根“/”,为进程0 形成执行环境。设置好进程0 的环境后,内核便作为进程0 开始执行,并调用系统调用fork。因为这时进程0 运行在内核状态,所以新的进程也运行在内核状态。新的进程(进程1)创建自己的用户级上下文,设置并保存好用户寄存器上下文。这时,进程1 就从内核状态返回用户状态执行从内核拷贝的代码(exec),并调用exec 执行/sbin/init 程序。进程1 通常称为初始化进程,它负责初始化新的进程。进程init 除了产生新的进程外,还负责一些使用
47、户在系统上注册的进程。例如,进程init 一般要产生一些getty 的子进程来监视终端。如果一个终端被打开,getty 子进程就要求在这个终端上执行一个注册的过程,当成功注册后,执行一个shell 程序,来使得用户与系统交互。同时,进程init执行系统调用wait 来监视子进程的死亡,以及由于父进程的退出而产生的孤儿进程的移交。以上是系统启动和进程init 的一个粗略的模型。3)进程调度的概念Linux 系统是一个分时系统,内核给每个进程分一个时间片,该进程的时间片用完就会调度另一个进程执行。Linux 系统上的调度程序属于多级反馈循环调度。该调度方法是,给一个进程分一个时间片,抢先一个运行超
48、过时间片的进程,并把进程反馈到若干优先级队列中的一个队列。进程在执行完之前,要经过这样多次反馈循环。进程调度分成两个部分,一个是调度的时机,即什么时候调度;一个是调度的算法,即如何调度和调度哪个进程。我们先来看看调度的算法,假设目前内核要求进行调度,调度程序从“在内存中就绪”和“被抢先”状态的进程中选择一个优先权最高的进程,如果有若干优先。4)多进程编程在传统的Unix 环境下,有两个基本的操作用于创建和修改进程:函数fork()用来创建一个新的进程,该进程几乎是当前进程的一个完全拷贝;函数族exec()用来启动另外的进程以取代当前运行的进程。Linux 的进程控制和传统的Unix 进程控制基
49、本一致,只在一些细节的地方有些区别,例如在Linux 系统中调用vfork 和fork 完全相同,而在有些版本的Unix 系统中,vfork 调用有不同的功能。由于这些差别几乎不影响我们大多数的编程,在这里我们不予考虑。fork 在英文中是“分叉”的意思。为什么取这个名字呢?因为一个进程在运行中,如果使用了fork,就产生了另一个进程,于是进程就“分叉”了,所以这个名字取得很形象。下面就看看如何具体使用fork,这段程序演示了使用fork 的基本框架:void main()int i;if ( fork() = 0 )/ 子进程程序for ( i = 1; i <1000; i + )
50、printf("This is child processn");else/ 父进程程序for ( i = 1; i <1000; i + ) printf("This is process processn");程序运行后,你就能看到屏幕上交替出现子进程与父进程各打印出的一千条信息了。如果程序还在运行中,你用ps 命令就能看到系统中有两个它在运行了。那么调用这个fork 函数时发生了什么呢?fork 函数启动一个新的进程,前面我们说过,这个进程几乎是当前进程的一个拷贝:子进程和父进程使用相同的代码段;子进程复制父进程的堆栈段和数据段。这样,父进程
51、的所有数据都可以留给子进程。但是,子进程一旦开始运行,虽然它继承了父进程的一切数据,但实际上数据却已经分开,相互之间不再有影响了,也就是说,它们之间不再共享任何数据了。它们再要交互信息时,只有通过进程间通信来实现,这将是我们下面的内容。既然它们如此相象,系统如何来区分它们呢?这是由函数的返回值来决定的。对于父进程,fork 函数返回了子程序的进程号,而对于子程序,fork 函数则返回零。在操作系统中,我们用ps 函数就可以看到不同的进程号,对父进程而言,它的进程号是由比它更低层的系统调用赋予的,而对于子进程而言,它的进程号即是fork 函数对父进程的返回值。在程序设计中,父进程和子进程都要调用
52、函数fork()下面的代码,而我们就是利用fork()函数对父子进程的不同返回值用if.else.语句来实现让父子进程完成不同的功能,正如我们上面举的例子一样。我们看到,上面例子执行时两条信息是交互无规则的打印出来的,这是父子进程独立执行的结果,虽然我们的代码似乎和串行的代码没有什么区别。读者也许会问,如果一个大程序在运行中,它的数据段和堆栈都很大,一次fork 就要复制一次,那么fork 的系统开销不是很大吗?其实UNIX 自有其解决的办法,大家知道,一般CPU 都是以“页”为单位来分配内存空间的,每一个页都是实际物理内存的一个映像,像Intel 的CPU,其一页在通常情况下是4086 字节
53、大小,而无论是数据段还是堆栈段都是由许多“页”构成的,fork 函数复制这两个段,只是“逻辑”上的,并非“物理”上的,也就是说,实际执行fork 时,物理空间上两个进程的数据段和堆栈段都还是共享着的,当有一个进程写了某个数据时,这时两个进程之间的数据才有了区别,系统就将有区别的“页”从物理上也分开。系统在空间上的开销就可以达到最小。下面演示一个足以“搞死”Linux 的小程序,其源代码非常简单:void main()for( ; ; ) fork();这个程序什么也不做,就是死循环地fork,其结果是程序不断产生进程,而这些进程又不断产生新的进程,很快,系统的进程就满了,系统就被这么多不断产生
54、的进程“撑死了”。当然只要系统管理员预先给每个用户设置可运行的最大进程数,这个恶意的程序就完成不了企图了。5)进程间通信首先,进程间通信至少可以通过传送打开文件来实现,不同的进程通过一个或多个文件来传递信息。事实上,在很多应用系统里,都使用了这种方法。但一般说来,进程间通信(IPC:Inter ProcessCommunication)不包括这种似乎比较低级的通信方法。Unix 系统中实现进程间通信的方法很多,而且不幸的是,极少方法能在所有的Unix 系统中进行移植(唯一一种是半双工的管道,这也是最原始的一种通信方式)。而Linux 作为一种新兴的操作系统,几乎支持所有的Unix 下常用的进程
55、间通信方法:管道、消息队列、共享内存、信号量、套接口等等。下面我们将逐一介绍。 管道管道是进程间通信中最古老的方式,它包括无名管道和有名管道两种,前者用于父进程和子进程间的通信,后者用于运行于同一台机器上的任意两个进程间的通信。无名管道由pipe()函数创建:#include <unistd.h>int pipe(int filedes2);参数filedes 返回两个文件描述符:filedes0为读而打开,filedes1为写而打开。filedes1的输出是filedes0的输入。下面的例子示范了如何在父进程和子进程间实现通信。#define INPUT 0#
56、define OUTPUT 1void main()int file_descriptors2;pid_t pid; / 定义子进程号char buf256;int returned_count;pipe(file_descriptors); / 创建无名管道if(pid = fork() = -1) / 创建子进程printf("Error in forkn");exit(1);if(pid = 0) / 执行子进程printf("in the spawned (child) process.n");/ 子进程向父进程写数据,关闭管道的读端close(
57、file_descriptorsINPUT);write(file_descriptorsOUTPUT, "test data", strlen("test data");exit(0);else/ 执行父进程printf("in the spawning (parent) process.n");/ 父进程从管道读取子进程写的数据,关闭管道的写端close(file_descriptorsOUTPUT);returned_count = read(file_descriptorsINPUT, buf, sizeof(buf);pri
58、ntf("%d bytes of data received from spawned process: %sn",returned_count, buf);在Linux 系统下,有名管道可由两种方式创建:命令行方式mknod 系统调用和函数mkfifo。下面的两种途径都在当前目录下生成了一个名为myfifo 的有名管道:方式一:mkfifo("myfifo","rw");方式二:mknod myfifo p。生成了有名管道后,就可以使用一般的文件I/O 函数如open、close、read、write 等来对它进行操作。 消息队列消息队列用于运行于同一台机器上的进程间通信,它和管道很相似。事实上,它是一种正逐渐被淘汰的通信方式,我们可以用流管道或者套接口的方式来取代它。所以,我们对此方式也不再解释,也建议读者忽略这种方式。 共享内存共享内存是运行在同一台机器上的进程间通信最快的方式,因为数据不需要在不同的进程间复制。通常由一个进程创建一块共享内存区,其余进程对这块内存区进行读写。得到共享内存有两种方式:映射/dev/mem 设备和内存映像文件。前一种方式不给系统带来额外的开销,但在现实中并不常用,因为它控制存取的将是实际的物理内存,在Linux 系统下,这只
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2024的广东省室内环境质量保证合同C款(家具购买)
- 2024建筑合同法全文
- 钢结构施工承包合同范本
- 2024个人住房装修合同书协议
- 收藏品赠送合同范本
- 面包店转让协议书模板
- 建筑工程监理服务合同
- 建筑设备出租合同范本
- 普通合伙人合同协议书范文
- 证券交易云平台运营协议
- 第二单元 成长的时空(单元教学设计)-【大单元教学】2024-2025学年六年级道德与法治全一册同步备课系列(统编版2024·五四学制)
- 2024至2030年中国内燃机制造行业发展形势及未来趋势展望研究报告
- 尿素中含氮量的测定
- 生态环境执法大练兵比武竞赛理论备赛试题库(浓缩500题)
- 普法课件:统计法培训
- 《我和鸟类做朋友》(教学设计)-2023-2024学年五年级上册综合实践活动粤教版
- DL∕T 516-2017 电力调度自动化运行管理规程
- 关于合同违约扣款的函件
- NB-T33004-2013电动汽车充换电设施工程施工和竣工验收规范
- 2024版劳动合同合同范本
- 古希腊文明智慧树知到期末考试答案章节答案2024年复旦大学
评论
0/150
提交评论