一、编写多进程程序
该实验有3个进程,其中一个为父进程,其余两个是该父进程创建的子进程,其中一个子进程运行“ls -l”指令,另一个子进程在暂停5s之后异常退出,父进程先用阻塞方式等待第一个子进程的结束,然后用非阻塞方式等待另一个子进程的退出,待收集到第二个子进程结束的信息,父进程就返回。
实验流程图:
实验代码:
/* multi_proc_wrong.c */
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
int main(void)
{
pid_t child1, child2, child;
/*创建两个子进程*/
child1 = fork();
child2 = fork();
/*子进程1的出错处理*/
if (child1 == -1)
{
printf("Child1 fork error\n");
exit(1);
}
else if (child1 == 0) /*在子进程1中调用execlp()函数*/
{
printf("In child1: execute 'ls -l'\n");
if (execlp("ls", "ls","-l", NULL)<0)
{
printf("Child1 execlp error\n");
}
}
if (child2 == -1) /*子进程2的出错处理*/
{
printf("Child2 fork error\n");
exit(1);
}
else if( child2 == 0 ) /*在子进程2中使其暂停5s*/
{
printf("In child2: sleep for 5 seconds and then exit\n");
sleep(5);
exit(0);
}
else /*在父进程中等待两个子进程的退出*/
{
printf("In father process:\n");
child = waitpid(child1, NULL, 0); /* 阻塞式等待 */
if (child == child1)
{
printf("Get child1 exit code\n");
}
else
{
printf("Error occured!\n");
}
do
{
child =waitpid(child2, NULL, WNOHANG);/* 非阻塞式等待 */
if (child == 0)
{
printf("The child2 process has not exited!\n");
sleep(1);
}
} while (child == 0);
if (child == child2)
{
printf("Get child2 exit code\n");
}
else
{
printf("Error occured!\n");
}
}
exit(0);
}
运行结果:
In father process:
In child1: execute 'ls -l'
In child2: sleep for 5 seconds and then exit
In child1: execute 'ls -l'
totaltotal 00
Get child1 exit code
The child2 process has not exited!
The child2 process has not exited!
The child2 process has not exited!
The child2 process has not exited!
The child2 process has not exited!
Get child2 exit code
改进代码:
/*multi_proc.c */
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
int main(void)
{
pid_t child1, child2, child;
/*创建两个子进程*/
child1 = fork();
/*子进程1的出错处理*/
if (child1 == -1)
{
printf("Child1 fork error\n");
exit(1);
}
else if (child1 == 0) /*在子进程1中调用execlp()函数*/
{
printf("In child1: execute 'ls -l'\n");
if (execlp("ls", "ls", "-l", NULL) < 0)
{
printf("Child1 execlp error\n");
}
}
else /*在父进程中再创建进程2,然后等待两个子进程的退出*/
{
child2 = fork();
if (child2 == -1) /*子进程2的出错处理*/
{
printf("Child2 fork error\n");
exit(1);
}
else if(child2 == 0) /*在子进程2中使其暂停5s*/
{
printf("In child2: sleep for 5 seconds and then exit\n");
sleep(5);
exit(0);
}
printf("In father process:\n");
child = waitpid(child1, NULL, 0); /* 阻塞式等待 */
if (child == child1)
{
printf("Get child1 exit code\n");
}
else
{
printf("Error occured!\n");
}
do
{
child = waitpid(child2, NULL, WNOHANG ); /* 非阻塞式等待 */
if (child == 0)
{
printf("The child2 process has not exited!\n");
sleep(1);
}
} while (child == 0);
if (child == child2)
{
printf("Get child2 exit code\n");
}
else
{
printf("Error occured!\n");
}
}
exit(0);
}
运行结果:
In father process:
In child2: sleep for 5 seconds and then exit
In child1: execute 'ls -l'
total 0
Get child1 exit code
The child2 process has not exited!
The child2 process has not exited!
The child2 process has not exited!
The child2 process has not exited!
The child2 process has not exited!
Get child2 exit code
比较之前两个代码,可以发现第二个代码在父进程中再创建进程2,然后等待两个子进程的退出,减少了一次child1的执行,提升了代码效率。
得出结论:几个子进程的执行有竞争关系,因此,结果中的顺序是随机的。
一个进程,包括分配给该进程的代码、数据和资源。fork()函数使用一个系统调用来创建一个与原始进程几乎相同的进程,这意味着两个进程可以做完全相同的事情,但如果传入的初始参数或变量不同,也可以做不同的事情。在进程调用fork()函数后,系统首先为新进程分配资源,例如存储数据和代码的空间。然后将原始进程的所有值复制到新进程中,除了一些与原始进程不同的值,即相当于对自己的克隆。
二、编写linux定时器程序
下面的程序实现了如下功能:main函数运行中被定时信号打断,从而转入用户指定的信号处理函数执行。该程序说明在linux也可以通过信号模拟硬件定时器的中断触发机制。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
void sig_handler(int num)
{
printf("receive the signal %d.\n", num);
alarm(2);
}
int main()
{
signal(SIGALRM, sig_handler);
alarm(2);
while(1){
pause();
printf("pause is over.\n");
}
exit(0);
}
运行结果:
Time limit exceeded (wall clock)
三、管道通信实验
mknod命令的功能是用于创建字符及块设备文件,Linux系统中的一切都是文件,在系统与设备通信之前,都会自动在/dev目录中生成对应的设备文件,如若没有自动完成,则需要管理员手动使用mknod命令建立它们。
实验流程图:
运行代码:
/* pipe_select.c*/
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#define FIFO1 "in1"
#define FIFO2 "in2"
#define MAX_BUFFER_SIZE 1024 /* 缓冲区大小*/
#define IN_FILES 3 /* 多路复用输入文件数目*/
#define TIME_DELAY 60 /* 超时值秒数 */
#define MAX(a, b) ((a > b)?(a):(b))
int main(void)
{
int fds[IN_FILES];
char buf[MAX_BUFFER_SIZE];
int i, res, real_read, maxfd;
struct timeval tv;
fd_set inset,tmp_inset;
fds[0] = 0;
/* 创建两个有名管道 */
if (access(FIFO1, F_OK) == -1)
{
if ((mkfifo(FIFO1, 0666) < 0) && (errno != EEXIST))
{
printf("Cannot create fifo file\n");
exit(1);
}
}
if (access(FIFO2, F_OK) == -1)
{
if ((mkfifo(FIFO2, 0666) < 0) && (errno != EEXIST))
{
printf("Cannot create fifo file\n");
exit(1);
}
}
/* 以只读非阻塞方式打开两个管道文件 */
if((fds[1] = open (FIFO1, O_RDONLY|O_NONBLOCK)) < 0)
{
printf("Open in1 error\n");
return 1;
}
if((fds[2] = open (FIFO2, O_RDONLY|O_NONBLOCK)) < 0)
{
printf("Open in2 error\n");
return 1;
}
/*取出两个文件描述符中的较大者*/
maxfd = MAX(MAX(fds[0], fds[1]), fds[2]);
/*初始化读集合inset,并在读文件描述符集合中加入相应的描述集*/
FD_ZERO(&inset);
for (i = 0; i < IN_FILES; i++)
{
FD_SET(fds[i], &inset);
}
FD_SET(0, &inset);
tv.tv_sec = TIME_DELAY;
tv.tv_usec = 0;
/*循环测试该文件描述符是否准备就绪,并调用select()函数对相关文件描述符做相应操作*/
while(FD_ISSET(fds[0],&inset)
|| FD_ISSET(fds[1],&inset) || FD_ISSET(fds[2], &inset))
{
/* 文件描述符集合的备份, 免得每次进行初始化 */
tmp_inset = inset;
res = select(maxfd + 1, &tmp_inset, NULL, NULL, &tv);
switch(res)
{
case -1:
{
printf("Select error\n");
return 1;
}
break;
case 0: /* Timeout */
{
printf("Time out\n");
return 1;
}
break;
default:
{
for (i = 0; i < IN_FILES; i++)
{
if (FD_ISSET(fds[i], &tmp_inset))
{
memset(buf, 0, MAX_BUFFER_SIZE);
real_read = read(fds[i], buf, MAX_BUFFER_SIZE);
if (real_read < 0)
{
if (errno != EAGAIN)
{
return 1;
}
}
else if (!real_read)
{
close(fds[i]);
FD_CLR(fds[i], &inset);
}
else
{
if (i == 0)
{/* 主程序终端控制 */
if ((buf[0] == 'q') || (buf[0] == 'Q'))
{
return 1;
}
}
else
{/* 显示管道输入字符串 */
buf[real_read] = '\0';
printf("%s", buf);
}
}
} /* end of if */
} /* end of for */
}
break;
} /* end of switch */
} /*end of while */
return 0;
}
运行结果:
Time limit exceeded (wall clock)
与实验二结果一致。
四、共享内存实验
该实现要求利用共享内存实现文件的打开和读写操作。
实验流程图:
共享内存缓冲区的数据结构的定义代码如下:
/* shm_com.h */
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#define SHM_BUFF_SZ 2048
struct shm_buff
{
int pid;
char buffer[SHM_BUFF_SZ];
};
以下是“生产者”程序部分:
/* sem_com.h 和 sem_com.c 与“信号量”小节示例中的同名程序相同 */
/* producer.c */
#include "shm_com.h"
#include "sem_com.h"
#include <signal.h>
int ignore_signal(void)
{ /* 忽略一些信号,免得非法退出程序 */
signal(SIGINT, SIG_IGN);
signal(SIGSTOP, SIG_IGN);
signal(SIGQUIT, SIG_IGN);
return 0;
}
int main()
{
void *shared_memory = NULL;
struct shm_buff *shm_buff_inst;
char buffer[BUFSIZ];
int shmid, semid;
/* 定义信号量,用于实现访问共享内存的进程之间的互斥*/
ignore_signal(); /* 防止程序非正常退出 */
semid = semget(ftok(".", 'a'), 1, 0666|IPC_CREAT); /* 创建一个信号量*/
init_sem(semid);/* 初始值为1 */
/* 创建共享内存 */
shmid = shmget(ftok(".", 'b'), sizeof(struct shm_buff), 0666|IPC_CREAT);
if (shmid == -1)
{
perror("shmget failed");
del_sem(semid);
exit(1);
}
/* 将共享内存地址映射到当前进程地址空间 */
shared_memory = shmat(shmid, (void*)0, 0);
if (shared_memory == (void*)-1)
{
perror("shmat");
del_sem(semid);
exit(1);
}
printf("Memory attached at %X\n", (int)shared_memory);
/* 获得共享内存的映射地址 */
shm_buff_inst = (struct shared_use_st *)shared_memory;
do
{
sem_p(semid);
printf("Enter some text to the shared memory(enter 'quit' to exit):");
/* 向共享内存写入数据 */
if (fgets(shm_buff_inst->buffer, SHM_BUFF_SZ, stdin) == NULL)
{
perror("fgets");
sem_v(semid);
break;
}
shm_buff_inst->pid = getpid();
sem_v(semid);
} while(strncmp(shm_buff_inst->buffer, "quit", 4) != 0);
/* 删除信号量 */
del_sem(semid);
/* 删除共享内存到当前进程地址空间中的映射 */
if (shmdt(shared_memory) == 1)
{
perror("shmdt");
exit(1);
}
exit(0);
}
以下是“消费者”程序部分:
/* customer.c */
#include "shm_com.h"
#include "sem_com.h"
int main()
{
void *shared_memory = NULL;
struct shm_buff *shm_buff_inst;
int shmid, semid;
/* 获得信号量 */
semid = semget(ftok(".", 'a'), 1, 0666);
if (semid == -1)
{
perror("Producer is'nt exist");
exit(1);
}
/* 获得共享内存 */
shmid = shmget(ftok(".", 'b'), sizeof(struct shm_buff), 0666|IPC_CREAT);
if (shmid == -1)
{
perror("shmget");
exit(1);
}
/* 将共享内存地址映射到当前进程地址空间 */
shared_memory = shmat(shmid, (void*)0, 0);
if (shared_memory == (void*)-1)
{
perror("shmat");
exit(1);
}
printf("Memory attached at %X\n", (int)shared_memory);
/* 获得共享内存的映射地址 */
shm_buff_inst = (struct shm_buff *)shared_memory;
do
{
sem_p(semid);
printf("Shared memory was written by process %d :%s"
, shm_buff_inst->pid, shm_buff_inst->buffer);
if (strncmp(shm_buff_inst->buffer, "quit", 4) == 0)
{
break;
}
shm_buff_inst->pid = 0;
memset(shm_buff_inst->buffer, 0, SHM_BUFF_SZ);
sem_v(semid);
} while(1);
/* 删除共享内存到当前进程地址空间中的映射 */
if (shmdt(shared_memory) == -1)
{
perror("shmdt");
exit(1);
}
/* 删除共享内存 */
if (shmctl(shmid, IPC_RMID, NULL) == -1)
{
perror("shmctl(IPC_RMID)");
exit(1);
}
exit(0);
}
实验结果:
$./producer
Memory attached at B7F90000
Enter some text to the shared memory(enter 'quit' to exit):First message
Enter some text to the shared memory(enter 'quit' to exit):Second message
Enter some text to the shared memory(enter 'quit' to exit):quit
$./customer
Memory attached at B7FAF000
Shared memory was written by process 3815 :First message
Shared memory was written by process 3815 :Second message
Shared memory was written by process 3815 :quit