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

2023-05-16

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

文章目录

  • 操作系统实践课作业(南航)
  • 1. job2
    • 1.1 main.c
    • 1.2 math.c
    • 1.3 Makefile
  • 2. job3
    • 2.1 myecho.c
    • 2.2 mycat.c
    • 2.3 mycp.c
    • 2.4 mysys.c
    • 2.5 sh1.c
  • 3. job4
    • 3.1 myls.c
    • 3.2 mytree.c
  • 4. job5
    • 4.1 sh2.c
  • 5. job7
    • 5.1 pi1.c
    • 5.2 pi2.c
    • 5.3 sort.c
  • 6. job8
    • 6.1 pc.c
    • 6.2 pp.c
  • 7. job9
    • 7.1 pc.c
    • 7.2 pp.c

实验环境:从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;
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

操作系统实践课作业(南航) 的相关文章

  • 计算总线带宽

    总线带宽 总线带宽 xff1a 指总线在单位时间内可以传输的数据总数 xff08 等于总线的宽度与工作频率的乘积 xff09 通常单位 xff1a MB s MBps 总线的传输速率 61 总线的带宽 61 xff08 总线位宽 8位 xf
  • 1.VScode中配置GIT

    1 VScode开发环境配置GIT 1 查看git安装目录 where git Windows指令 which git Mac指令 2 在VScode开发环境的setting json文件中配置git路径 2 配置代码提交GIT 2 1传统
  • 143-牛客网C++刷题9

    1 不定义第三个变量 xff0c 交换两个变量的数据 x 43 61 y y 61 x y x 61 y 2 常量可以是任何的基本数据类型 xff0c 可分为整型数字 浮点数字 字符 字符串和布尔值 3 在C 43 43 中 xff0c 可
  • 电脑Windows找不到gpedit.msc请确定文件名情况的处理方法

    有时我们清理优化系统后 xff0c 当运行 gpedit msc 命令想要打开 本地组策略编辑器 时 xff0c 会提示找不到 gpedit msc 请确定文件名的情况 解决方法如下 xff1a 1 新建一个文本文档 xff0c 粘贴以下代
  • IDEA更换主题

    IDEA自带的主题比较少 xff0c 但是它的主题可以更换的 xff0c 可以找一些自己喜欢的主题皮肤换上 操作如下 xff1a 下载主题 要更换主题皮肤 xff0c 首先得找到自己喜欢的主题下载下来 这里推荐两个主题 xff0c 第一个下
  • JDBC操作MySQL数据库出现:No suitable driver found for...异常

    JDBC操作MySQL数据库出现 xff1a No suitable driver found for 异常 xff0c 可能原因如下 xff1a 一 没有导入jdbc驱动jar包 导入方法如下 xff08 以IDEA为例 xff09 xf
  • Visual Studio无法打开源文件“stdio.h“问题

    出现该问题是因为没有安装对应的Win10 SDK的原因 xff0c 安装Visual Studio时默认可能不会安装 xff0c 所以需要手动勾选对应的问题安装 操作如下 xff1a 点击Windows的开始菜单 xff0c 在所有应用里选
  • Redis的下载安装

    一 Windows下的下载安装 1 Redis的下载 官方不支持Windows版本的Redis xff0c 因此官网上不提供下载 xff0c 但微软开发和维护着支持win 64的Redis版本 xff0c 因此可以去下载 地址 xff1a
  • 极大似然估计(Maximum-Likelihood)的理解

    极大似然估计 是建立在 极大似然原理 的基础上的一个统计方法 xff0c 是概率论在统计学中的应用 目录 1 极大似然原理 2 极大似然估计 1 极大似然原理 极大似然原理 xff1a 在随机试验中 xff0c 许多事件都有可能发生 xff
  • 数据库可视化工具——HeidiSQL

    简介 HeidiSQL是一款用于简单化迷你的 MySQL 服务器和数据库管理的图形化界面 HeidiSQL提供了一个用于在数据库浏览之间切换 SQL 查询和标签带有语法突出显示的简单易用的界面 其它功能包括BLOB 和 MEMO 编辑 xf
  • 远程连接工具——WindTerm

    一 简介 WindTerm 是一款开源免费 跨平台SSH Sftp Shell Telnet Serial 客户端 xff0c 即远程连接工具 该网站有详细的介绍 xff0c 可以了解一下 xff0c 网址 xff1a https king
  • Eclipse自定义注释

    我们在使用eclipse编写Java代码时 xff0c 想要让自己生成相关注释 xff0c 可以通过自定义注释模板来实现 xff0c 操作如下 xff1a 选择点击导航栏的Window xff0c 在打开的列表框里 xff0c 选择点击Pr
  • MarkDown 编辑器——Moeditor

    Moeditor是一款免费开源的MarkDown 编辑器 xff0c 界面清新简洁 xff0c 支持多平台 下载 浏览器输入框输入Moeditor进行搜索 xff0c 选择官网进入 xff0c Moeditor 进入官网界面如下 xff1a
  • Github的加速访问

    文章目录 概述Steam 43 43 的下载Steam 43 43 的安装使用 概述 GitHub打开访问速度比较慢 xff0c 这儿介绍一种加速访问的方式 xff0c 是正规的方式 xff0c 采用 Steam 43 43 来加速 Ste
  • 脚本基本命令自定义变量讲解

    功能 自定义变量功能 不再受固定几个变量限制 使用方法 三个步骤 1 首先要声明变量 2 读取变量 3 保存变量该变量支持增加 减少 脚本查看 检查大小等功能 xff0c 该功能的开发突破了变量使用受限制 xff0c 利用该变量可制作出超强
  • RPLIDAR A2 Windows 下开发

    RPLIDAR A2 Windows 下开发 一 思岚雷达官网 xff1a 传送门1 传送门2 二 SDK库文件学习 xff1a 传送门
  • 工业互联网项目积累

    新时代的物联网工程师 xff1a 做算法 要会图像处理 PCL 软件要会常见的通信协议 数据库 界面设计 串口通信 TCP UDP网络通信 1 物联网 传感器通信协议 MODBUS 2 什么是通信协议 xff1f 1 xff09 通信协议是
  • 标定--故事的开端 (阅读请评论,技术交流,创造不易)

    第一章 感知传感器 万物皆可标 离开slam xff0c 踏足视觉测量已经5个月 xff0c 出去转一趟 xff0c 进一步了解PCL点云库的应用 再次回归slam xff0c 倍感亲切 继续奋斗吧 xff01 加油 xff01 知识有限
  • PCL 缘起缘灭

    一 PCL是什么 PCL xff08 Point Cloud Library xff0c 点云库 xff09 是在吸收了前人点云相关研究基础上建立起来的大型跨平台开源C 43 43 编程库 xff0c 它实现了大量点云相关的通用算法和高校数
  • 随机变量 的 分布函数 与 概率密度函数 的区别

    目录 1 分布函数 2 概率密度函数 1 分布函数 分布函数 显示了随机变量的取值落在某个区间上的概率 xff0c 是一种不减函数 设 X 是一个随机变量 xff0c x 是任意实数 xff0c 函数 成为 X 的分布函数 分布函数是一个普

随机推荐

  • PCL(1)搭建与工业测量应用 总结

    PCL搭建与工业测量应用 PCL Point Cloud Learning 0 摘要 windows和ROS下安装配置PCL开发环境 利用CMake等编程模式建立实例应用程序 掌握PCL开发环境搭建流程 注意事项和关键配置选项 开发自己的P
  • 创建ROS工作空间

    创建ROS工作空间
  • matlab 相机标定

    matlab 相机标定 原理链接描述 matlab 自带相机标定工具 1 教程链接描述 2 链接描述 3 链接描述
  • 大疆览沃浩界(Livox Horizon)激光雷达测评(激光相机联合标定)

    1 大疆览沃浩界 xff08 Livox Horizon xff09 激光雷达测评 测评链接 2 livox horizon激光与zed相机彩色点云地图构建 3 Autoware激光雷达与网络摄像机联合标定 四 Livox Horizon
  • ceres-slover库安装

    安装ceres slover 2 1版本
  • ZED相机+ubuntu 18.04+ros melodic

    0 l环境配置 1 安装链接描述 2 双目标定 3 zed cpu安装 guithub
  • UBUNTU 笔记常用软件

    金山文档 有道笔记 JOPLIN
  • ROS中用cv_bridge和opencv时出现cv::xxx未定义的问题

    1前景提要 xff1a cv bridge是ros中常用的消息类型转换的包 xff08 ros自带的包 xff09 xff0c 要在ros环境下利用opencv处理USB相机拍摄的视频必须用到cv bridge xff08 如下图 xff0
  • 忘记hadoop安装路径,忘记hadoop根目录怎么进入根目录启动hadoop

    有时候很久没用VMware虚拟机了 xff0c Linux操作系统如果不是刻意去用 xff0c 基本上没什么机会去使用 更别提计算机小白了 xff0c Windows系统都没弄懂 xff0c 又出来一个Linux系统 某天我打开目录 xff
  • 微服务应用之OAuth2.0的四种授权方式

    引言 OAuth 2 0 是一种授权机制 xff0c 主要用来颁发令牌 xff08 token xff09 OAuth 2 0 的标准是 RFC 6749 文件 xff0c 这个文件写出 xff08 由于互联网有多种场景 xff0c xff
  • 期望、方差、协方差、相关系数的理解

    目录 1 数学期望 xff08 均值 xff09 2 方差 D X 或 Var X 3 协方差 Cov X Y 4 相关系数 5 协方差矩阵 一句话概括 xff1a 期望 反映了平均水平 xff0c 方差 反映了数据波动程度 xff0c 协
  • Ubuntu系统通过脚本实现循环访问网页

    bin bash step 61 5 间隔的秒数 for i 61 0 i lt 60 i 61 i 43 step do curl https www baidu com crul能达到想要的效果 w3m https www csdn n
  • resource not found: roslaunch和Command ‘rosrun‘ not found的解决方法

    安装ros的时候 xff0c 历经种种困难后终于完成了前面的步骤 xff0c 却在运行roscore时再次出现了问题 xff1a Resource not found roslaunch ROS path 0 61 opt ros noet
  • vscode配置clang-tidy插件

    先拥有一个 clang tidy文件 首先确保系统已经安装clang tidy xff0c 在项目的目录下新建一个 clang tidy文件 xff0c 具体如何配置在网上搜索 xff0c 或者用已有的 也可以在vscode的文件中配置 x
  • 4、linux初级——Linux在开发板中的使用

    目录 一 用CRT连接开发板 1 安装CRT调试工具 2 连接开发板 3 开机后ctrl 43 c关闭登录程序可以进入命令行 二 开发板和电脑文件之间的传输 xff08 串口 xff09 1 rx xff08 从电脑下载文件到开发板 xff
  • FreeRtos--中断

    采用二值信号量同步 二值信号量可以在某个特殊的中断发生时 xff0c 让任务解除阻塞 xff0c 相当于让任务与中断同步 这样就可以让中断事件处理量大的工作在同步任务中完成 xff0c 中断服务例程 ISR 中只是快速处理少部份工作 如此
  • FreeRTOS--资源管理

    函数重入 如果一个函数可以安全地被多个任务调用 xff0c 或是在任务与中断中均可调用 xff0c 则这个函数是可重入的 每个任务都单独维护自己的栈空间及其自身在的内存寄存器组中的值 如果一个函数除了访问自己栈空间上分配的数据或是内核寄存器
  • vscode代码提交到gittee码云 第一次提交方法

    学习3 xff1a 今天是第一次将vscode代码提交到gittee xff0c 废话不多说 xff0c 直接上方法 xff1a 查看git仓库 gt git status 将当前项目文件初始化为仓库 如果当前文件夹不是git仓库 xff0
  • 明火烟雾目标检测项目部署(YoloV5+Flask)

    明火烟雾目标检测项目部署 文章目录 明火烟雾目标检测项目部署1 拉取Docker PyToch镜像2 配置系统环境2 1 更换软件源2 2 下载vim2 3 解决vim中文乱码问题 3 运行项目3 1 拷贝项目到容器中3 2 安装项目所需的
  • 操作系统实践课作业(南航)

    操作系统实践课作业 xff08 南航 xff09 文章目录 操作系统实践课作业 xff08 南航 xff09 1 job21 1 main c1 2 math c1 3 Makefile 2 job32 1 myecho c2 2 myca