实验2-进程的初步认识

更新时间:2023-10-13 15:52:01 阅读量: 综合文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

实验二:进程的初步认识实验

实验学时:2学时 一、

实验目的

(1) 掌握进程的概念

(2) 掌握系统调用

(3)设计程序,实现结果的不可再现性;使用同步原语,实现结果的可再现性 (4)设计程序,对系统的进程数目进行压力测试,对运行时间进行监控 二、实验基本原理 1.简单的系统调用

(1) fork();当fork()函数返回值为0时,子进程成功创建,以下为子进程作用域 头文件:

#include

#include 函数原型:

pid_t fork( void);

(pid_t 是一个宏定义,其实质是int 被定义在#include中)

返回值: 若成功调用一次则返回两个值,子进程返回0,父进程返回子进程ID;否则,出错返回-1

函数说明:

一个现有进程可以调用fork函数创建一个新进程。由fork创建的新进程被称为子进程(child process)。fork函数被调用一次但返回两次。两次返回的唯一区别是子进程中返回0值而父进程中返回子进程ID。

子进程是父进程的副本,它将获得父进程数据空间、堆、栈等资源的副本。注意,子进程持有的是上述存储空间的“副本”,这意味着父子进程间不共享这些存储空间。 linux将复制父进程的地址空间内容给子进程,因此,子进程有了独立的地址空间。

fork()在Linux系统中的返回值是没有NULL的. Error Codes

出错返回错误信息如下: EAGAIN

达到进程数上限.

ENOMEM

没有足够空间给一个新进程分配.

(2)syscall(SYS_getpid);调用系统函数syscall(),返回当前进程号 (3)getpid();调用系统函数getpid(),返回当前进程号 (4)getppid();获得父进程id号 (5)execl( );

头文件:#include

原型:int execl(const char *path, const char *arg, ...);

函数说明:execl()用来执行参数path字符串所代表的文件路径, 接下来的参数代表执行该文件时传递的argv[0],argv[1].....是后一个参数必须用空指针NULL作结束 返回值:成功则不返回值, 失败返回-1,失败原因存于errno中 2.linux的进程同步原语

(1)wait();

函数原型:

#include #include pid_t wait(int *status);

进程一旦调用了wait就立即阻塞自己,由wait自动分析是否当前进程的某个子进程已经退出,如果让它找到了这样一个已经变成僵尸的子进程,wait就会收集这个子进程的信息,并把它彻底销毁后返回;如果没有找到这样一个子进程,wait就会一直阻塞在这里,直到有一个出现为止。

参数status用来保存被收集进程退出时的一些状态,它是一个指向int类型的指针。但如果我们对这个子进程是如何死掉毫不在意,只想把这个僵尸进程消灭掉,(事实上绝大多数情况下,我们都会这样想),我们就可以设定这个参数为NULL,就像下面这样: pid = wait(NULL);

如果成功,wait会返回被收集的子进程的进程ID,如果调用进程没有子进程,调用就会失败,此时wait返回-1,同时errno被置为ECHILD。 (2)#include

#include

key_t ftok (char*pathname, char proj); 它返回与路径pathname相对应的一个键值。

(3)int semget(key_t key, int nsems, int semflg)

参数key是一个键值,由ftok获得,唯一标识一个信号灯集,用法与msgget()中的key相同;参数nsems指定打开或者新创建的信号灯集中将包含信号灯的数目;semflg参数是一些标志位。参数key和semflg的取值,以及何时打开已有信号灯集或者创建一个新的信号灯集与msgget()中的对应部分相同,不再祥述。该调用返回与健值key相对应的信号灯集描述字。调用返回:成功返回信号灯集描述字,否则返回-1。

注:如果key所代表的信号灯已经存在,且semget指定了IPC_CREAT|IPC_EXCL标志,那么即使参数nsems与原来信号灯的数目不等,返回的也是EEXIST错误;如果semget只指定了IPC_CREAT标志,那么参数nsems必须与原来的值一致,在后面程序实例中还要进一步说明。 (4)int semop(int semid, struct sembuf *sops, unsigned nsops);

semid是信号灯集ID,sops指向数组的每一个sembuf结构都刻画一个在特定信号灯上的操作。nsops为sops指向数组的大小。

(5)int semctl(int semid,int semnum,int cmd,union semun arg)

该系统调用实现对信号灯的各种控制操作,参数semid指定信号灯集,参数cmd指定具体的操作类型;参数semnum指定对哪个信号灯操作,只对几个特殊的cmd操作有意义;arg用于设置或返回信号灯信息。

该系统调用详细信息请参见其手册页,这里只给出参数cmd所能指定的操作。 IPC_STAT 获取信号灯信息,信息由arg.buf返回; IPC_SET

设置信号灯信息,待设置信息保存在arg.buf中(在manpage中给出了可以设置哪些信息);

GETALL 返回所有信号灯的值,结果保存在arg.array中,参数sennum被忽略; GETNCNT

返回等待semnum所代表信号灯的值增加的进程数,相当于目前有多少进程在等待semnum代表的信号灯所代表的共享资源;

GETPID 返回最后一个对semnum所代表信号灯执行semop操作的进程ID; GETVAL 返回semnum所代表信号灯的值;

GETZCNT 返回等待semnum所代表信号灯的值变成0的进程数; SETALL

通过arg.array更新所有信号灯的值;同时,更新与本信号集相关的semid_ds结构的sem_ctime成员;

SETVAL 设置semnum所代表信号灯的值为arg.val;

二、

参考程序

0. 创建进程示例 示例代码:

#include //对于此程序而言此头文件用不到 #include #include

int main(int argc, char ** argv ) {

int pid = fork(); if (pid < 0) {

printf(\ }

else if( pid == 0 ) {

printf(\ } else{

printf(\ } return 0; }

1.程序的不可再现性: $cat tc_print.c #include

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

printf(\ sleep(3);

printf(\

return(0); }

$cat ecp1.c

#include #include #include #include void main() {

int *status;

char *f=\要执行的程序名*/

char *argv1[3],*argv2[3],*argv3[3],*argv4[3]; /*执行程序所需的参数*/ argv1[0]=\ argv1[1]=\

argv1[2]=0; /*参数结束的标志*/

argv2[0]=\ argv2[1]=\ argv2[2]=0;

argv3[0]=\ argv3[1]=\ argv3[2]=0;

argv4[0]=\ argv4[1]=\ argv4[2]=0;

if(fork()==0) /*创建进程*/ {

execvp(f,argv1); /*执行程序*/ sleep(1);

execvp(f,argv2); sleep(1); } else {

if(fork()==0) /*创建进程*/ {

execvp(f,argv3); sleep(1);

}

else

{ wait(status); execvp(f,argv3);

sleep(1); } }

printf(\}

2. 实现程序结果的可再现性

(1)用wait()函数实现程序可再现性

#include int n=0;

int status; //改动地点 void pp() {

n++;

if(n>=10)return;

wait(&status); //改动地点 wait系统调用会使父进程阻塞直到一个子进程结束 if(fork()==0) {

printf(\

for(n=0;n<1000000;n++) ; }

else pp(); }

int main() {

int i;

for(i=0;i<3;i++) { pp();

printf(\ } return 0; }

(2)用信号量semget()、semctl()、semop()实现进程同步 #include #include #include #include #include #include #include #include

#define SEMKEY (key_t)0x200

typedef union _senum{ int val;

struct semid_ds *buf; ushort *array; }semun;

static int semid;

struct sembuf p1={0,-1,0};//第一个是索引量,第二个-1是p操作,1是v操作。 struct sembuf v1={0,1,0};

int initsem() /*信号量初始化*/ {

semun x;

x.val=0; //用于付信号量初值

if((semid=semget(SEMKEY,1,0600|IPC_CREAT|IPC_EXCL))==-1)//创建信号量集,1为信号量集的中信号的个数

//1为信号量集中信号量个数 {

if(errno==EEXIST) //已经存在,则获得ID号. semid=semget(SEMKEY,1,0); }

if(semctl(semid,0,SETVAL,x)==-1) 0索引号, 设置初值,x为初值 {

perror(\ return(-1); }

return(semid); }

main() /*主操作*/ {

int i=0,semid; int j;

semid=initsem(); if(fork()==0) {

semop(semid,&p1,1);/*p操作*/ for(j=0;j<1000;j++) {

printf(\ sleep(1); }

printf(\ } else

{

if(fork()==0) {

for(i=0;i<1000;i++) {

printf(\ sleep(1); }

printf(\ semop(semid,&v1,1); } } }

3.创建进可能多的进程,得到这个数目的极限,进程启动后可以进入死循环 #include #include #include #include

#define MAXPRO 50 //此处定义估计最大的进程数,可以设为很大 #define SELLP 10000 int main(void) {

long max=0;

pid_t pid,pids[MAXPRO+1]; long i,l=0;

for (max=1;max

pid=fork(); //不断创建进程 if (pid<0) break; else if (pid>0) {

printf(\ pids[max]=pid; } else {

while(1) {

l++; //进入死循环 } } }

for (i=1;i

{

if(kill(pids[i],SIGKILL)<0) //程序结束时将所有创建的进程强制关闭 {

printf(\ }

else printf(\}

printf(\输出最大值 return 1; }

(3)递归创建200个进程,记录总体创建的起始时间和结束时间,最后求创建单个线程的平均时间

#include #include #include #include #include #include

#define MAXPRO 200 //创建200个进程 #define SELLP 10000 #define TESTTIME 10 long max=0,l=0;

void myfork() //递归函数 {

pid_t pid; int status;

//if (max>=MAXPRO) exit(1); max++;

pid=fork(); //递归创建进程 if (pid<0) exit(1); if (pid>0) {

waitpid(pid,&status,0); //等待子进程结束 if (max<2) return; exit(1); }

if (pid==0) {

if (max>=MAXPRO) exit(1); myfork(); exit(1); } }

int main(void) {

long ave=0; time_t time1;

struct tm *t1,*t2;

pid_t pid,pids[MAXPRO+1]; long i,ti; int status;

struct timeval tv1[TESTTIME],tv2[TESTTIME]; struct timezone tz;

for (l=0;l

max=0;

gettimeofday (&tv1[l],&tz); //记录开始时间 myfork();

gettimeofday (&tv2[l],&tz); //记录结束时间 }

for(l=0;l

printf(\tv1[l].tv_sec,tv1[l].tv_usec);

printf(\is %ds%dus \\n\l+1,tv2[l].tv_sec,tv2[l].tv_usec); ti=(tv2[l].tv_sec-tv1[l].tv_sec)*1000000+(tv2[l].tv_usec-tv1[l].tv_usec); printf(\ave+=ti/MAXPRO;

printf(\}

printf(\return 1; }

本文来源:https://www.bwwdw.com/article/tr4f.html

Top