操作系统实践课作业(南航)

操作系统实践课作业(南航)


实验环境:从Docker Hub中拉取了一个GCC的镜像,并基于该镜像创建了linux系统的容器。

1. job2

1.1 main.c

int min(int a, int b)
{
    
    
	if(a < b)
		return a;
	else
		return b;
}

int max(int a, int b)
{
    
    
	if(a > b)
		return a;
	else
		return b;
}

1.2 math.c

#include<stdio.h>

extern int max(int a, int b);
extern int min(int a, int b);

int main()
{
    
    
	printf("min = %d\n", min(1, 2));
	printf("max = %d\n", max(1, 2));
	return 0;
}

1.3 Makefile

exe:main.o math.o
	cc -o exe main.o math.o

main.o:main.c
	cc -c main.c

math.o:math.c
	cc -c math.c

clean:
	rm exe *.o

2. job3

2.1 myecho.c

实现功能:

  • myecho.c的功能与系统echo程序相同,接受命令行参数,并将参数打印出来
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
    
    
    // 输入的参数必须大于1个
    if (argc == 1)
    {
    
    
        printf("Error!\n");
        exit(0);
    }
    // 便利输入参数数组,argc[0]是指令名称而不是需要打印的内容
    for (int i = 1; i < argc; i++)
    {
    
    
        printf("%s ", argv[i]);
    }
    printf("\n");
}

输出如下:

root@12144f68020b:/os-practice/job3# ./myecho Kint
Kint
root@12144f68020b:/os-practice/job3# ./myecho aviod bananas close
aviod bananas close

2.2 mycat.c

实现功能:

  • mycat.c的功能与系统cat程序相同
  • mycat将指定的文件内容输出到屏幕
  • 要求使用系统调用open/read/write/close实现
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[])
{
    
    
    // 文件描述符,用于打开文件或者关闭文件
    int fd;
    // 参数数组的长度必须为2
    if (argc != 2)
    {
    
    
        printf("There must be only one source path!\n");
        return 0;
    }
    // 文件的路径
    char *path = argv[1];
    // 打开文件
    fd = open(path, O_RDONLY);
    // 判断是否成功打开
    if (fd < 0)
    {
    
    
        printf("Open Error!\n");
    }
    // 缓存文件内容的字符串变量content
    char *content[1024];
    // 读取1024字节文件内容到content变量中
    fd = read(fd, content, 1024);
    if (fd >= 0)
    {
    
    
        printf("%s", content);
    }
    else
    {
    
    
        printf("Read Error!\n");
    }
    // 关闭文件
    close(fd);
    return 0;
}

考虑到读取内容如果大于1024字节的话,该程序会存在一定的问题,故更改后的代码如下:

#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[])
{
    
    
    // 文件描述符,用于打开文件或者关闭文件
    int fd;
    // 参数数组的长度必须为2
    if (argc != 2)
    {
    
    
        printf("There must be only one source path!\n");
        return 0;
    }
    // 文件的路径
    char *path = argv[1];
    // 打开文件
    fd = open(path, O_RDONLY);
    // 判断是否成功打开
    if (fd < 0)
    {
    
    
        printf("Open File Error!\n");
    }
    // 缓存文件内容的字符串变量content
    char *content[128];
    int flag;
    // 每次读取文件中的128字节
    while ((flag = read(fd, content, 128)) > 0)
    {
    
    
        // STDOUT_FILENO是终端的描述符
        write(STDOUT_FILENO, content, flag);
    }
    // 关闭文件
    close(fd);
    return 0;
}

2.3 mycp.c

实现功能:

  • mycp.c的功能与系统cp程序相同
  • 将源文件复制到目标文件
  • 要求使用系统调用open/read/write/close实现
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[])
{
    
    
    int fd;
    if (argc != 3)
    {
    
    
        printf("There must be only one source path and one destination path!\n");
        return 0;
    }
    // 获取源文件路径
    char *path = argv[1];
    fd = open(path, O_RDONLY);
    if (fd < 0)
    {
    
    
        printf("Open Error!\n");
    }
    char *content[1024];
    // 读取源文件内容
    fd = read(fd, content, 1024);
    if (fd >= 0)
    {
    
    
        // 以读写的方式打开或者创建目的文件
        int cp_fd = open(argv[2], O_CREAT | O_RDWR | O_TRUNC);
        // 拷贝源文件的内容到目的文件中
        cp_fd = write(cp_fd, content, fd);
        if (cp_fd < 0)
        {
    
    
            printf("Write Error!\n");
        }
        // 关闭目的文件
        close(cp_fd);
    }
    else
    {
    
    
        printf("Read Error!\n");
    }
    // 关闭源文件
    close(fd);
    return 0;
}

对程序进行修改,修改后的程序如下:

#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[])
{
    
    
    int fd;
    if (argc != 3)
    {
    
    
        printf("There must be only one source path and one destination path!\n");
        return 0;
    }
    // 获取源文件路径
    char *path = argv[1];
    fd = open(path, O_RDONLY);
    if (fd < 0)
    {
    
    
        printf("Open Error!\n");
    }
    char *content[128];
    int flag;
    // 以读写的方式打开或者创建目的文件
    int cp_fd = open(argv[2], O_CREAT | O_RDWR | O_TRUNC);
    // 每次读取文件中的128字节
    while ((flag = read(fd, content, 128)) > 0)
    {
    
    
        // printf("flag=%d\n",flag);
        // printf("%s\n",content);
        // 拷贝源文件的内容到目的文件中
        int cp_flag = write(cp_fd, content, flag);
        if (cp_flag < 0)
        {
    
    
            printf("Write Error!\n");
        }
    }
    // 关闭源文件
    close(fd);
    // 关闭目的文件
    close(cp_fd);
    return 0;
}

2.4 mysys.c

实现功能:

  • mysys的功能与系统函数system相同,要求用进程管理相关系统调用自己实现一遍
  • 使用fork/exec/wait系统调用实现mysys
  • 不能通过调用系统函数system实现mysys
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <string.h>

void mysys(char *command)
{
    
    
    char c[100];
    // 复制命令字符串到变量c中
    strcpy(c, command);
    // printf("%s\n",c);
    pid_t pid;
    // 创建子进程
    pid = fork();
    char *argv[10];
    int i = 0;
    char *s;
    // 子进程中执行以下的代码
    if (pid == 0)
    {
    
    
        // strtok函数以空格为分隔符将字符串分成两部分
        s = strtok(c, " ");
        argv[i] = s;
        while (s != NULL)
        {
    
    
            i++;
            s = strtok(NULL, " ");
            argv[i] = s;
        }
        // argv数组的最后一项必须是NULL指针
        argv[i] = NULL;
        //printf("i = %d\n", i);
        //for(int j=0;j<i;j++)
        //{
    
    
        //      printf("j = %d, s = %s\n",j,argv[j]);
        //}
        //printf("i = %d, s = %s\n",i,argv[i]);
        // 装入程序
        int error = execvp(argv[0], argv);
        if (error < 0)
            perror("execvp");
        printf("End!!!\n");
    }
    // 等待子进程结束再执行wait后面的代码
    wait(NULL);
}
int main()
{
    
    
    printf("---------------------------------------\n");
    mysys("echo HELLO WORLD");
    printf("---------------------------------------\n");
    mysys("ls /");
    printf("---------------------------------------\n");
    mysys("ls");
    printf("---------------------------------------\n");
    return 0;
}

这里我规定了命令长度不超过100字符,健壮性不佳,修改后的代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <string.h>

void mysys(char *command)
{
    
    
    pid_t pid;
    // 指令为空
    if (command == NULL)
    {
    
    
        printf("Error: wrong command!");
        exit(0);
    }
    pid = fork();
    if (pid == 0)
    {
    
    
        int error = execl("/bin/sh", "sh", "-c", command, NULL);
        if (error < 0)
        {
    
    
            perror("execl");
        }
    }
    wait(NULL);
}

int main()
{
    
    
    printf("---------------------------------------\n");
    mysys("echo HELLO WORLD");
    printf("---------------------------------------\n");
    mysys("ls /");
    printf("---------------------------------------\n");
    mysys("ls");
    printf("---------------------------------------\n");
    return 0;
}

输出如下:

root@12144f68020b:/os-practice/job3# ./mysys
---------------------------------------
HELLO WORLD
---------------------------------------
bin  boot  dev  etc  home  lib  lib64  media  mnt  opt  os-practice  proc  root  run  sbin  srv  sys  tmp  usr  var
---------------------------------------
mycat  mycat.c  mycp  mycp.c  myecho  myecho.c  mysys  mysys.c  mysys1  mysys1.c  passwd.bak  sh1  sh1.c
---------------------------------------

2.5 sh1.c

实现功能:

  • 该程序读取用户输入的命令,调用函数mysys(上一个作业)执行用户的命令
  • 实现内置命令cd、pwd、exit
#include <unistd.h>

// 更改当前工作目录,成功返回0 ,失败返回-1
int chdir(const char *path);

// 获取当前工作目录,成功则返回当前工作目录,失败返回FALSE
char *getcwd( char *buffer, int maxlen );
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <string.h>

void mysys(char *command)
{
    
    
    char c[100];
    strcpy(c, command);
    //printf("%s\n",c);
    char *argv[10];
    int i = 0;
    char *s;
    // 解析命令字符串
    s = strtok(c, " ");
    argv[i] = s;
    while (s != NULL)
    {
    
    
        i++;
        s = strtok(NULL, " ");
        argv[i] = s;
    }
    argv[i] = NULL;
    // 需要将命令字符串中的回车去除
    for (int j = 0; j < strlen(argv[i - 1]); j++)
    {
    
    
        if (argv[i - 1][j] == '\n')
        {
    
    
            argv[i - 1][j] = '\0';
            break;
        }
    }
    //for(int j=0;j<i;j++)
    //{
    
    
    //      printf("for j = %d, s = %s\n",j,argv[j]);
    //}
    //printf("after for i = %d, s = %s\n",i,argv[i]);
    // 没有输入命令,忽略
    if (argv[0][0] == '\0')
    {
    
    
        return;
    }
    // 退出指令
    if (strcmp(argv[0], "exit") == 0)
    {
    
    
        exit(0);
    }
    // 进入某个路径
    if (strcmp(argv[0], "cd") == 0)
    {
    
    
        if (chdir(argv[1]))
        {
    
    
            printf("sh1=>cd:no such directory %s\n", argv[1]);
        }
        return;
    }
    // 获取当前目录
    if (strcmp(argv[0], "pwd") == 0)
    {
    
    
        char buf[100];
        printf("%s\n", getcwd(buf, sizeof(buf)));
        return;
    }
    // 除了exit、cd、pwd之外的指令
    pid_t pid;
    // 生成子进程
    pid = fork();
    if (pid == 0)
    {
    
    

        int error = execvp(argv[0], argv);
        if (error < 0)
            perror("execvp");
        printf("End!!!\n");
        return;
    }
    wait(NULL);
}
int main()
{
    
    
    while (1)
    {
    
    
        printf("> ");
        char command[100];
        fgets(command, 100, stdin);
        mysys(command);
    }
    return 0;
}

存在的问题:如果输入的指令格式正确没问题,但是如果在输入错误命令后执行exit不会马上退出。修改后代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <string.h>
extern void mysys(char *command);
// void mysys(char *command)
// {
    
    
//     pid_t pid;
//     // 指令为空
//     if (command == NULL)
//     {
    
    
//         printf("Error: wrong command!");
//         exit(0);
//     }
//     pid = fork();
//     if (pid == 0)
//     {
    
    
//         int error = execl("/bin/sh", "sh", "-c", command, NULL);
//         if (error < 0)
//         {
    
    
//             perror("execl");
//         }
//     }
//     wait(NULL);
// }

int main(int argc, char *argv[])
{
    
    
    while (1)
    {
    
    
        printf("> ");
        // 字符串初始化
        char command[100] = {
    
    0};
        int i = 0;
        char c;
        do
        {
    
    
            // 每次读取一个字符,结束字符为换行符
            c = getchar();
            command[i] = c;
            i++;
        } while (c != '\n');
        // 将换行符改为字符串结束符
        command[i - 1] = '\0';
        // printf("command:%s\n",command);
        // 空指令,进行循环
        if (command[0] == '\0')
        {
    
    
            continue;
        }
        // 获取指令名
        char c1[100];
        strcpy(c1, command);
        char *order = strtok(c1, " ");
        // printf("order:%s\n",order);
        if (!strcmp(order, "exit"))
        {
    
    
            exit(0);
        }
        else if (!strcmp(order, "pwd"))
        {
    
    
            char *path = getcwd(NULL, 0);
            printf("%s\n", path);
            free(path);
        }
        else if (command[0] == 'c' && command[1] == 'd' && command[2] == ' ')
        {
    
    
            char cd_path[100]={
    
    0};
            strcpy(cd_path,command+3);
            if (chdir(cd_path))
            {
    
    
                printf("sh1=>cd:no such directory %s\n", argv[1]);
            }else{
    
    
                perror("cd");
            }
        }else{
    
    
            mysys(command);
        }  
    }
    return 0;
}

调用mysys.c中的mysys函数,首先将其主函数注释掉。需要进行重定位,用extern引用外部函数mysys,并进行静态库链接。

# 将静态库中包含的目标模块先生成可重定位目标文件
cc -c mysys.c
cc -c sh1.c
# 打包编译
cc -o sh1 sh1.o mysys.o

输出如下:

root@12144f68020b:/os-practice/job3# ./sh1
> echo a b c
a b c
> cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
_apt:x:100:65534::/nonexistent:/usr/sbin/nologin
> pwd
/os-practice/job3
> cd ..
> pwd
/os-practice
> exit
root@12144f68020b:/os-practice/job3#

3. job4

3.1 myls.c

  • 功能与系统ls程序相同
// 指向目录
DIR *dp;  
// 指向目录中的对象
struct dirent *dirp; 
 
//DIR结构
struct __dirstream
{
    
    
void *__fd; /* struct hurd_fd pointer for descriptor.   */
char *__data; /* Directory block.   */
int __entry_data; /* Entry number __data corresponds to.   */
char *__ptr; /* Current pointer into the block.   */
int __entry_ptr; /* Entry number __ptr corresponds to.   */
size_t __allocation; /* Space allocated for the block.   */
size_t __size; /* Total valid data in the block.   */
__libc_lock_define (, __lock) /* Mutex lock for this structure.   */
};
typedef struct __dirstream DIR;
 
//dirent结构
struct dirent
{
    
    
   long d_ino; /* inode number 索引节点号 */
   off_t d_off; /* offset to this dirent 在目录文件中的偏移 */
   unsigned short d_reclen; /* length of this d_name 文件名长 */
   unsigned char d_type; /* the type of d_name 文件类型 */
   char d_name [NAME_MAX+1]; /* file name (null-terminated) 文件名,最长255字符 */
}

了解三个函数的功能:

  • opendir(name):用来打开参数name指定的目录,打开成功则返回DIR*形态的目录流,和open()类似,接下来对目录的读取和搜索都要使用此返回值;打开失败则返回NULL。
#include<dirent.h>
// 函数原型
DIR * opendir(const char * name);
  • readdir(dir):返回参数dir目录流的下个目录进入点。
#include<dirent.h>
// 函数原型
struct dirent * readdir(DIR * dir);
  • closedir(dir):关闭参数dir所指的目录流。关闭成功则返回0,失败返回-1。
#include <sys/types.h>
#include <dirent.h>
// 函数原型
int closedir(DIR *dir);

实现代码如下:

#include <stdio.h>
#include <dirent.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>

void myls(DIR *dp)
{
    
    
    //指向目录下对象
    struct dirent *dirp;
    while ((dirp = readdir(dp)) != NULL) //不断读取目录下的内容,指向下一个对象直到为空
    {
    
    
        // 忽略当前目录.和上层目录..
        if (!strcmp(dirp->d_name, ".") || !strcmp(dirp->d_name, ".."))
        {
    
    
            continue;
        }
        // 打印文件名字
        printf("%s  ", dirp->d_name);
    }
    printf("\n");
    // 关闭目录
    closedir(dp);
}

int main(int argc, char *argv[])
{
    
    
    // 指向目录
    DIR *dp;

    if (argc == 1)
    {
    
    
        char path[100];
        // 获取当前地址
        getcwd(path, sizeof(path));
        // 打开目录,指向第一个对象
        dp = opendir(path);
        myls(dp);
    }
    else if (argc == 2)
    {
    
    
        // 只有一个输入路径
        dp = opendir(argv[1]);
        if (dp == NULL)
        {
    
    
            char *str1 = "myls: cannot access '";
            char *str2 = "'";
            char *message = (char *)malloc(strlen(str1) + strlen(str2) + strlen(argv[1]));
            strcpy(message, str1);
            strcat(message, argv[1]);
            strcat(message, str2);
            perror(message);
            exit(1);
        }
        myls(dp);
    }
    else
    {
    
    
        // 多个输入路径
        for (int i = 1; i < argc; i++)
        {
    
    
            printf("%s:\n", argv[i]);
            dp = opendir(argv[i]);
            if (dp == NULL)
            {
    
    
                char *str1 = "myls: cannot access '";
                char *str2 = "'";
                char *message = (char *)malloc(strlen(str1) + strlen(str2) + strlen(argv[i]));
                strcpy(message, str1);
                strcat(message, argv[i]);
                strcat(message, str2);
                // printf("myls: cannot access '%s'", argv[i]);
                perror(message);
                exit(1);
            }
            myls(dp);
            if (i != argc - 1)
            {
    
    
                printf("\n");
            }
        }
    }
    return 0;
}

输出如下:

root@8596f4026c53:/os-practice/job4# ./myls
myls  test  myls.c
root@8596f4026c53:/os-practice/job4# ls
myls  myls.c  test
root@8596f4026c53:/os-practice/job4# ./myls . test
.:
myls  test  myls.c

test:
a  b  c
root@8596f4026c53:/os-practice/job4# ls . test
.:
myls  myls.c  test

test:
a  b  c
root@8596f4026c53:/os-practice/job4#

3.2 mytree.c

  • 功能与系统tree程序相同

思路:

认识stat函数和stat结构体:

  • stat函数:用来获取指定路径的文件或者文件夹的信息。
#include <sys/types.h>    
#include <sys/stat.h>  

// 原型
int stat(const char *filename, struct stat *buf);
// filename是文件或者文件夹的路径,获取的信息保存在buf中
// 正确返回0,错误返回-1
  • stat结构体:文件(夹)信息结构体,可以通过stat函数获取的所有相关信息。
struct stat
{
    
    
    mode_t st_mode; //文件对应的模式,文件,目录等
    ino_t st_ino; // inode节点号
    dev_t st_dev; //设备号码
    dev_t st_rdev; //特殊设备号码
    nlink_t st_nlink; //文件的连接数
    uid_t st_uid; //文件所有者
    gid_t st_gid; //文件所有者对应的组
    off_t st_size; //普通文件,对应的文件字节数
    time_t st_atime; //文件最后被访问的时间
    time_t st_mtime; //文件内容最后被修改的时间
    time_t st_ctime; //文件状态改变时间
    blksize_t st_blksize; //文件内容对应的块大小
    blkcnt_t st_blocks; //文件内容对应的块数量
};

// 主要用到st_mode属性
S_ISREG(st_mode)        // 是否为一般文件
S_ISDIR(st_mode)        // 是否为目录
S_ISLINGK(st_mode)      // 判断是否位符号链接
S_ISCHR(st_mode)        // 是否位字符装置文件
S_ISBLK(s3e)            // 是否先进先出
S_ISSOCK(st_mode)       // 是否为socket
  • 简单例子:
#include <iostream>
#include <ctime>
#include <sys/types.h>
#include <sys/stat.h>
using namespace std;
int main()
{
    
    
    struct stat buf;
    int result;
    result = stat("./Makefile", &buf);
    if (result != 0)
    {
    
    
        perror("Failed");
    }
    else
    {
    
    
        //! 文件的大小,字节为单位
        cout << "size of the file in bytes: " << buf.st_size << endl;
        //! 文件创建的时间
        cout << "time of creation of the file: " << ctime(&buf.st_ctime) << endl;
        //! 最近一次修改的时间
        cout << "time of last modification of the file: " << ctime(&buf.st_mtime) << endl;
        //! 最近一次访问的时间
        cout << "time of last access of the file: " << ctime(&buf.st_atime) << endl;
    }
    return 0;
}

再了解一下sprintf函数的用法:

// 发送格式化输出到 str 所指向的字符串
int sprintf(char *str, const char *format, ...) 

首先输入路径,缺省即获取当前路径。类似DFS深度搜索:

  • 打开输入路径,获取路径下的文件列表,依次打印文件名;
  • 对文件名进行判断,如果是目录类型继续深搜,深度+1,并格式化输出文件名;
  • deep深度参数用于打印缩进;
  • files,dirs变量分别标识文件数和目录数。

代码中并没有使用到stat函数和stat结构体,就当是额外学习了。

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <dirent.h>
#include <stdlib.h>
int dirs = 0, files = 0;
void mytree(char *path, int deep)
{
    
    
    // printf("in mytree function: %s\n",path);
    // 指向目录
    DIR *dp;
    // 指向目录下的对象
    struct dirent *dirp;
    // 子文件夹
    char sub_dir[1024];

    dp = opendir(path);
    if (dp == NULL)
    {
    
    
        char *str1 = " [error opening dir]";
        char *message = (char *)malloc(strlen(str1) + strlen(path));
        strcpy(message, path);
        strcat(message, str1);
        perror(message);
        return ;
    }
    
    //不断读取目录下的内容,指向下一个对象直到为空
    while ((dirp = readdir(dp)) != NULL)
    {
    
    
        // 忽略当前目录.和上层目录..
        if (!strcmp(dirp->d_name, ".") || !strcmp(dirp->d_name, ".."))
        {
    
    
            continue;
        }
        for (int i = 0; i < deep; i++)
        {
    
    
            printf("    ");
        }
        // 如果是一个文件夹,用S_ISDIR(file_info.st_mode)会出错
        if (dirp->d_type == DT_DIR)
        {
    
    
            // 文件夹数增加
            dirs++;
            printf("|-- %s\n", dirp->d_name);
            sprintf(sub_dir, "%s/%s", path, dirp->d_name);
            // 深度搜索
            mytree(sub_dir, deep + 1);
        }
        else
        {
    
    
            // 如果是普通文件,文件数增加
            files++;
            printf("|-- %s\n", dirp->d_name);
        }
    }
    closedir(dp);
}
int main(int argc, char *argv[])
{
    
    
    if (argc == 1)
    {
    
    
        // 当前路径
        char path[1024];
        getcwd(path, sizeof(path));
        // printf("getcwd:%s\n",path);
        printf(".\n");
        mytree(path, 0);
    }
    else
    {
    
    
        // 一个或多个路径
        for (int i = 1; i < argc; i++)
        {
    
    
            printf("%s\n", argv[i]);
            mytree(argv[i], 0);
        }
    }
    // 打印文件夹数和文件数
    printf("\n%d directories, %d files.\n", dirs, files);
    return 0;
}

输出如下:

# mytree和tree指令缺省输出对比
root@8596f4026c53:/os-practice/job4# ./mytree
.
|-- myls
|-- mytree
|-- test
    |-- a
    |-- b
        |-- z
        |-- y
        |-- x
    |-- c
|-- myls.c
|-- mytree.c

2 directories, 9 files.
root@8596f4026c53:/os-practice/job4# tree
.
|-- myls
|-- myls.c
|-- mytree
|-- mytree.c
`-- test
    |-- a
    |-- b
    |   |-- x
    |   |-- y
    |   `-- z
    `-- c

2 directories, 9 files

# mytree和tree后接多个路径的输出对比
root@8596f4026c53:/os-practice/job4# ./mytree . test
.
|-- myls
|-- mytree
|-- test
    |-- a
    |-- b
        |-- z
        |-- y
        |-- x
    |-- c
|-- myls.c
|-- mytree.c
test
|-- a
|-- b
    |-- z
    |-- y
    |-- x
|-- c

3 directories, 14 files.
root@8596f4026c53:/os-practice/job4# tree . test
.
|-- myls
|-- myls.c
|-- mytree
|-- mytree.c
`-- test
    |-- a
    |-- b
    |   |-- x
    |   |-- y
    |   `-- z
    `-- c
test
|-- a
|-- b
|   |-- x
|   |-- y
|   `-- z
`-- c

3 directories, 14 files

4. job5

4.1 sh2.c

  • mian函数中的循环中会处理空指令的情况

  • mysh2函数中实现了cd、pwd和exit指令功能。

  • check函数的功能是对指令进行检查是否需要重定向。

  • redirecth函数根据不同的情况进行重定向。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
// strtok函数在string.h头文件中 
#include <string.h>

int redirect(int i, char command[], int flag)
{
    
    
    // 提取文件名
    char file[100];
    int k = 0, j, fd;
    for (j = i; command[j] != ' ' && command[j] != '\0'; j++)
    {
    
    
        file[k++] = command[j];
    }
    file[k] = 0;
    // 根据不同的情况进行重定向
    if (flag == 0)
    {
    
    
        fd = open(file, O_CREAT | O_RDWR, 0666);
        dup2(fd, 1);
    }
    else if (flag == 1)
    {
    
    
        fd = open(file, O_CREAT | O_RDWR, 0666);
        dup2(fd, 0);
    }
    else if (flag == 2)
    {
    
    
        fd = open(file, O_CREAT | O_APPEND, 0666);
        dup2(fd, 1);
    }
    close(fd);
    return j - 1;
}

void check(char *command)
{
    
    
    for (int i = 0; command[i] != '\0'; i++)
    {
    
    
        // >file
        if (command[i] == '>' && command[i + 1] != ' ' && command[i + 1] != '>')
        {
    
    
            i = redirect(i + 1, command, 0);
        }
        // > file
        else if (command[i] == '>' && command[i + 1] == ' ')
        {
    
    
            i = redirect(i + 2, command, 0);
        }
        // <file
        else if (command[i] == '<' && command[i + 1] != ' ')
        {
    
    
            i = redirect(i + 1, command, 1);
        }
        // < file
        else if (command[i] == '<' && command[i + 1] == ' ')
        {
    
    
            i = redirect(i + 2, command, 1);
        }
        // >>file
        else if (command[i] == '>' && command[i + 1] == '>' && command[i + 1] != ' ')
        {
    
    
            i = redirect(i + 2, command, 2);
        }
        // >> file
        else if (command[i] == '>' && command[i + 1] == '>' && command[i + 2] == ' ')
        {
    
    
            i = redirect(i + 3, command, 2);
        }
    }
    int error = execl("/bin/sh", "sh", "-c", command, NULL);
    if (error < 0)
    {
    
    
        perror("execl");
    }
}

void mysh2(char *command)
{
    
    
    // 获取指令名
    char c1[100];
    strcpy(c1, command);
    char *order = strtok(c1, " ");
    // printf("order:%s\n",order);
    if (!strcmp(order, "exit"))
    {
    
    
        // exit指令
        exit(0);
    }
    else if (!strcmp(order, "pwd"))
    {
    
    
        // pwd指令
        char *path = getcwd(NULL, 0);
        printf("%s\n", path);
        free(path);
    }
    else if (command[0] == 'c' && command[1] == 'd' && command[2] == ' ')
    {
    
    
        // cd指令
        char cd_path[100] = {
    
    0};
        strcpy(cd_path, command + 3);
        if (chdir(cd_path) == -1)
        {
    
    
            printf("sh1: cd: %s No such file or directory\n", cd_path);
        }
    }
    else
    {
    
    
        // 其他指令
        pid_t pid;
        pid = fork();
        if (pid == 0)
        {
    
    
            // 这里要对指令进行检查,是否需要重定向
            check(command);
        }
        wait(NULL);
    }
}

int main(int argc, char *argv[])
{
    
    
    while (1)
    {
    
    
        printf("> ");
        // 字符串初始化
        char command[100] = {
    
    0};
        int i = 0;
        char c;
        do
        {
    
    
            // 每次读取一个字符,结束字符为换行符
            c = getchar();
            command[i] = c;
            i++;
        } while (c != '\n');
        // 将换行符改为字符串结束符
        command[i - 1] = '\0';
        // printf("command:%s\n",command);
        // 空指令,跳过循环
        if (command[0] == '\0')
        {
    
    
            continue;
        }
        mysh2(command);
    }
    return 0;
}

需要注意的是strtok的函数使用,下面对字符串的写法会导致内存错误,因为在C语言中字符串常量不可以修改。

char *line="echo abc xyz >log"
char *word;
word=strtok(line," ")

修改可以这样写。

char line[]="echo abc xyz >log";
// 等价于
int len=strlen("echo abc xyz >log")+1;
char line[len+1];
strcpy(line,"echo abc xyz >log");

strtok的标准用法,

word=strtok(line," ");
while(word!=NULL){
    
    
    printf("[%s]\n",word);
    word=strtok(NULL," ");
}

5. job7

5.1 pi1.c

使用2个线程根据莱布尼兹级数计算PI:

  • 莱布尼兹级数公式: 1 - 1/3 + 1/5 - 1/7 + 1/9 - … = PI/4
  • 主线程创建1个辅助线程
  • 主线程计算级数的前半部分
  • 辅助线程计算级数的后半部分
  • 主线程等待辅助线程运行結束后,将前半部分和后半部分相加
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>

#define HALF 300

double worker_output;
double master_output;

void *worker(void *arg)
{
    
    
    double j;
    worker_output = 0;
    for (int i = 1; i <= HALF; i++)
    {
    
    
        // 将int型变量i转换为double型变量j,不然计算除法的结果只能取整
        j = i;
        if (i % 2 == 0)
            worker_output -= 1 / (2 * j - 1);
        else
            worker_output += 1 / (2 * j - 1);
    }
    return NULL;
}

void master()
{
    
    
    double j;
    master_output = 0;
    for (int i = HALF + 1; i <= 2 * HALF; i++)
    {
    
    
        j = i;
        if (i % 2 == 0)
            master_output -= 1 / (2 * j - 1);
        else
            master_output += 1 / (2 * j - 1);
    }
}

int main()
{
    
    
    pthread_t worker_tid;

    pthread_create(&worker_tid, NULL, worker, NULL);
    master();
    pthread_join(worker_tid, NULL);
    double PI = (worker_output + master_output) * 4;
    printf("master_output = %f, worker_output = %f, PI= %f\n", master_output, worker_output, PI);
    return 0;
}

5.2 pi2.c

使用N个线程根据莱布尼兹级数计算PI:

  • 与上一题类似,但本题更加通用化,能适应N个核心
  • 主线程创建N个辅助线程
  • 每个辅助线程计算一部分任务,并将结果返回
  • 主线程等待N个辅助线程运行结束,将所有辅助线程的结果累加
  • 本题要求 1: 使用线程参数,消除程序中的代码重复
  • 本题要求 2: 不能使用全局变量存储线程返回值
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <stdlib.h>

#define NR_TOTAL 600             
#define NR_CPU 20                  
#define NR_SIZE (NR_TOTAL / NR_CPU) 

double PI;

struct parameter
{
    
    
    int start;
    int end;
};

struct result
{
    
    
    double sum;
};

void *compute(void *arg)
{
    
    
    struct parameter *param = (struct parameter *)arg;
    struct result *result;
    double output;
    double j;
    for (int i = param->start; i < param->end; i++)
    {
    
    
        // 将int型变量i转换为double型变量j,不然计算除法的结果只能取整
        j = i;
        if (i % 2 == 0)
        {
    
    
            output -= 1 / (2 * j - 1);
        }
        else
        {
    
    
            output += 1 / (2 * j - 1);
        }
    }
    result = malloc(sizeof(struct result));
    result->sum = output;
}

int main()
{
    
    
    pthread_t workers[NR_CPU];
    struct parameter params[NR_CPU];

    for (int i = 0; i < NR_CPU; i++)
    {
    
    
        struct parameter *param;
        param = &params[i];
        // 从1开始
        param->start = i * NR_SIZE + 1;
        param->end = (i + 1) * NR_SIZE + 1;
        pthread_create(&workers[i], NULL, compute, param);
    }

    double sum = 0;
    for (int i = 0; i < NR_CPU; i++)
    {
    
    
        struct result *result;
        pthread_join(workers[i], (void **)&result);
        sum += result->sum;
        free(result);
    }
    
    PI = 4 * sum;
    printf("PI = %f\n", PI);
    return 0;
}

5.3 sort.c

多线程排序:

  • 主线程创建两个辅助线程
  • 辅助线程1使用选择排序算法对数组的前半部分排序
  • 辅助线程2使用选择排序算法对数组的后半部分排序
  • 主线程等待辅助线程运行結束后,使用归并排序算法归并子线程的计算结果
  • 本题要求 1: 使用线程参数,消除程序中的代码重复
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

int array[10] = {
    
    41, 67, 34, 58, 69, 24, 78, 58, 62, 64};
int sorted[10];

#define NR_TOTAL 10                 // round count
#define NR_CPU 2                    // thread num
#define NR_SIZE (NR_TOTAL / NR_CPU) // one compute

struct parameter
{
    
    
    int *array;
    int start;
    int end;
};

void *sort(void *arg)
{
    
    
    // receive parameter and switch the type
    struct parameter *param = (struct parameter *)arg;
    int tmp;
    int min;
    for (int i = param->start; i < param->end; i++)
    {
    
    
        min = i;
        for (int j = i + 1; j < param->end; j++)
        {
    
    
            if (param->array[min] > param->array[j])
                min = j;
        }
        if (min != i)
        {
    
    
            tmp = param->array[i];
            param->array[i] = param->array[min];
            param->array[min] = tmp;
        }
    }

    return 0;
}

void Merge(int *array)
{
    
    
    int i = 0, j = NR_TOTAL / 2, k = 0;
    while (i < NR_TOTAL / 2 && j < NR_TOTAL)
    {
    
    
        if (array[i] < array[j])
            sorted[k++] = array[i++];
        else
            sorted[k++] = array[j++];
    }
    while (i < NR_TOTAL / 2)
        sorted[k++] = array[i++];
    while (j < NR_TOTAL)
        sorted[k++] = array[j++];
}

int main()
{
    
    
    pthread_t workers[NR_CPU];
    struct parameter params[NR_CPU];

    struct parameter *param;
    for (int i = 0; i < NR_CPU; i++)
    {
    
    
        param = &params[i];
        param->array = array;
        param->start = i * NR_SIZE;
        param->end = (i + 1) * NR_SIZE;
        // 创建子线程
        pthread_create(&workers[i], NULL, sort, param);
    }

    for (int i = 0; i < NR_CPU; i++)
    {
    
    
        pthread_join(workers[i], NULL);
    }
    Merge(array);
    for (int i = 0; i < NR_TOTAL; i++)
        printf("%d ", sorted[i]);
    printf("\n");
    return 0;
}

6. job8

6.1 pc.c

使用条件变量解决生产者、计算者、消费者问题

+ 系统中有3个线程:生产者、计算者、消费者
+ 系统中有2个容量为4的缓冲区:buffer1、buffer2
+ 生产者
  - 生产'a''b''c'、‘d'、'e'、'f'、'g'、'h'八个字符
  - 放入到buffer1
  - 打印生产的字符
+ 计算者
  - 从buffer1取出字符
  - 将小写字符转换为大写字符,按照 input:OUTPUT 的格式打印
  - 放入到buffer2
+ 消费者
  - 从buffer2取出字符
  - 打印取出的字符
+ 程序输出结果(实际输出结果是交织的)
a
b
c
...
    a:A
    b:B
    c:C
    ...
        A
        B
        C
        ..

设置两个缓冲区buffer1和buffer2,同时对两个缓冲区设置互斥变量mutex1和mutex2,以及条件变量wait_empty_buffer1、wait_full_buffer1、wait_empty_buffer2、wait_full_buffer2。

计算者线程需要对buffer1和buffer2进行操作,前半部分参考消费者,后半部分参考生产者。

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>

#define CAPACITY 4
int buffer1[CAPACITY];
int buffer2[CAPACITY];
int in1, in2;
int out1, out2;
// 判断缓冲区是否为空
int buffer_is_empty(int *in, int *out)
{
    
    
    return *in == *out;
}
// 判断缓冲区是否为满
int buffer_is_full(int *in, int *out)
{
    
    
    return (*in + 1) % CAPACITY == *out;
}
// 根据type的取值对不同缓冲区进行写入/读出操作
int get_item(int *out, int type)
{
    
    
    int item;
    if (type == 1)
    {
    
    
        item = buffer1[*out];
    }
    else
    {
    
    
        item = buffer2[*out];
    }
    *out = (*out + 1) % CAPACITY;
    return item;
}
void put_item(int item, int *in, int type)
{
    
    
    if (type == 1)
    {
    
    
        buffer1[*in] = item;
    }
    else
    {
    
    
        buffer2[*in] = item;
    }
    *in = (*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)

// 生产者进程向buffer1写入数据
void *produce(void *arg)
{
    
    
    int i, item, type = 1;
    for (i = 0; i < ITEM_COUNT; i++)
    {
    
    
        pthread_mutex_lock(&mutex1);
        while (buffer_is_full(&in1, &out1))
            pthread_cond_wait(&wait_empty_buffer1, &mutex1);
        item = 'a' + i;
        put_item(item, &in1, type);
        printf("produce item: %c\n", item);

        pthread_cond_signal(&wait_full_buffer1);
        pthread_mutex_unlock(&mutex1);
    }
    return NULL;
}
// 计算者进程从buffer1中读出数据,写入到buffer2中
void *compute(void *arg)
{
    
    
    int i, item, type;
    for (i = 0; i < ITEM_COUNT; i++)
    {
    
    
        type = 1;
        pthread_mutex_lock(&mutex1);
        while (buffer_is_empty(&in1, &out1))
            pthread_cond_wait(&wait_full_buffer1, &mutex1);
        item = get_item(&out1, type);
        // printf("	compute item: %c:",item);
        pthread_cond_signal(&wait_empty_buffer1);
        pthread_mutex_unlock(&mutex1);

        type = 2;
        pthread_mutex_lock(&mutex2);
        while (buffer_is_full(&in2, &out2))
            pthread_cond_wait(&wait_empty_buffer2, &mutex2);
        item -= 32;
        put_item(item, &in2, type);
        printf("\tcompute item: %c:%c\n", item + 32, item);
        pthread_cond_signal(&wait_full_buffer2);
        pthread_mutex_unlock(&mutex2);
    }
}
// 消费者进程
void *consume(void *arg)
{
    
    
    int i, item, type = 2;
    for (i = 0; i < ITEM_COUNT; i++)
    {
    
    
        pthread_mutex_lock(&mutex2);
        while (buffer_is_empty(&in2, &out2))
            pthread_cond_wait(&wait_full_buffer2, &mutex2);
        item = get_item(&out2, type);
        printf("\t\tconsume item: %c\n", item);
        pthread_cond_signal(&wait_empty_buffer2);
        pthread_mutex_unlock(&mutex2);
    }
}

int main()
{
    
    
    pthread_t compute_tid;
    pthread_t consume_tid;

    // 初始化mutex变量和条件变量
    pthread_mutex_init(&mutex1, NULL);
    pthread_mutex_init(&mutex2, NULL);
    pthread_cond_init(&wait_empty_buffer1, NULL);
    pthread_cond_init(&wait_full_buffer1, NULL);
    pthread_cond_init(&wait_empty_buffer2, NULL);
    pthread_cond_init(&wait_full_buffer2, NULL);

    // 创建消费者线程和计算者线程
    pthread_create(&compute_tid, NULL, compute, NULL);
    pthread_create(&consume_tid, NULL, consume, NULL);
    // 主线程作为生产者,执行produce函数
    produce(NULL);
    // 等待消费者线程和计算者线程结束
    pthread_join(consume_tid, NULL);
    pthread_join(compute_tid, NULL);
    return 0;
}

6.2 pp.c

使用条件变量实现 ping-pong 问题

+ 系统中有2个线程:ping 线程和 pong 线程
+ ping 线程先执行
+ ping 线程执行流程如下
  1. 打印输出 ping
  2. 等待 pong 线程输出
  3. 执行第 1+ pong 线程执行流程如下
  1. 打印输出 pong
  2. 等待 ping 线程输出
  3. 执行第 1+ 程序输出结果
  ping
  pong
  ping
  pong
  ...

定义一个状态变量status,为1表示ping进程正在运行,为0表示pong进程正在运行。ping线程和pong线程是一个相互协作的关系,ping线程执行之后,才执行pong线程,反之也是如此。

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
// status为1表示ping进程正在运行,为0表示pong进程正在运行
int status = 1;
#define COUNT 10
pthread_mutex_t mutex;
pthread_cond_t wait_ping;
pthread_cond_t wait_pong;

void *ping(void *arg)
{
    
    
    int i = COUNT;
    while (i--)
    {
    
    
        pthread_mutex_lock(&mutex);
        while (status == 0)
            pthread_cond_wait(&wait_pong, &mutex);
        printf("ping\n");
        p = 0;
        pthread_cond_signal(&wait_ping);
        pthread_mutex_unlock(&mutex);
    }
    return NULL;
}
void *pong(void *arg)
{
    
    
    int i = COUNT;
    while (i--)
    {
    
    
        pthread_mutex_lock(&mutex);
        while (status == 1)
            pthread_cond_wait(&wait_ping, &mutex);
        printf("pong\n");
        p = 1;
        pthread_cond_signal(&wait_pong);
        pthread_mutex_unlock(&mutex);
    }
    return NULL;
}
int main()
{
    
    
    pthread_t pong_tid;

    // 初始化互斥变量和条件变量
    pthread_mutex_init(&mutex, NULL);
    pthread_cond_init(&wait_ping, NULL);
    pthread_cond_init(&wait_pong, NULL);

    // 创建pong线程
    pthread_create(&pong_tid, NULL, pong, NULL);
    // 主线程为ping线程
    ping(NULL);
    // 等待pong线程结束
    pthread_join(pong_tid, NULL);
    return 0;
}

7. job9

7.1 pc.c

使用信号量解决生产者、计算者、消费者问题,功能同6.1

同步关系:

  • 生产者、计算者共享一个初始为空、大小为n的缓冲区1。
  • 计算者、消费者共享一个初始为空、大小为n的缓冲区2。
  • 只有缓冲区没满时,生产者(计算者)才能把产品放入缓冲区,否则必须等待。
  • 只有缓冲区不空时,消费者(计算者)才能从中取出产品,否则必须等待。

互斥关系:

  • 缓冲区是临界资源,各进程必须互斥地访问。
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

#define CAPACITY 4
int buffer1[CAPACITY];
int buffer2[CAPACITY];
int in1, in2;
int out1, out2;
int get_item(int *out, int type)
{
    
    
    int item;
    if (type == 1)
        item = buffer1[*out];
    else
        item = buffer2[*out];
    *out = (*out + 1) % CAPACITY;
    return item;
}
void put_item(int item, int *in, int type)
{
    
    
    if (type == 1)
        buffer1[*in] = item;
    else
        buffer2[*in] = item;
    *in = (*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 mutex_sema2;
sema_t empty_buffer1_sema;
sema_t empty_buffer2_sema;
sema_t full_buffer1_sema;
sema_t full_buffer2_sema;

#define ITEM_COUNT (CAPACITY * 2)
// 生产者
void *produce()
{
    
    
    int i, item, type = 1;
    for (i = 0; i < ITEM_COUNT; i++)
    {
    
    
        sema_wait(&empty_buffer1_sema);
        sema_wait(&mutex_sema1);
        item = i + 'a';
        put_item(item, &in1, type);
        printf("produce item: %c\n", item);

        sema_signal(&mutex_sema1);
        sema_signal(&full_buffer1_sema);
    }
}
// 计算者
void *compute(void *arg)
{
    
    
    int i, item, type;
    for (i = 0; i < ITEM_COUNT; i++)
    {
    
    
        type = 1;
        sema_wait(&full_buffer1_sema);
        sema_wait(&mutex_sema1);
        item = get_item(&out1, type);
        sema_signal(&mutex_sema1);
        sema_signal(&empty_buffer1_sema);

        type = 2;
        sema_wait(&empty_buffer2_sema);
        sema_wait(&mutex_sema2);
        item -= 32;
        put_item(item, &in2, type);
        printf("\tcompute item: %c:%c\n", item + 32, item);
        sema_signal(&mutex_sema2);
        sema_signal(&full_buffer2_sema);
    }
    return NULL;
}
// 消费者
void *consume(void *arg)
{
    
    
    int i, item, type = 2;
    for (i = 0; i < ITEM_COUNT; i++)
    {
    
    
        sema_wait(&full_buffer2_sema);
        sema_wait(&mutex_sema2);

        item = get_item(&out2, type);
        printf("\t\tconsume item: %c\n", item);
        sema_signal(&mutex_sema2);
        sema_signal(&empty_buffer2_sema);
    }
    return NULL;
}
int main()
{
    
    
    pthread_t consumer_tid;
    pthread_t computer_tid;
    sema_init(&mutex_sema1, 1);
    sema_init(&mutex_sema2, 1);
    sema_init(&empty_buffer1_sema, CAPACITY);
    sema_init(&empty_buffer2_sema, CAPACITY);
    sema_init(&full_buffer1_sema, 0);
    sema_init(&full_buffer2_sema, 0);
    // 创建消费者线程
    pthread_create(&consumer_tid, NULL, consume, NULL);
    // 创建计算者线程
    pthread_create(&computer_tid, NULL, compute, NULL);
    // 主线程为生产者线程
    produce(NULL);
    // 等待线程结束
    pthread_join(consumer_tid, NULL);
    pthread_join(computer_tid, NULL);
    return 0;
}

7.2 pp.c

使用信号量实现 ping-pong 问题,功能同6.2

不需要访问临界区,而是进行状态的转化。故设置一个状态变量status,为1表示ping进程,为0表示pong进程。

#include <stdio.h>
#include <pthread.h>
#define COUNT 100
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, int status)
{
    
    
    pthread_mutex_lock(&sema->mutex);
    while (sema->value != status)
    {
    
    
        pthread_cond_wait(&sema->cond, &sema->mutex);
    }
    pthread_mutex_unlock(&sema->mutex);
}
void sema_signal(sema_t *sema, int status)
{
    
    
    pthread_mutex_lock(&sema->mutex);
    sema->value = status;
    pthread_cond_signal(&sema->cond);
    pthread_mutex_unlock(&sema->mutex);
}
sema_t mutex_sema;
void *ping(void *arg)
{
    
    
    int i = COUNT;
    while (i--)
    {
    
    
        // 如果状态变量为1,执行ping线程,否则等待
        sema_wait(&mutex_sema, 1);
        printf("ping\n");
        // ping线程结束,切换状态
        sema_signal(&mutex_sema, 0);
    }
}
void *pong(void *arg)
{
    
    
    int i = COUNT;
    while (i--)
    {
    
    
        // 如果状态变量为0,执行pong线程,否则等待
        sema_wait(&mutex_sema, 0);
        printf("pong\n");
        // pong线程结束,切换状态
        sema_signal(&mutex_sema, 1);
    }
}

int main()
{
    
    
    pthread_t pong_tid;
    sema_init(&mutex_sema, 0);
    pthread_create(&pong_tid, NULL, pong, NULL);
    ping(NULL);
    pthread_join(pong_tid, NULL);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_46003347/article/details/123574499