CS252 Final

A | B: A的输出是B的输入,比如cat infile.txt | grep hello

  • C程序中,模拟bash的重定向和管道等行为,一定会用到fork() + dup2() + pipe()
    • A | B
      • pipe()创建一个管道
      • fork()创建两个子进程(执行A和B)
      • dup2()Astdout->管道写端, Bstdin<-管道读端
      • exec()分别执行AB命令, 一般是execvp(命令, 参数), execlp()
    • < file
      • open(file, O_RDONLY)打开文件
      • dup2(fd, STDIN_FILENO)把文件重新定向为标准输入(通常放在fork后的子进程中)
    • > file
      • open(file, O_WRONLY | O_CREAT | O_TRUNC, 0644)打开文件
      • dup2(fd, STDOUT_FILENO)把文件重新定向为标准输出
    • >> file
      • open(file, O_WRONLY | O_CREAT | O_APPEND, 0644)打开文件
      • dup2(fd, STDOUT_FILENO)把文件重新定向为标准输出 ```C //A, B是命令, argvA/argvB是参数组 void redirect(const char * A, char *const argvA[], const char * B, char *const argvB[]) { // A | B int fd[2]; if(pipe(fd) == -1){ perror(“pipe”); exit(1); };

    int pid1 = fork(); if (pid1 == -1) { perror(“fork1”); exit(1); } if (pid1 == 0) { // handle A close(fd[0]); dup2(fd[1], 1); //STDOUT这个宏不存在,用1代替 close(fd[1]); //重新定向后就可以关闭 execvp(A, argvA); //一般后面直接加perror perror(“exec A”); exit(1); }

    int pid2 = fork(); if (pid2 == -1) { perror(“fork2”); exit(1); } if (pid2 == 0){ // handle B close(fd[1]); dup2(fd[0], 0); //STDIN这个宏也不存在,用0代替 close(fd[0]); //重新定向后就可以关闭 execvp(B, argvB); perror(“exec B”); exit(1); }

    //在父进程中,关闭所有pip端并等待两个子进程 close(fd[0]); //不关闭读端,写端就会一直挂着 close(fd[1]); waitpid(pid1, NULL, 0); waitpid(pid2, NULL, 0); } ```

// < file
void redirectToFile(const char * file) {
    int fd = open(file, O_RDONLY); // 不需要管道,所以不是int fd[2], 只要用open(file, O_RDONLY)就可以
    if (fd == -1) {
        perror("file open failed");
        exit(1);
    }
    if (dup2(fd, STDIN_FILENO) == -1) {
        perror("dup2 failed");
        exit(1);
    }
    close(fd); //最后依然是关闭
}
// > file
void redirect(const char * file) {
    int fd = open(file, O_WRONLY | O_CREAT | O_TRUNC, 0644);
    if (fd == -1) {
        perror("open failed");
        exit(1);
    }
    if (dup2(fd, STDOUT_FILENO) == -1) {
        perror("dup2 failed");
        exit(1);
    }
    close(fd);
}
// >> file
void redirect(const char * file) {
    int fd = open(file, O_WRONLY | O_CREAT | O_APPEND, 0644);
    if (fd == -1) {
        perror("open failed");
        exit(1);
    } 
    if (dup2(fd, STDOUT_FILENO) == -1) {
        perror("dup2 failed");
        exit(1);
    }
    close(fd);
}

sort < file.txt: 读取file.txt的内容并进行排序,等价于cat file.txt | sort

  • sortstdin读取指令:
    char *argv[] = {"sort", NULL};
    execvp("sort", argv);
    
  • sortfile.txt读取指令:
    char *argv[] = {"sort", "file.txt", NULL};
    execvp("sort", argv);
    
  • grepstdin读取指令:
    char *argv[] = {"grep", "hello", NULL};
    execvp("grep", argv);
    
  • grepfile.txt读取指令:
    char *argv[] = {"grep", "hello", "file.txt", NULL};
    execvp("grep", argv);
    
//sort < file.txt
void sortCommand(const char * file) {
    int fd = open(file, O_RDONLY);
    if (fd == -1) {
        perror("open file failed");
        exit(1);
    }
    if(dup2(fd, STDIN_FILENO) == -1){
        perror("dup2 failed");
        exit(1);
    }
    close(fd);

    char *argv[] = {"sort", NULL};
    execvp('sort', argv);
    perror("sort error");
    exit(1);
}

grep A: 从标准输入读取内容,并筛出包含A的行

void grep(char * A){
    char * argv[] = {"grep", A, NULL};
    execvp("grep", argv);
    perror("exec fail");
    exit(1);
}

command >> file.txt: 追加重定向,把command输出追加到file.txt的结尾(不会覆盖原本的内容)

void redirect(const char* command, const char* file){
    int fd = open(file, O_WRONLY | O_CREAT | O_APPEND, 0644);
    if (fd == -1) {
        perror("open fail");
        exit(1);
    }
    if (dup2(fd, STDOUT_FILENO) == -1){
        perror("dup2 failed");
        exit(1);
    }
    close(fd);//不要忘记close
    char *argv[] = {(char *) command, NULL}; //不要写死,用command变量
    execvp(command, argv);
    perror("exec failed");
    exit(1);
}

sort < infile.txt | grep hello >> output.txt: 读取file.txt的内容并排序,从中找到包含hello的行,添加到output.txt的结尾

  • sort:
    • 读取从STDIN 变成infile.txt
    • 输出从STDOUT变成pipeline写端
  • grep:
    • 读取从STDIN变成pipeline读端
    • 输出从STDOUT变成output.txt
//grepsort arg1 arg2 arg3 = sort < arg2 | grep arg1 >> arg3
//参数应该是, argv[0] = grepsort, argv[1] = arg1
void grepsort(char * argv[]){
    int fd[2]; //一个管道即可,因为只有一个管道符号 | 
    if (pipe(fd) == -1){
        perror("pipe failed");
        exit(1);
    }
    int pid1 = fork();
    //不要忘记检查fork
    if (pid1 == -1) {
        perror("fork1 failed");
        exit(1);
    }
    //fork成功,还有父子进程之分
    if (pid1 == 0){
        close(fd[0]);
        //子进程1: sort < infile.txt | -> 写入管道
        int fd_in = open(argv[2], O_RDONLY);
        if (fd_in == -1){
            perror("open infile failed");
            exit(1);
        }
        //这里的输入来自infile.txt, 输出是去管道,所以有两个dup2
        if(dup2(fd_in, STDIN_FILENO) == -1){
            // dup2(A, STDIN_FILENO): 标准输入来自A
            perror("dup2 from infile.txt failed");
            exit(1);
        }
        if(dup2(fd[1], STDOUT_FILENO) == -1){
            //标准输出来自fd[1]
            perror("dup2 to pipe failed");
            exit(1);
        }
        close(fd_in);
        close(fd[1]);

        char * argv1[] = {"sort", NULL};
        execvp("sort", argv1);
        perror("sort exec failed");
        exit(1);
    }

    int pid2 = fork();
    if (pid2 == -1) {
        perror("fork2 failed");
        exit(1);
    }
    if (pid2 == 0) {
        close(fd[1]);
        int fd_out = open(argv[3], O_WRONLY | O_CREAT | O_APPEND, 0644);
        if (fd_out == -1) {
            perror("open output failed");
            exit(1);
        }
        if(dup2(fd_out, STDOUT_FILENO) == -1){
            perror("dup2 for writing failed");
            exit(1);
        }
        if (dup2(fd[0], STDIN_FILENO) == -1){
            perror("dup2 for pipe reading failed");
            exit(1);
        }
        close(fd_out);
        close(fd[0]);

        char * argv2[] = {"grep", argv[1], NULL};
        execvp("grep", argv2);
        perror("grep exec failed");
        exit(1);
    }
    close(fd[0]);
    close(fd[1]);
    wait(pid1, NULL, 0);
    wait(pid2, NULL, 0);
}

print entries就是打印每一行的意思 entries = lines

sort < infile.txt | grep hello >> output.txt这本身就是一个bash命令,所以如果要用program来实现它,一定不是用bash,而是用C.

Question 2:

int runCommand(char * command, char * outputBuffer, int maxBufferSize){
    int fd[2];
    if (pipe(fd) == -1) {
        perror("pipe failed");
        exit(1);
    }
    int pid = fork();
    if (pid == -1) {
        perror("fork failed");
        exit(1);
    }
    if (pid == 0){
        //child process:
        close(fd[0]);
        if(dup2(fd[1], STDOUT_FILENO) == -1){
            perror("dup2 failed");
            exit(1);
        }
        close(fd[1]);

        char * argv[] = {(char *)command, NULL};
        execvp(command, argv);
        perror("command exec failed");
        exit(1);
    } 
    close(fd[1]); //不写入管道任何东西,写入outputBuffer不等于写入管道
    int bytesRead = 0;
    int totalRead = 0;
    while ((bytesRead = read(fd[0], outputBuffer + totalRead, maxBufferSize - 1 - totalRead)) > 0) {
        totalRead += bytesRead;
        if (totalRead >= maxBufferSize - 1){
            break;
        }
    }
    close(fd[0]);
    //添加结束符
    outputBuffer[totalRead] = '\0';

    int status;
    wait(pid, &status, 0);
    return (bytesRead < 0 || !WIFEXITED(status)) ? -1 : 0;
}

Question3:

make them synchronized: 让他们变得线程安全

insert() -> 生产者; removeFirst() -> 消费者

semaphores: 信号量(资源计数器)

struct List * head = NULL;
pthread_mutex_t mutexLock;
sema_t fullSem;
sema_t emptySem;

main(){
    pthread_mutext_init(&mutexLock, NULL);
    sema_init(&fullSem, 20, USYNC_THREAD, NULL);
    sema_init(&emptySem, 0, USYNC_THREAD, NULL);
}

Question 7:

What are four parameters that computer needs to get connected to the internet and what are they used for?

  • IP Address: a unique identifier assigned to a device on a network
  • Subnet Mask: determines which IP addresses are considered part of the same local network
  • Default gateway(router): the IP address of a router that connects the local network to external networks
  • DNS Server: translates human-readable domain name(like www.google.com) into IP address.

Question 8:

How does the computer know when it can deliver a packet directly or when it have to pass a packet to a router?

Computer uses the Subnet Mask and IP Address to make this decision.

  • Check the destination IP address
  • Apply the subnet mask to both the destination IP and its own IP
  • Compare the result:
    • if they are in the same subnet, the destination is on the local network -> send directly
    • not the same subnet, the destination is outside the local network -> send to the default gateway (router)

Question 9:

What is ARP(地址解析协议) and how does it work?

  • ARP (Address Resolution Protocal) is a protocal used to map an IP address to a MAC address in a local network (LAN). It accepts an IP address of a computer in locally connected network and outputs the Ethernet address of the computer

Question 10:

What does DNS mean and what it is used for?

  • Domain Name Server. Convert human-readable domain name into IP address.

Question 11:

What does DHCP mean and how does it work?

  • Dynamic Host Configuration Protocal

Question 12:

What does UDP mean?

User Datagram Protocol. It is an unreliable protocol and message-oriented. It does not require a connection to send messages. Each message is encapsulated in an IP datagram and its size is limited to the network’s MTU. It is mainly used for broadcasting and for real-time data applications.

Question 13:

What does TCP mean? What are the 6 features of TCP?

Transmission Control Protocol

  • Connection-Oriented: A Connection must be established before data transfer (via a 3-way handshake)
  • Reliable Data Transfer: Ensures all packets are reveived using acknowledgments and retransmissions.
  • Ordered Data Delivery: Packets arrive at the receiver in the same order as sent.
  • Error Checking: Uses checksums to detec corrupted segments.
  • Flow Control: uses a sliding window to prevent sender from overwhelming receiver
  • Congestion Control - Adjust sending rate based on network congestion to avoid overload.



Enjoy Reading This Article?

Here are some more articles you might like to read next:

  • CS252 Mid
  • Backtracking, LeetCode
  • Mock Interview(2), LeetCode
  • Binary Tree, LeetCode
  • NVIDIA, LeetCode