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

这篇具有很好参考价值的文章主要介绍了操作系统实践课作业(南航)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

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


实验环境:从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进程。文章来源地址https://www.toymoban.com/news/detail-400382.html

#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;
}

到了这里,关于操作系统实践课作业(南航)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • HNU-操作系统OS-作业3(26-31章)

    这份文件是OS_homework_3 by计科210X wolf 202108010XXX 文档设置了目录,可以通过目录快速跳转至答案部分。 运行该程序wolf/OS-homework/threads-intro/x86.py 通过README可知一些重要的标识符如下 26.1 开始,我们来看一个简单的程序,“loop.s”。首先,阅读这个程序,看看你是否能理解它: cat loop.

    2024年02月08日
    浏览(45)
  • 头歌操作系统 课后作业4.1:段式内存管理 答案

    第1关:0号进程和1号进程的mynext变量的取值变化 编程要求 根据相关知识,修改版本 1.3 内核回答问题: 通过 gdb 调试查找答案,将第三关的答案填写在 /data/workspace/myshixun/第三关.txt 中。 1 号进程的 output_char 函数调用会执行几次? 每次调用时,1 号进程和 0 号进程的 mynext 变

    2024年02月06日
    浏览(42)
  • 操作系统课程设计(作业调度、内存管理、进程调度、进程阻塞等)

    资源下载: https://download.csdn.net/download/fufuyfu/85811450 操作系统是计算机系统配置的基本软件之一。它在整个计算机系统软件中占有中心地位。其作用是对计算机系统进行统一的调度和管理,提供各种强有力的系统服务,为用户创造既灵活又方便的使用环境。本课程是计算机及

    2024年02月03日
    浏览(50)
  • 头歌操作系统 课后作业3.1:进程的描述与状态

    第1关:1 号进程的核心栈内容分析 编程要求 根据相关知识,回答问题: (将答案填写在 /data/workspace/myshixun/第三关.txt 中) 1 号进程的核心栈栈底的位置是多少? 1 号进程(用 si)执行函数 task1 中的第一个 int 0x81 指令后,核心栈栈顶的位置是多少?从栈底到栈顶依次放了哪

    2024年02月05日
    浏览(36)
  • 【操作系统--页面置换算法】C语言详解--大作业版(附代码)

    1设计和实现FIFO,LRU,OPT和CLOCK算法 2设计和实现一个完整的可供选择不同算法的程序 3通过页面访问序列随机发生器实现对上述算法的测试及性能比较 4领略页面置换背后的资源调配思想,并将其运用到其他的操作系统的知识,以及运用到生活中的资源调配策略以及解决措施 5理

    2024年02月06日
    浏览(40)
  • 微控制器实时操作系统实践1实时系统介绍

    这本实践指南将为你提供最重要的功能知识,以使实时操作系统(RTOS)在微控制器(MCU)上启动和运行。如果你有兴趣学习如何通过使用实际硬件的实例来实现RTOS的应用,并讨论常见的性能与开发时间的权衡,那么你就来对地方了!我们将使用自由RTOS来实现代码! 我们将使

    2024年02月08日
    浏览(42)
  • 【操作系统】-- 先来先服务算法(FCFS)、短作业优先算法(SJF)、高响应比调度算法(HRRN)

    1、算法思想 主要从 公平 的角度考虑。 2、算法规则 按照 作业/进程 到达的 先后顺序 进行服务。 3、是否可抢占 非抢占式算法。 4、是否可导致饥饿 不会导致饥饿。 5、优缺点 优点:公平、算法实现简单。 缺点:对长作业有利,对短作业不利。 6、例题 例:各进程到达就绪

    2024年02月02日
    浏览(48)
  • 微控制器实时操作系统实践5选择IDE

    集成开发环境(IDE integrated development environment)有能力极大地影响开发。集成开发环境被设计成具有较小的学习曲线,并且通常提供一种简单的方法来从现有的驱动程序和中间件建立解决方案。 在本章中,我们将讨论如何选择IDE,看看不同类型的IDE,并选择一个IDE来创建你在

    2024年02月08日
    浏览(46)
  • 微控制器实时操作系统实践2了解RTOS任务

    超级循环编程范式通常是嵌入式系统工程师最先接触到的编程方法之一。用超级循环实现的程序有一个单一的顶层循环,在系统需要执行的各种功能之间循环。这些简单的while循环很容易创建和理解(当它们很小的时候)。在FreeRTOS中,任务与超级循环非常相似--主要区别在于

    2024年02月08日
    浏览(45)
  • 操作系统有关进程调度算法(含先来先服务,短作业优先,优先级调度算法和时间片轮转调度算法)

    本文采用的进程调度算法有:先来先服务,短作业优先,优先级调度算法和时间片轮转调度算法。 针对这四种算法,我采用的是建立数组结构体,如: 先来先服务(FCFS)调度算法是一种最简单的调度算法,该算法既可用于作业调度,也可用于进程调度。采用FCFS算法,每次从

    2024年02月03日
    浏览(59)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包