2024-08-27



#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <signal.h>
 
#define BUFFER_SIZE 10
 
int buffer[BUFFER_SIZE];
int read_pos = 0;
int write_pos = 0;
int count = 0;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t not_full = PTHREAD_COND_INITIALIZER;
pthread_cond_t not_empty = PTHREAD_COND_INITIALIZER;
 
void handle_sigint(int sig) {
    write(1, "\n", 1);  // 打印换行
    exit(0);
}
 
void insert(int item) {
    pthread_mutex_lock(&mutex);
    while (count == BUFFER_SIZE) {
        pthread_cond_wait(&not_full, &mutex);
    }
    buffer[write_pos] = item;
    write_pos = (write_pos + 1) % BUFFER_SIZE;
    count++;
    pthread_cond_signal(&not_empty);
    pthread_mutex_unlock(&mutex);
}
 
int remove() {
    int item;
    pthread_mutex_lock(&mutex);
    while (count == 0) {
        pthread_cond_wait(&not_empty, &mutex);
    }
    item = buffer[read_pos];
    read_pos = (read_pos + 1) % BUFFER_SIZE;
    count--;
    pthread_cond_signal(&not_full);
    pthread_mutex_unlock(&mutex);
    return item;
}
 
void* producer(void* arg) {
    int i;
    for (i = 0; i < 100; i++) {
        insert(i);
        printf("Produced: %d\n", i);
        sleep(1);
    }
    return NULL;
}
 
void* consumer(void* arg) {
    int data;
    while (1) {
        data = remove();
        printf("Consumed: %d\n", data);
        sleep(2);
    }
    return NULL;
}
 
int main() {
    pthread_t producer_thread, consumer_thread;
    signal(SIGINT, handle_sigint);
    pthread_create(&producer_thread, NULL, producer, NULL);
    pthread_create(&consumer_thread, NULL, consumer, NULL);
    pthread_join(producer_thread, NULL);
    pthread_join(consumer_thread, NULL);
    return 0;
}

这段代码实现了一个简单的生产者-消费者模型,其中使用了环形队列来管理数据。它定义了插入和移除操作,并且在多线程环境中安全地进行。当SIGINT信号到来时,程序优雅地退出。这个例子展示了如何使用条件变量和互斥锁来同步线程,并且如何处理UNIX信号。

2024-08-27

在Linux中,重定向指的是改变标准输入、标准输出和标准错误输出流的方向。这可以通过一些特殊的符号来实现,例如 > 用于将输出重定向到文件,>> 用于追加到文件,< 用于从文件中读取输入,等等。

以下是一些实现重定向的示例代码:




# 将 echo 命令的输出重定向到文件,如果文件已存在则覆盖它
echo "Hello World" > output.txt
 
# 将 cat 命令的输出追加到文件末尾
echo "Another line" >> output.txt
 
# 从文件中读取输入并输出
cat < input.txt
 
# 将标准错误重定向到标准输出,并将它们都重定向到同一个文件
ls non_existing_file 2>&1 > output.txt
 
# 清空文件内容
> output.txt

在Linux中,还有一个概念叫做“虚拟文件系统”或“proc文件系统”,它允许用户和程序员以文件和目录的形式访问内核空间中的数据和接口。例如,/proc/cpuinfo 文件包含了CPU的信息,/proc/meminfo 包含了内存的信息。

以下是如何使用虚拟文件系统的示例代码:




# 查看CPU信息
cat /proc/cpuinfo
 
# 查看内存信息
cat /proc/meminfo

重定向和虚拟文件系统是Linux系统管理和编程中非常基础且重要的概念。熟悉这些概念有助于你更高效地管理和操作Linux系统。

2024-08-27



# 使用curl命令行工具进行网络数据传输,并展示其中的一些高级用法。

# 1. 使用-u选项发送用户名和密码进行认证。
curl -u username:password http://example.com

# 2. 使用-H选项添加或修改HTTP请求头。
curl -H "Content-Type: application/json" -H "Authorization: Bearer $ACCESS_TOKEN" http://example.com

# 3. 使用-d选项发送POST数据。
curl -d "param1=value1&param2=value2" http://example.com/post

# 4. 使用-X选项指定HTTP方法。
curl -X GET http://example.com
curl -X POST -d "param1=value1" http://example.com

# 5. 使用-o选项将响应保存到文件。
curl -o filename.html http://example.com

# 6. 使用-s选项静默模式,不显示进度和错误信息。
curl -s http://example.com

# 7. 使用-L选项跟随HTTP重定向。
curl -L http://example.com

# 8. 使用-I选项仅获取响应头信息。
curl -I http://example.com

# 9. 使用-e选项设置引用页,即Referer头。
curl -e "http://referer.com" http://example.com

# 10. 使用-C选项禁止时间戳,禁止所有URL的一般错误。
curl -C - http://example.com

# 11. 使用-T选项上传文件。
curl -T filename.txt http://example.com

# 12. 使用-e选项设置引用页,即Referer头。
curl -e "http://referer.com" http://example.com

# 13. 使用-A选项设置用户代理字符串。
curl -A "Mozilla/5.0" http://example.com

# 14. 使用-b选项传递cookie。
curl -b "cookie_name=cookie_value" http://example.com

# 15. 使用-v选项详细模式,打印所有信息。
curl -v http://example.com

这个示例代码展示了curl命令的一些高级用法,包括认证、HTTP头操作、文件上传、引用页设置、cookie处理等。每一个示例都是独立的,并且可以直接在命令行中运行,以便理解和学习curl的各种功能。

2024-08-27

在Linux中,文件描述符(file descriptor, fd)是一个用于表示文件或者其他数据源的抽象概念。文件描述符是一个非负整数,它提供了一种机制,使得我们可以引用打开的文件。

在Linux中,我们可以通过系统调用来操作文件描述符。例如,open()read()write()close()系统调用可以用来打开、读取、写入和关闭文件描述符对应的文件。

以下是一个简单的示例,展示了如何在Linux中使用文件描述符进行文件操作:




#include <fcntl.h>  // 包含 open(), close()
#include <unistd.h> // 包含 read(), write()
 
int main() {
    // 打开文件
    int fd = open("example.txt", O_RDWR | O_CREAT, 0644);
    if (fd == -1) {
        // 错误处理
        perror("open");
        return -1;
    }
 
    // 写入数据
    const char *buffer = "Hello, world!";
    if (write(fd, buffer, strlen(buffer)) == -1) {
        // 错误处理
        perror("write");
        close(fd);
        return -1;
    }
 
    // 移动文件偏移量
    if (lseek(fd, 0, SEEK_SET) == -1) {
        // 错误处理
        perror("lseek");
        close(fd);
        return -1;
    }
 
    // 读取数据
    char read_buffer[1024];
    ssize_t bytes_read = read(fd, read_buffer, sizeof(read_buffer) - 1);
    if (bytes_read == -1) {
        // 错误处理
        perror("read");
        close(fd);
        return -1;
    }
    read_buffer[bytes_read] = '\0'; // 确保字符串以 null 结尾
 
    // 输出读取的数据
    printf("Read: %s\n", read_buffer);
 
    // 关闭文件
    if (close(fd) == -1) {
        // 错误处理
        perror("close");
        return -1;
    }
 
    return 0;
}

在这个示例中,我们使用了open()函数来创建并打开一个文件,使用write()函数来写入数据,使用lseek()函数来移动文件的读/写位置,使用read()函数来读取数据,最后使用close()函数来关闭文件。

注意,在实际编程中,应该检查每个系统调用的返回值,并在出错时进行适当的错误处理。

2024-08-27

在Docker容器中修改时区,可以通过设置环境变量TZ来实现。以下是一个Dockerfile的例子,它使用Alpine Linux作为基础镜像,并将时区设置为Asia/Shanghai




FROM alpine:latest
 
# 设置时区为Asia/Shanghai
ENV TZ=Asia/Shanghai
 
# 安装tzdata包
RUN apk add --no-cache tzdata
 
# 配置时区
RUN cp /usr/share/zoneinfo/$TZ /etc/localtime && \
    echo $TZ > /etc/timezone && \
    date
 
# 其他Docker指令...

构建并运行这个Docker镜像后,容器内应该会显示为亚洲上海的时间。

2024-08-27

TCP(Transmission Control Protocol)是一种面向连接的、可靠的传输层协议。它通过以下方式提供可靠性:

  1. 序列号:TCP为发送的每个字节分配一个序列号,接收方可以通过序列号重新组装数据包。
  2. 确认应答:每个TCP数据包都包含一个确认应答号,表示接收方已经成功接收的序列号。
  3. 重传机制:如果发送方在指定时间内没有收到确认应答,它会重发数据包。
  4. 流量控制:通过滑动窗口实现,防止发送数据过快导致接收方处理不过来。
  5. 拥塞控制:通过滑动窗口和慢启动算法,管理网络中的数据流量,避免网络拥塞。

TCP头部示例:




源端口 (16) | 目的端口 (16) | 序列号 (32) | 确认应答号 (32) | 头部长度 (4) | 保留 (6) | URG | ACK | PSH | RST | SYN | FIN | 窗口大小 (16) | 校验和 (16) | 紧急指针 (16) | 选项 (0或更多) ...

以下是一个简单的TCP头部解析示例(仅为示例,不完整):




#include <stdio.h>
#include <stdint.h>
 
void parse_tcp_header(const uint8_t *packet, size_t length) {
    if (length < 20) { // TCP头部最小长度为20字节
        printf("Invalid TCP header length\n");
        return;
    }
 
    uint16_t source_port = (packet[0] << 8) | packet[1];
    uint16_t destination_port = (packet[2] << 8) | packet[3];
    uint32_t sequence_number = (packet[4] << 24) | (packet[5] << 16) | (packet[6] << 8) | packet[7];
    uint32_t acknowledgement_number = (packet[8] << 24) | (packet[9] << 16) | (packet[10] << 8) | packet[11];
    uint16_t window_size = (packet[18] << 8) | packet[19];
 
    printf("Source Port: %u\n", ntohs(source_port));
    printf("Destination Port: %u\n", ntohs(destination_port));
    printf("Sequence Number: %u\n", ntohl(sequence_number));
    printf("Acknowledgement Number: %u\n", ntohl(acknowledgement_number));
    printf("Window Size: %u\n", ntohs(window_size));
}
 
int main() {
    // 假设packet是从网络中捕获的TCP数据包
    const uint8_t packet[] = { /* ... */ };
    size_t length = sizeof(packet);
 
    parse_tcp_header(packet, length);
 
    return 0;
}

这个简单的示例展示了如何解析TCP头部中的一些基本字段。在实际情况中,你需要处理整个TCP头部以及可能存在的选项字段。解析完成后,可以根据TCP头部中的信息进行相应的处理,例如数据传输、流量控制、连接管理等。

2024-08-27

在Linux中,文件系统提供了一种方式来创建链接,这种链接不仅可以对文件进行链接,还可以对目录进行链接。链接可以分为两种:硬链接和软链接(也被称为符号链接)。

  1. 硬链接

    硬链接是通过索引节点(inode)进行的链接。在Unix文件系统中,同一个文件的硬链接数会增加。硬链接实际上是现有文件的另一个名字(引用),这个名字与文件的原始名字在逻辑上是分开的。删除一个硬链接名并不影响其他的硬链接名,只有当文件的所有硬链接都被删除时,文件才会从文件系统中删除。

创建一个硬链接的命令是ln,不带任何参数,默认创建的就是硬链接。

例如,创建一个文件file1.txt的硬链接file2.txt




$ touch file1.txt
$ ln file1.txt file2.txt
  1. 软链接

    软链接是一个新的文件,这个文件的内容是它所链接的文件的路径名。删除软链接并不影响被链接的文件,删除被链接的文件也不影响软链接(除了删除被链接文件后软链接变成死链接,不能再被使用)。

创建一个软链接的命令是ln,需要使用-s选项。

例如,创建一个文件file1.txt的软链接file3.txt




$ touch file1.txt
$ ln -s file1.txt file3.txt

注意:不论是硬链接还是软链接,创建时源文件必须存在,否则链接无法创建。

2024-08-27

在Linux CentOS系统中安装Redis的步骤如下:

  1. 使用yum安装Redis:



sudo yum install epel-release -y
sudo yum update -y
sudo yum install redis -y
  1. 启动Redis服务:



sudo systemctl start redis
  1. 设置Redis在系统启动时自动启动:



sudo systemctl enable redis
  1. 检查Redis服务状态:



sudo systemctl status redis
  1. 如果需要,可以通过以下命令停止Redis服务:



sudo systemctl stop redis
  1. 测试Redis是否正常工作:



redis-cli ping

如果返回PONG,则表示Redis已成功安装并正在运行。

以上步骤为Redis的基本安装和启动过程。如果需要配置Redis,可以编辑其配置文件 /etc/redis.conf

2024-08-27

在Linux下实现进度条可以通过检测命令输出的方式来实现。以下是一个简单的Bash脚本,用于模拟一个进度条的实现:




#!/bin/bash
 
# 进度条函数
progress_bar() {
    # 初始化进度条长度
    let progress_length=10-1
    # 循环显示进度条
    for i in $(seq 0 $progress_length); do
        # 打印进度条
        printf "[%-${progress_length}s]\r" $(seq -f "." $i | tr -d '[:digit:]')
        sleep 0.25  # 每次循环等待一定时间
    done
    # 换行
    echo
}
 
# 调用进度条函数
progress_bar

运行这个脚本将会显示一个简单的进度条。

在Linux下使用git上传到远程仓库的命令如下:




git push origin master

这里假设你要推送到远程的分支是master,如果你要推送到其他分支,将master替换为相应的分支名。

如果你想要在这个过程中显示进度,可以使用git push命令的--progress选项,它会在推送时显示进度信息:




git push --progress origin master

如果你想要在脚本中显示进度条,可以将git push命令的输出重定向到一个变量,然后通过检测这个变量的内容来实现进度条的显示。但是,进度信息需要是可解析的,这样才能实现自动化的进度条显示。

下面是一个简单的Bash脚本,结合了进度条和git远程推送的功能:




#!/bin/bash
 
# 进度条函数
progress_bar() {
    # 初始化进度条长度
    let progress_length=10-1
    # 循环显示进度条
    while true; do
        # 读取输出的进度信息
        output=$(git push --progress origin master 2>&1 | grep '^Progress')
        if [[ $output ]]; then
            # 提取进度信息
            progress=$(echo $output | cut -d' ' -f2)
            # 计算进度条的长度
            let filled_length=$(echo $progress | cut -d'/' -f1) * $progress_length / $(echo $progress | cut -d'/' -f2)
            # 打印进度条
            printf "[%-${progress_length}s]\r" $(seq -f "=" $filled_length | tr -d '[:digit:]')
        fi
        sleep 1  # 每秒检查一次进度
    done
}
 
# 在后台运行进度条函数
progress_bar &
 
# 执行git推送
git push --progress origin master
 
# 等待进度条线程完成
wait

请注意,这个脚本假设git推送的进度信息可以被正确解析。如果git的进度条格式在更新,则可能需要相应地更新脚本来解析新的格式。

2024-08-27



#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
 
// 示例函数:进程创建
pid_t create_process(const char *program, char *const argv[]) {
    pid_t pid = fork();
    if (pid == -1) {
        perror("fork failed");
        exit(EXIT_FAILURE);
    } else if (pid == 0) {
        if (execvp(program, argv) == -1) {
            perror("execvp failed");
            exit(EXIT_FAILURE);
        }
    }
    return pid;
}
 
// 示例函数:进程终止
void terminate_process(int status) {
    if (WIFEXITED(status)) {
        printf("子进程正常结束,退出状态 %d\n", WEXITSTATUS(status));
    } else {
        printf("子进程异常结束\n");
    }
}
 
// 示例函数:进程等待
void wait_for_process(pid_t pid) {
    int status;
    if (waitpid(pid, &status, 0) == -1) {
        perror("waitpid failed");
        exit(EXIT_FAILURE);
    }
    terminate_process(status);
}
 
// 示例函数:进程程序替换
void replace_process(const char *program, char *const argv[]) {
    if (execvp(program, argv) == -1) {
        perror("execvp failed");
        exit(EXIT_FAILURE);
    }
}
 
int main() {
    // 创建子进程
    char *argv[] = {"ps", "aux", NULL};
    pid_t pid = create_process("ps", argv);
 
    // 等待子进程结束
    wait_for_process(pid);
 
    // 程序替换:实际上会替换当前进程,不会创建新进程
    argv[0] = "ls";
    replace_process("ls", argv);
 
    return 0;
}

这个示例代码展示了如何在Linux环境下使用C语言进行进程控制,包括创建进程、终止进程、等待进程和进程程序替换。在实际使用中,应该注意错误处理和资源管理,避免产生僵尸进程或者其他问题。