题目要求
实现函数mysys,用于执行一个系统命令,要求如下
#include
void mysys(char *command)
{
实现该函数,该函数执行一条命令,并等待该命令执行结束
}
int main()
{
printf("--------------------------------------------------\n");
mysys("echo HELLO WORLD");
printf("--------------------------------------------------\n");
mysys("ls /");
printf("--------------------------------------------------\n");
return 0;
}
--------------------------------------------------
HELLO WORLD
--------------------------------------------------
bin core home lib mnt root snap tmp vmlinuz
boot dev initrd.img lost+found opt run srv usr vmlinuz.old
cdrom etc initrd.img.old media proc sbin sys var
--------------------------------------------------
解决思路
将输入的命令存储在字符串str内,之后传入mysys函数,mysys函数首先判断命令是否合法,如果是空的,则输出error,反之,fork产生子进程,也就是表示pid的变量p为0时,即当前进程为子进程,然后在子进程中完成execl函数的调用,去执行字符串传入的命令,父进程等待子进程完成后退出mysys函数。
实验代码
#include
#include
#include
#include
void mysys(char *str)
{
pid_t p;
if(str != NULL){
p = fork();
if(p == 0){
execl("/bin/sh","sh","-c",str,NULL);
}
wait(NULL);
}
else
{
printf("error\n");
exit(0);
}
}
int main()
{
printf("-----------------------------\n");
mysys("echo HELLO WORLD");
printf("-----------------------------\n");
mysys("ls /");
return 0;
}
运行结果
shiyanlou:~/ $ cc mysys.c -o mysys
shiyanlou:~/ $ ./mysys
--------------------------------------------------
HELLO WORLD
--------------------------------------------------
anaconda3 boot dev home lib64 mnt proc run srv tmp var
bin data etc lib media opt root sbin sys usr
--------------------------------------------------
题目要求
实现shell程序,要求在第2版的基础上,添加如下功能
# 执行sh3
$ ./sh3
# 执行命令cat和wc,使用管道连接cat和wc
> cat /etc/passwd | wc -l
$ cat input.txt
3
2
1
3
2
1
$ cat output.txt
$ cat output.txt
1
2
3
解决思路
对于管道:
父进程调用pipe函数创建管道,得到两个文件描述符fd[0]、fd[1]指向管道的读端和写端。
父进程调用fork创建子进程,那么子进程也有两个文件描述符指向同一管道。
父进程关闭管道读端,子进程关闭管道写端。父进程可以向管道中写入数据,子进程将管道中的数据读出。由于管道是利用环形队列实现的,数据从写端流入管道,从读端流出,这样就实现了进程间通信。
代码思路:首先读取一行指令,进入exec函数执行指令,对指令进行划分判断,根据字符串的不同情况,判断命令参数和重定向内容,运用execl函数执行不同的逻辑操作。在对字符串进行判断时,碰到 ‘>’ 时处理输出重定向,下一单词为output,碰到 ‘<’ 的下一单词为输入重定向,下一单词为input。
实验代码
#include
#include
#include
#include
#include
#include
#include
#define BUFLEN 1024
#define RE_NO 0
#define RE_OUT 1
#define RE_IN 2
#define RE_ALL 3
int getLine(char *buffer, int length) {
char c;
int count = 0;
c = getchar();
while (c != '\n') {
count++;
if (count < length - 1) {
buffer[count - 1] = c;
} else return 0;
c = getchar();
}
buffer[count] = '\0';
return 1;
}
void clearBuf(char *buffer, int length) {
for (int i = 0; i < length; i++) buffer[i] = '\0';
}
int redirctYN(char *str) {
int lef = 0;
int rig = 0;
for (int i = 0; i < strlen(str); i++) {
if (str[i] == '<') lef = 1;
if (str[i] == '>') rig = 1;
}
if (!lef && rig) return RE_OUT;
if (lef && !rig) return RE_IN;
if (lef && rig) return RE_ALL;
return RE_NO;
}
void exec(char *str) {
char buffer[BUFLEN];
char str2[BUFLEN];
strcpy(str2, str);
char path[BUFLEN];
char *p = NULL;
char *name = NULL;
int fd;
pid_t pid;
if (strcmp(str, "")) {
int type = redirctYN(str);
switch (type) {
case RE_NO:
strcpy(buffer, str);
p = strtok(buffer, " ");
if (p) {
if (!strcmp(p, "cd")) {
p = strtok(NULL, "");
if (chdir(p) < 0) printf("error\n");
} else if (!strcmp(p, "pwd")) {
getcwd(path, BUFLEN);
printf("%s\n", path);
} else if (!strcmp(p, "exit")) {
exit(0);
} else {
mysys(str);
}
}
break;
case RE_ALL:
case RE_OUT:
name = outRedDeal(str2);
if (name) {
pid = fork();
if (pid == 0) {
fd = open(name, O_CREAT | O_RDWR, 0666);
dup2(fd, 1);
close(fd);
exec(str2);
exit(0);
} else waitpid(pid, NULL, 0);
}
break;
case RE_IN:
name = inRedDeal(str2);
if (name) {
pid = fork();
if (pid == 0) {
fd = open(name, O_CREAT | O_RDWR, 0666);
dup2(fd, 0);
close(fd);
exec(str2);
exit(0);
} else waitpid(pid, NULL, 0);
}
break;
}
}
}
char *outRedDeal(char *str) {
char *name = (char *) malloc(sizeof(char) * BUFLEN);
int end;
int symbol = getIndex(str, '>');
if (symbol < 0 || symbol == strlen(str) - 1) return NULL;
if (str[symbol + 1] == ' ') {
end = symbol + 2;
for (int i = symbol + 2; i < strlen(str); i++) {
if (str[i] == ' ') break;
end++;
}
strncpy(name, &str[symbol + 2], end - (symbol + 2));
delStr(str, symbol, end);
} else {
end = symbol + 1;
for (int i = symbol + 1; i < strlen(str); i++) {
if (str[i] == ' ') break;
end++;
}
strncpy(name, &str[symbol + 1], end - (symbol + 1));
delStr(str, symbol, end);
}
return name;
}
char *inRedDeal(char *str) {
char *name = (char *) malloc(sizeof(char) * BUFLEN);
int end;
int symbol = getIndex(str, '<');
if (symbol < 0 || symbol == strlen(str) - 1) return NULL;
if (str[symbol + 1] == ' ') {
end = symbol + 2;
for (int i = symbol + 2; i < strlen(str); i++) {
if (str[i] == ' ') break;
end++;
}
strncpy(name, &str[symbol + 2], end - (symbol + 2));
delStr(str, symbol, end);
} else {
end = symbol + 1;
for (int i = symbol + 1; i < strlen(str); i++) {
if (str[i] == ' ') break;
end++;
}
strncpy(name, &str[symbol + 1], end - (symbol + 1));
delStr(str, symbol, end);
}
return name;
}
void delStr(char *str, int start, int end) {
int length = end - start;
if (length > 0) {
for (int i = start; i < strlen(str) - 1; i++) str[i] = str[i + 1];
str[strlen(str) - 1] = '\0';
delStr(str, start, end - 1);
}
}
int getIndex(char *str, char c) {
int symbol = -1;
int i, j, end;
char *name = (char *) malloc(sizeof(char) * BUFLEN);
for (i = 0; i < strlen(str); i++) {
if (str[i] == c) {
symbol = i;
break;
}
}
return symbol;
}
int mysys(char *arg) {
pid_t fpid = fork();
if (fpid < 0) {
return -1;
} else if (fpid == 0) {
if (execl("/bin/sh", "sh", "-c", arg, (char *) 0) < 0) {
return 127;
}
} else {
waitpid(fpid, NULL, 0);
return 0;
}
return 0;
}
char *Split(char *str, int location) {
str[location] = '\0';
return &str[location + 1];
}
int main(int argc, char *argv[]) {
char buffer[BUFLEN];
while (1) {
printf(">");
clearBuf(buffer, BUFLEN);
if (getLine(buffer, BUFLEN)) {
exec(buffer);
} else {
printf("input error\n");
}
}
return 0;
}
运行结果
shiyanlou:~/ $ cc sh3.c -o sh3
shiyanlou:~/ $ ./sh3
>cat /etc/passwd | wc -l
36
>cat input.txt
1 2 3 5 4 3
>cat output.txt
>cat output.txt
1 2 3 5 4 3
题目要求
使用2个线程根据莱布尼兹级数计算PI
解决思路
使用两个线程去计算莱布尼兹级数公式,主线程计算前半部分,辅助线程计算后半部分,最后计算得到的结果为PI/4,将其进行输出。工作线程执行函数worker计算级数的前半段(0n/2+1),主线程执行函数master计算级数的后半段(n/2+1n-1),pthread_join等待子线程结束,得到结果,主函数内total是二者值的和,为最后结果。
本题中n为变量NUMBER,将它定义为150000,计算结果,得到结果PI/4=0.785397,PI = 4×0.785397 = 3.141588,结果正确。当NUMBER越大,得到的结果也就约精确,当然程序耗时也会更长。
实验代码
#include
#include
#include
#define NUMBER 150000
float worker_output=0;
void *worker(void *arg)
{
int i;
float w;
for (i = 0; i < NUMBER / 2+1; i++)
{
w=1.0/(i*2 + 1);
for(int j=0;j<i;j++)
w=-1*w;
worker_output +=w;
}
return NULL;
}
float master_output=0;
void master()
{
int i;
float m;
for (i = NUMBER / 2+1; i < NUMBER; i++)
{
m=1.0/(i*2 + 1);
for(int j=0;j<i;j++)
m=-1*m;
master_output += m;
}
}
int main()
{
pthread_t worker_tid;
float total;
pthread_create(&worker_tid, NULL, worker, NULL);
master();
pthread_join(worker_tid, NULL);
total = master_output + worker_output;
printf(" PI/4 = %f\n", total);
return 0;
}
运行结果
shiyanlou:~/ $ cc pi1.c -o pi1 -lpthread
shiyanlou:~/ $ ./pi1
PI/4 = 0.785397
题目要求
使用N个线程根据莱布尼兹级数计算PI
解决思路
NR_CPU代表线程数,结构体param用来传参,之后利用线程参数传递每个部分的起始值和结束值,每个线程负责NR_TOTAL/NR_CPU长度的计算,结构体result储存线程返回值。
在主线程内开启NR_CPU个线程,每个辅助线程调用compute函数计算对应的的值,利用pthread_join函数接收线程入口返回值result,之后累加得到PI/4。本题中取n为500000,线程数为10,计算结果为0.785398,为PI/4的近似值。
实验代码
#include
#include
#include
#define NR_TOTAL 500000
#define NR_CPU 10
#define NR_CHILD (NR_TOTAL/NR_CPU)
struct param {
int start; //起始值
int end; //结束值
};
struct result {
float sum; //线程返回值
};
void *compute(void *arg)
{
struct param *param;
struct result *result;
float sum = 0;
int i;
param = (struct param *)arg;
for (i = param->start; i < param->end; i++)
if(i%2)
sum -= 1.0/(i*2+1);
else
sum += 1.0/(i*2+1);
result = malloc(sizeof(struct result));
result->sum = sum;
return result;
}
int main()
{
pthread_t workers[NR_CPU];
struct param params[NR_CPU];
int i;
for (i = 0; i < NR_CPU; i++) {
struct param *param;
param = ¶ms[i];
param->start = i * NR_CHILD;
param->end = (i + 1) * NR_CHILD;
pthread_create(&workers[i], NULL, compute, param);
}
float sum = 0;
for (i = 0; i < NR_CPU; i++) {
struct result *result;
pthread_join(workers[i], (void **)&result);
sum += result->sum;
free(result);
}
printf("PI/4 = %f\n", sum);
return 0;
}
运行结果
shiyanlou:~/ $ cc pi2.c -o pi2 -lpthread
shiyanlou:~/ $ ./pi2
PI/4 = 0.785398
题目要求
使用条件变量解决生产者、计算者、消费者问题
解决思路
总共有三个角色,他们的关系是:计算者相当于生产者的消费者,“消费者”相当于计算者的消费者。
1.生产者产生字符后,查看buffer1的锁状态,加锁,之后判断buffer1是否为满,满则阻塞等待,反之放入缓冲区buffer1,之后唤醒等待的计算者线程,解开锁。
2.计算者被唤醒后,获取buffer1的锁,加锁,判断buffer1是否为空,如果空则阻塞等待,反之取出一个字符,转化为大写形式。之后再唤醒生产者,释放锁。接下来,它作为消费者的生产者,查看buffer2的锁状态,加锁,之后判断buffer2是否为满,满则阻塞等待,反之将字符放入缓冲区buffer2,之后唤醒等待的消费者线程,解开锁。
3.消费者被唤醒后,获取buffer2的锁,加锁,判断buffer2是否为空,如果空则阻塞等待,反之取出一个字符,输出到屏幕上,之后唤醒消费者,解开锁。
以上三个进程以此往复,进行工作,产生的结果每次可能是不一样的,因为进程的执行顺序未知。
实验代码
#include
#include
#include
#define CAPACITY 4
int buffer1[CAPACITY];
int buffer2[CAPACITY];
int buffer1_in;
int buffer1_out;
int buffer2_in;
int buffer2_out;
void buffer_init()
{
buffer1_in = 0;
buffer1_out = 0;
buffer2_in = 0;
buffer2_out = 0;
}
int buffer1_is_empty()
{
return buffer1_in == buffer1_out;
}
int buffer1_is_full()
{
return (buffer1_in + 1) % CAPACITY == buffer1_out;
}
int buffer2_is_empty()
{
return buffer2_in == buffer2_out;
}
int buffer2_is_full()
{
return (buffer2_in + 1) % CAPACITY == buffer2_out;
}
int get_buffer1_item()
{
int item;
item = buffer1[buffer1_out];
buffer1_out = (buffer1_out + 1) % CAPACITY;
return item;
}
void put_buffer1_item(int item)
{
buffer1[buffer1_in] = item;
buffer1_in = (buffer1_in + 1) % CAPACITY;
}
int get_buffer2_item()
{
int item;
item = buffer2[buffer2_out];
buffer2_out = (buffer2_out + 1) % CAPACITY;
return item;
}
void put_buffer2_item(int item)
{
buffer2[buffer2_in] = item;
buffer2_in = (buffer2_in + 1) % CAPACITY;
}
pthread_mutex_t mutex1;
pthread_mutex_t mutex2;
pthread_cond_t wait_empty_buffer1;
pthread_cond_t wait_full_buffer1;
pthread_cond_t wait_empty_buffer2;
pthread_cond_t wait_full_buffer2;
#define ITEM_COUNT (CAPACITY * 2)
void *produce(void *arg)
{
int i;
int item;
for (i = 0; i < ITEM_COUNT; i++) {
pthread_mutex_lock(&mutex1);
while (buffer1_is_full())
pthread_cond_wait(&wait_empty_buffer1, &mutex1);
item = 'a'+i;
put_buffer1_item(item);
printf(" produce : %c\n", item);
pthread_cond_signal(&wait_full_buffer1);
pthread_mutex_unlock(&mutex1);
}
return NULL;
}
void *count(void *arg)
{
int i;
int item;
for(i = 0; i < ITEM_COUNT; i++)
{
pthread_mutex_lock(&mutex1);
while(buffer1_is_empty())
pthread_cond_wait(&wait_full_buffer1, &mutex1);
item=get_buffer1_item()-32;
pthread_cond_signal(&wait_empty_buffer1);
pthread_mutex_unlock(&mutex1);
pthread_mutex_lock(&mutex2);
while(buffer2_is_full())
pthread_cond_wait(&wait_empty_buffer2, &mutex2);
put_buffer2_item(item);
pthread_cond_signal(&wait_full_buffer2);
pthread_mutex_unlock(&mutex2);
}
return NULL;
}
void *consume(void *arg)
{
int i;
int item;
for (i = 0; i < ITEM_COUNT; i++) {
pthread_mutex_lock(&mutex2);
while (buffer2_is_empty())
pthread_cond_wait(&wait_full_buffer2, &mutex2);
item = get_buffer2_item();
printf("consume : %c\n", item);
pthread_cond_signal(&wait_empty_buffer2);
pthread_mutex_unlock(&mutex2);
}
return NULL;
}
int main()
{
pthread_t producer;
pthread_t counter;
pthread_t consumer;
buffer_init();
pthread_create(&producer, NULL, produce, NULL);
pthread_create(&producer, NULL, count, NULL);
pthread_create(&consumer, NULL, consume, NULL);
pthread_join(producer, NULL);
pthread_join(counter, NULL);
pthread_join(consumer, NULL);
return 0;
}
运行结果
shiyanlou:~/ $ cc pc1.c -o pc1 -lpthread
shiyanlou:~/ $ ./pc1
produce item: a
produce item: b
produce item: c
produce item: d
produce item: e
consume item: A
consume item: B
consume item: C
produce item: f
consume item: D
consume item: E
produce item: g
consume item: F
produce item: h
consume item: G
consume item: H
题目要求
使用信号量解决生产者、计算者、消费者问题
解决思路
总共有三个角色,他们的关系是:计算者相当于生产者的消费者,“消费者”相当于计算者的消费者,按一定的逻辑执行PV操作。
1.生产者首先执行sema_wait(&empty_buffer_sema1) 函数,访问buffer1的空闲位置。当有空闲位置时, empty_buffer_sema1减1,对buffer1加锁,加入字符后解开锁。再执行sema_signal(&full__buffer_sema1)函数,full_buffer_semal加1,唤起计算者进程。
2.计算者首先执行sema_wait(&full_buffer_sema1) 函数,判断buffer1是否有值,如果等到有值的情况,则full_buffer_sema1减1,加buffer1的锁,取字符转换为大写形式,解开锁,再执行(&empty_buffer_sema1),此时empty_buffer_sema1加1,唤醒等待的生产者线程。之后作为生产者,向buffer2写字符,首先执行sema_wait(&full_buffer_sema2) 函数,判断是否buffer2有值,如果等到有值的情况,则full_buffer_sema2减1,加buffer1的锁,取字符转换为大写形式,解开锁,再执行(&empty_buffer_sema2),此时empty_buffer_sema2加1,唤醒等待的消费者线程。
3.消费者首先执行sema_wait(&full_buffer_sema2) ,判断是否buffer2有值,如果有值,则full_buffer_sema2减1,加buffer2的锁,取字符打印输出,解开锁,再执行sema_signal(&empty_buffer_sema2),此时empty__buffer_sema2加1,唤醒等待的计算者线程。
实验代码
#include
#include
#include
#define CAPACITY 4
int buffer1[CAPACITY];
int buffer2[CAPACITY];
int buffer1_in;
int buffer1_out;
int buffer2_in;
int buffer2_out;
void buffer_init()
{
buffer1_in = 0;
buffer1_out = 0;
buffer2_in = 0;
buffer2_out = 0;
}
int buffer1_is_empty()
{
return buffer1_in == buffer1_out;
}
int buffer1_is_full()
{
return (buffer1_in + 1) % CAPACITY == buffer1_out;
}
int buffer2_is_empty()
{
return buffer2_in == buffer2_out;
}
int buffer2_is_full()
{
return (buffer2_in + 1) % CAPACITY == buffer2_out;
}
int get_buffer1_item()
{
int item;
item = buffer1[buffer1_out];
buffer1_out = (buffer1_out + 1) % CAPACITY;
return item;
}
void put_buffer1_item(int item)
{
buffer1[buffer1_in] = item;
buffer1_in = (buffer1_in + 1) % CAPACITY;
}
int get_buffer2_item()
{
int item;
item = buffer2[buffer2_out];
buffer2_out = (buffer2_out + 1) % CAPACITY;
return item;
}
void put_buffer2_item(int item)
{
buffer2[buffer2_in] = item;
buffer2_in = (buffer2_in + 1) % CAPACITY;
}
typedef struct {
int value;
pthread_mutex_t mutex;
pthread_cond_t cond;
} sema_t;
void sema_init(sema_t *sema, int value)
{
sema->value = value;
pthread_mutex_init(&sema->mutex, NULL);
pthread_cond_init(&sema->cond, NULL);
}
void sema_wait(sema_t *sema)
{
pthread_mutex_lock(&sema->mutex);
while (sema->value <= 0)
pthread_cond_wait(&sema->cond, &sema->mutex);
sema->value--;
pthread_mutex_unlock(&sema->mutex);
}
void sema_signal(sema_t *sema)
{
pthread_mutex_lock(&sema->mutex);
++sema->value;
pthread_cond_signal(&sema->cond);
pthread_mutex_unlock(&sema->mutex);
}
sema_t mutex_sema1;
sema_t empty_buffer_sema1;
sema_t full_buffer_sema1;
sema_t mutex_sema2;
sema_t empty_buffer_sema2;
sema_t full_buffer_sema2;
#define ITEM_COUNT (CAPACITY * 2)
void *produce(void *arg)
{
int i;
int item;
for (i = 0; i < ITEM_COUNT; i++) {
sema_wait(&empty_buffer_sema1);
sema_wait(&mutex_sema1);
item = 'a'+i;
put_buffer1_item(item);
printf(" produce item: %c\n", item);
sema_signal(&mutex_sema1);
sema_signal(&full_buffer_sema1);
}
return NULL;
}
void *count(void *arg)
{
int i;
int item;
for(i = 0; i < ITEM_COUNT; i++)
{
sema_wait(&full_buffer_sema1);
sema_wait(&mutex_sema1);
item=get_buffer1_item()-32;
sema_signal(&mutex_sema1);
sema_signal(&empty_buffer_sema1);
sema_wait(&empty_buffer_sema2);
sema_wait(&mutex_sema2);
put_buffer2_item(item);
sema_signal(&mutex_sema2);
sema_signal(&full_buffer_sema2);
}
return NULL;
}
void *consume(void *arg)
{
int i;
int item;
for (i = 0; i < ITEM_COUNT; i++) {
sema_wait(&full_buffer_sema2);
sema_wait(&mutex_sema2);
item = get_buffer2_item();
printf("consume item: %c\n", item);
sema_signal(&mutex_sema2);
sema_signal(&empty_buffer_sema2);
}
return NULL;
}
int main()
{
pthread_t producer;
pthread_t counter;
pthread_t consumer;
sema_init(&mutex_sema1, 1);
sema_init(&empty_buffer_sema1, CAPACITY - 1);
sema_init(&full_buffer_sema1, 0);
sema_init(&mutex_sema2, 1);
sema_init(&empty_buffer_sema2, CAPACITY - 1);
sema_init(&full_buffer_sema2, 0);
pthread_create(&producer, NULL, produce, NULL);
pthread_create(&producer, NULL, count, NULL);
pthread_create(&consumer, NULL, consume, NULL);
pthread_join(producer, NULL);
pthread_join(counter, NULL);
pthread_join(consumer, NULL);
return 0;
}
运行结果
shiyanlou:~/ $ cc pc2.c -o pc2 -lpthread
shiyanlou:~/ $ ./pc2
produce item: a
produce item: b
produce item: c
consume item: A
consume item: B
consume item: C
produce item: d
produce item: e
produce item: f
consume item: D
consume item: E
consume item: F
produce item: g
produce item: h
consume item: G
consume item: H