任务1:
要求:
编写一个进程创建实验程序task51.c,创建如图所示的进程族亲结构,其中p1是程序启动时由加载程序创建第一个进程。各进程的输出信息分别如下:
p1:I am father process
p11: 当前时间是< 年 月 日 时 分 秒>
p12: I am young brother process
p121:我的学号是<您的学号xxx>
p122:我的姓名是<您的姓名xxx>
#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <time.h>
int main()
{
int p11, p12, p121, p122;
struct tm *newtime;
time_t t;
char str[80];
p11 = fork();
if (p11 > 0)
printf("p1:I am fatherprocess\n");
if (p11 == 0)
{
t = time(NULL);
newtime = localtime(&t);
strftime(str, 100, "p11:当前时间是<%Y年%m月%d日%H时%M分%S秒>\n", newtime);
printf("%s", str);
exit(0);
}
else
{
p12 = fork();
if (p12 == 0)
{
printf("p12:I am young brother process\n");
p121 = fork();
if (p121 == 0)
{
printf("p121:我的学号是<201944>\n");
exit(0);
}
else
{
p122 = fork();
if (p122 == 0)
{
printf("p122:我的姓名是<泠霖凛>\n");
exit(0);
}
exit(0);
}
}
exit(0);
}
}
任务2:
要求:
参考教材shellex.c代码,实现一个简单的交互式shell程序task52.c,具备的功能至少有:
(1)打印提示符%;获取用户输入指令;解析指令;寻找命令文件,执行指令
(1)显示命令提示符%;
(2)获取用户输入指令;解析指令;寻找命令文件,执行指令;
(3)前一个命令完成后才继续显示命令提示符%,等待用户输入下一条命令;
(3)如果输入“exit”或“logout”,则退出shell.
(4)允许命令参数间有多个空格,也允许命令前后有空格
(5)支持输出重定向和管道功能。
设计思想:
当输入“Gdup”后,会进入Gdup函数,该函数通过重定向和管道命令传输特定的数据。
源代码:
#include "wrapper.h"
void execute(char *cmdline);
int builtin_command(char **argv);
int parseline(char *buf, char **argv);
void Gdup();
int main()
{
char cmdline[MAXLINE]; /* 命令行缓冲区 */
while (1)
{
printf("%%");
fgets(cmdline, MAXLINE, stdin); /* 读取命令行 */
if (feof(stdin))
exit(0);
execute(cmdline); /* 执行命令 */
}
}
void execute(char *cmdline)
{
char *argv[MAXLINE]; /*execve()参数表 */
char buf[MAXLINE]; /* 保存修改后的命令行 */
int bg; /* 是否在后台执行 */
pid_t pid; /* 子进程PID*/
strcpy(buf, cmdline);
bg = parseline(buf, argv); /* 解析命令行 */
if (argv[0] == NULL)
return; /* 如果第1个参数为空,则忽略命令 */
if (!builtin_command(argv))
{
if ((pid = fork()) == 0)
{ /* 创建子进程 */
if (execvp(argv[0], argv) < 0)
{
printf("%s:Command not found.\n", argv[0]);
exit(0);
}
}
if (!bg)
{ /* 前台执行 */
int status;
if (waitpid(pid, &status, 0) < 0)
perror("waitpid error");
}
else
printf("%d%s", pid, cmdline);
}
return;
}
/* 判断和执行内置命令 */
int builtin_command(char **argv)
{
if (!strcmp(argv[0], "exit")) /* 内置命令exit */
exit(0);
if (!strcmp(argv[0], "logout")) /* 内置命令logout */
exit(0);
if (!strcmp(argv[0], "Gdup"))
{ /* 重定向 */
Gdup();
return 1;
}
if (!strcmp(argv[0], "&")) /* 忽略由&开始的命令串 */
return 1;
return 0; /* 非内置命令 */
}
void Gdup()
{
int fds[2];
const char data[] = "已经得到数据!";
char buf[1024];
pid_t pid;
pipe(fds);
pid = fork();
if (pid == 0)
{
dup(0);
close(0);
dup(fds[0]);
read(fds[0], buf, 1024);
printf("\b%s\n", buf);
close(fds[0]);
close(fds[1]);
}
else
{
close(fds[0]);
write(fds[1], data, strlen(data));
printf("重定向后数据通过管道发送完成!\n");
close(fds[1]);
dup2(4, 0);
}
}
int parseline(char *buf, char **argv)
{
char *delim; /* 指向第1个分隔符 */
int argc; /* 字符串数组args中命令行参数个数 */
int bg; /* 后台作业 */
buf[strlen(buf) - 1] = ' '; /* 用空格替换行末换行符 */
while (*buf && (*buf == ' ')) /* 删除行首空格 */
buf++;
/* 创建 argv数组 */
argc = 0;
while ((delim = strchr(buf, ' ')))
{
argv[argc++] = buf;
*delim = '\0';
buf = delim + 1;
while (*buf && (*buf == ' ')) /* 忽略空格,查找下一个参数起始位置 */
buf++;
}
argv[argc] = NULL;
if (argc == 0) /* 忽略空行*/
return 1;
/* 命令是否应在后台执行 */
if ((bg = (*argv[argc - 1] == '&')) != 0)
argv[--argc] = NULL;
return bg;
}
任务3:
要求:
写一个子进程管理程序task54.c,借鉴sigmask.c方法管理子进程,父进程循环读取用户输入的操作命令,创建子进程、显示相关信息和终止子进程等。具体用户命令为:
1) 命令1:功能是创建一批子进程,格式为“create <进程数>” ,命令执行成功后,显示所有新创建子进程PID。比如“create 10”表示创建10个子进程,子进程执行的代码可以为:“while(1){};exit(100);”
2)命令2:终止一批子进程,格式为“kill <P1> <P2> …”(如“kill 123 456 789”为终止PID号为123、456、789的三个子进程),子进程显示“killed by parent”后终止,父进程通过SIGCHLD信号处理程序等待子进程终止,显示终止的子进程PID。
3)命令3:显示当前子进程列表,命令格式为:“ps -u”
4)命令4:父进程终止命令,格式为“exit”,当所有子进程都结束后,才允许执行该命令。
#include "wrapper.h"
#include <sys/stat.h>
int hashCode;
int fileInfo_daemon(void)
{
pid_t pid;
FILE *fp;
int fd;
struct stat buf;
char rd[4];
int i;
pid = fork(); /*创建新的进程*/
if (pid == -1)
return -1;
else if (pid != 0)
exit(EXIT_SUCCESS);
fp = fopen("data.txt", "r");
if (NULL != fp)
{
int tempHashcode = 0;
while ((fread(rd, sizeof(char), 4, fp)) != 0)
{
for (i = 0; i < sizeof(rd); i++)
tempHashcode += (int)rd[i];
}
printf("Hashcode = %d\n", tempHashcode);
fd = fileno(fp);
fstat(fd, &buf);
int size = buf.st_size; //get file size (byte)
long access_time = buf.st_atime; //latest access time
long modify_time = buf.st_mtime; //latest modification time (seconds passed from 01/01/00:00:00 1970 UTC)
printf("size = %d\n", size);
printf("access_time = %ld\n", access_time);
printf("modify_time = %ld\n", modify_time);
fclose(fp);
return modify_time;
}
printf("function error\n");
return 0;
}
int main(void)
{
while (1)
{
fileInfo_daemon();
sleep(60);
}
return 0;
}
任务4:
要求:
写一个关键目录和关键文件属性与内容监控的daemon程序task5-3.c,该程序每隔1分钟读取目录/etc及其中所有文件的最后访问时间与最后修改时间,读取用户数据库文件/etc的内容,如有变化,则将变化情况如入日志文件,测试程序的正确性。提示:可监控文件内容是否修改,可由文件内容计算一个hash值(又称数字指纹),比较hash值是否发生变化,一种简单的hash值计算方法是将一个文件划分为一系列四字节整数,把各个整数相加的和作为hash值。
#include "wrapper.h"
#include <stdio.h>
#include <string.h>
void execute(char *cmdline); /*执行命令*/
int builtin_command(char **argv); /*检查是否为用户内置命令*/
int parseline(char *buf, char **argv); /*将命令切割成标准格式的若个部分*/
void create(int num); /*创建子进程num个*/
void Exit();
void stop();
void waitBack();
void MyKill(char **argv);
int n;
int mask;
static int count = 0; /*父进程下面的子进程数量,若为0才能调用Exit()函数*/
int main()
{
char cmdline[MAXLINE]; /* 命令行缓冲区 */
signal(SIGCHLD, waitBack);
while (1)
{
printf("%%");
fgets(cmdline, MAXLINE, stdin); /* 读取命令行 */
if (feof(stdin))
exit(0);
execute(cmdline); /* 执行命令 */
//usleep(100);/*每次执行完任务,就停止100微秒*/
}
}
void execute(char *cmdline)
{
char *argv[MAXLINE]; /*execve()参数表 */
char buf[MAXLINE]; /* 保存修改后的命令行 */
pid_t pid; /* 子进程PID*/
strcpy(buf, cmdline);
parseline(buf, argv); /* 解析命令行 */
if (argv[0] == NULL)
return; /* 如果第1个参数为空,则忽略命令 */
if (!builtin_command(argv))
{
if ((pid = fork()) == 0)
{ /* 创建一个临时进程,因为调用execvp后的进程就不会回来了 */
count++;
if (execvp(argv[0], argv) < 0)
{
printf("%s:Command not found.\n", argv[0]);
exit(0); /*立即放弃该临时进程*/
}
}
else
{
usleep(200);
waitpid(pid, NULL, 0);
}
}
}
/* 判断和执行内置命令 */
int builtin_command(char **argv)
{
if (!strcmp(argv[0], "exit"))
{ /* 内置命令exit */
Exit();
return 1;
}
if (!strcmp(argv[0], "create"))
{ /* 内置命令create */
create(atoi(argv[1]));
return 1;
}
if (!strcmp(argv[0], "kill"))
{ /* 内置命令kill */
MyKill(argv);
return 1;
}
if (!strcmp(argv[0], "&")) /* 忽略由&开始的命令串 */
return 1;
return 0; /* 非内置命令 */
}
int parseline(char *buf, char **argv)
{
char *delim; /* 指向第1个分隔符 */
int argc; /* 字符串数组args中命令行参数个数 */
int bg; /* 后台作业 */
buf[strlen(buf) - 1] = ' '; /* 用空格替换行末换行符 */
while (*buf && (*buf == ' ')) /* 删除行首空格 */
buf++;
/* 创建 argv数组 */
argc = 0;
while ((delim = strchr(buf, ' ')))
{
argv[argc++] = buf;
*delim = '\0';
buf = delim + 1;
while (*buf && (*buf == ' ')) /* 忽略空格,查找下一个参数起始位置 */
buf++;
}
argv[argc] = NULL;
n = argc; /*记录命令中出现的参数个数*/
if (argc == 0) /* 忽略空行*/
return 1;
/* 命令是否应在后台执行 */
if ((bg = (*argv[argc - 1] == '&')) != 0)
argv[--argc] = NULL;
return bg;
}
void create(int num)
{ /* 命令1:创建num个子进程 */
int pid, i;
if (num <= 0)
{
return;
}
if ((pid = fork()) > 0)
{
create(num - 1);
count++;
//printf("%d\n",count); //测试
}
else
{
signal(10, stop);
printf("新创建的子进程PID=%d\n", getpid());
mask = 1;
while (mask)
; /*持续循环,等待终止信号10*/
printf("%d killed by parent\n", getpid());
exit(100);
}
}
void MyKill(char **argv)
{
int i = 1;
while (i < n)
{ /*向命令中出现的所有Pid进程发送信号10*/
kill(atoi(argv[i]), 10); /* 10 用户自定义信号,我定义为终止信号*/
i++;
}
//sleep(1); //测试
}
void Exit()
{ /* 命令4 */
if (count <= 0)
exit(0);
else
{
printf("error!还有子进程没有终止\n");
}
}
void stop()
{ /*子进程停止循环*/
mask = 0;
}
void waitBack()
{ /*父进程回收子进程资源,并显示子进程的pid*/
int a;
while ((a = waitpid(-1, NULL, 0)) > 0)
{
printf("终止的子进程PID=%d\n", a);
count--;
//printf("%d\n",count); //测试
}
}