Linux的进程与线程(一篇文章教会你区别和使用---附带示例)

1.前言:

        对于线程和进程而言,直接广泛的认为进程是线程的基础。每一个进程都可以有很多个线程。线程与线程间是并行运行的。那么在我们开发的过程中,由于cpu的能力有限,如何来分配这个算力是我们需要考虑的。我很喜欢的一个说法是大量使用线程对于一个项目,可以直接理解成用空间去换取时间。

        在 Linux 操作系统中,线程和进程是两个核心概念,是实现多任务并发执行的基础。理解它们对于掌握 Linux 的多任务处理和并发编程至关重要。接下来我将详细介绍进程和线程的定义、区别、创建和管理,以及在实际编程中的应用示例和通信方法。

2.进程:

2.1.进程的概念:

        进程是一个正在执行的程序实例,它拥有自己独立的地址空间、内存、数据和文件描述符。进程是操作系统进行资源分配和任务管理的基本单位。
        在Linux系统中,进程是操作系统资源分配和调度的基本单位。进程之间相互独立,一个进程的失败通常不会影响到其他进程。进程的生命周期从创建(forkexec)开始,到终止(自愿退出或被杀死)结束。
        进程可以通过fork()系统调用创建新进程,新进程是父进程的一个副本,拥有与父进程相同的环境和状态,但拥有自己的pid以及独立的执行序列。

2.2.进程的生命周期:

一个进程的生命周期包括以下几个阶段:

        1.创建:使用fork()或者exec()指令来创建进程

        2.运行:执行所编辑的代码。

        3.阻塞:进程等待某些条件的完成(i/o操作、fifio等等)

        4.唤醒:进程从阻塞态返回到运行态。

        5.终止:进程完成任务或者被强制终止。

2.3.进程的创建和管理:

        这里在 Linux 中,可以使用 fork() 系统调用创建新进程。fork() 创建一个子进程,子进程是父进程的副本,但拥有独立的内存空间。这里我们简单的举例来展示怎么创建进程,以及展示究竟进程是什么。通过运行下面的代码,在这里我使用了fork()函数来创建一个子进程。fork()函数会复制当前进程(父进程)并返回新进程的PID。如果fork()成功执行,它会在父进程中返回子进程的PID,而在子进程中返回0。结果如下:

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

int main() {
    pid_t pid = fork();
    
    if (pid < 0) {
        // fork 失败
        fprintf(stderr, "Fork failed\n");
        return 1;
    } else if (pid == 0) {
        // 子进程执行的代码
        printf("This is the child process. PID: %d\n", getpid());
    } else {
        // 父进程执行的代码
        printf("This is the father process. PID: %d, Child PID: %d\n", getpid(), pid);
    }
    
    return 0;
}

 2.4.进程间通信:

        由于进程拥有独立的内存空间,它们之间无法直接共享数据。常见的进程间通信(IPC)机制包括:1.管道 2.消息队列 3.共享内存 4.信号  5.套接字。

         接下来通过一个简单的举例,来展示进程间是如何进行通信的。首先,我们引用对应的所需的头文件。接下来通过使用int fd【2】来定义一个整型数组fd去存储管道的文件描述符。然后对于管道(上面提及的通信机制之一)我们使用两个文件描述符,fd[0]用于读取数据,fd[1]用于写入数据。

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

int main() {
    int fd[2];
    pipe(fd);
    
    pid_t pid = fork();
    
    if (pid == 0) {
        // 子进程
        close(fd[0]);
        char msg[] = "Hello from child";
        write(fd[1], msg, strlen(msg) + 1);
        close(fd[1]);
    } else {
        // 父进程
        close(fd[1]);
        char buffer[100];
        read(fd[0], buffer, sizeof(buffer));
        printf("father received: %s\n", buffer);
        close(fd[0]);
    }
    
    return 0;
}

3.线程:

3.1.线程的概念:

        进程是一个正在执行的程序实例,它拥有自己独立的地址空间、内存、数据和文件描述符。进程是操作系统进行资源分配和任务管理的基本单位。在Linux中,线程可以使用pthread_create()函数来创建。线程间的通信和数据共享比进程间更容易,因为线程可以直接读写进程的数据段(堆、全局变量等)。

3.2.线程的优点:

        1.创建和删除成本小;2.线程共享进程的地址空间,所以上下文的切换更快;3.线程可以直接访问同一进程的全局变量和堆内存,便于数据共享

3.3.线程的创建和管理:

        在 Linux 中,可以使用 POSIX 线程库(pthread)创建和管理线程。以下是一个,直接使用 pthread 库创建线程的示例(这里只是展示基本框架):

   void* thread_function(void* arg):这是一个线程函数,它将在新线程中执行。这个函数接受一个 void* 类型的参数,但实际上并没有使用它。函数返回 NULL,表示没有返回值。

   pthread_t thread;:定义一个 pthread_t 类型的变量 thread,用于存储新创建线程的ID。

   int result = pthread_create(&thread, NULL, thread_function, NULL);:调用pthread_create 函数创建一个新线程。第一个参数是一个指向 pthread_t 类型变量的指针,用于存储新线程的ID。第二个参数是线程属性,这里设置为 NULL,表示使用默认属性。第三个参数是要在新线程中执行的函数的指针。最后一个参数是传递给线程函数的参数,这里也设置为 NULL(空)

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

void* thread_function(void* arg) {
    printf("Hello from thread! Thread ID: %ld\n", pthread_self());
    return NULL;
}

int main() {
    pthread_t thread;
    int result = pthread_create(&thread, NULL, thread_function, NULL);
    
    if (result != 0) {
        fprintf(stderr, "Error creating thread\n");
        return 1;
    }
    
    pthread_join(thread, NULL);
    printf("Thread has finished execution\n");
    return 0;
}

3.4.线程同步:

        由于线程共享进程的内存空间,多个线程访问共享资源时可能会发生竞争,导致数据不一致。常用的线程同步机制包括互斥锁(mutex)、条件变量(condition variable)和读写锁(rwlock)。

        下面是一个互斥锁的示例(这里的话线程还有很多其他的结束的,锁的方式。如果想了解更多,可以给我评论。这里我们先只掌握最基本的)

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

pthread_mutex_t lock;
int counter = 0;

void* increment_counter(void* arg) {
    pthread_mutex_lock(&lock);
    counter++;
    printf("Counter: %d\n", counter);
    pthread_mutex_unlock(&lock);
    return NULL;
}

int main() {
    pthread_t threads[10];
    pthread_mutex_init(&lock, NULL);
    
    for (int i = 0; i < 10; i++) {
        pthread_create(&threads[i], NULL, increment_counter, NULL);
    }
    
    for (int i = 0; i < 10; i++) {
        pthread_join(threads[i], NULL);
    }
    
    pthread_mutex_destroy(&lock);
    return 0;
}

4.进程和线程的区别:

特性 进程 线程
内存空间 独立 共享
创建开销
上下文切换
通信方式 需要 IPC 机制 直接访问共享内存
崩溃影响 进程崩溃不影响其他进程 线程崩溃可能导致整个进程崩溃

5.总结:

        进程和线程是 Linux 操作系统中多任务处理的关键概念。进程是资源分配的基本单位,拥有独立的内存空间,而线程是执行单元,共享进程的资源。对于二者多加练习,对于在开发大型的项目时会拥有更多的优势。