美文网首页C++多线程linux编程
Linux 多进程、多线程 C 语言实现

Linux 多进程、多线程 C 语言实现

作者: 去旅行_2016 | 来源:发表于2019-03-17 20:22 被阅读81次

    概述

    Linux 是多任务操作系统,可以同时运行多个进程,来完成多项工作。

    进程就是处于活动状态的程序,占用一定的内存空间。进程可以把自己复制一份,从而创造出一个新的进程。新的进程称为 子进程,原来的进程称为 父进程

    进程可以复制自己。这意味着启动一个程序,可能会产生多个进程。这样的程序能同时进行多项工作。多进程编程就是要设计一个这样的程序。

    进程的调度

    实际上 CPU 只能同时处理一个进程的工作。也就是说,进程并不是真的同时都在运行。

    Linux 是一个 分时 操作系统。在同一时刻,只有一个进程得到 CPU 的处理,但很快就会变成另一个进程,如此往复。虽然每个进程一次只占用 CPU 很短的一段时间,但是每个进程总是很快就能再一次占用 CPU,所以这些进程看起来就好像一直都在运行一样。

    CPU 在工作时就好像人体的循环系统一样。心脏的跳动维持血液的流动;晶振的振荡维持 CPU 的运行。心脏一旦停止跳动,血液也就停止流动;晶振一旦停止振荡,CPU 也就停止工作。

    晶振在单位时间内的振荡次数称为 时钟频率,两次振荡之间的时间间隔称为 时钟周期。进程占用 CPU 的时间应该以时钟周期为单位来计量。

    进程的状态

    进程从创建到运行结束,经历的全部过程,称为进程的生命周期。在生命周期的不同阶段,进程会呈现不同的状态。下表列出了进程可能出现的所有状态。

    状态 含义
    创建状态 正在被创建
    就绪 刚刚创建好,还没运行过
    内核状态 运行中
    用户状态 暂停中
    睡眠 已经轮到这个进程上场了,但是它的某些需求得不到满足,只能继续等待
    唤醒 正在睡眠的进程,正在被唤醒
    被抢占 运行到一半,CPU 被另一个进程抢占
    僵死状态 进程已经结束,但记录还在

    进程的控制

    使用下文讨论的几个函数需要包含下面几个头文件。

    #include <stdlib.h> // exit()
    #include <unistd.h> // fork(), sleep(), _exit(), pid_t
    #include <sys/wait.h> // wait()
    

    fork() 创建子进程

    子进程通过 fork() 函数创建。fork() 不需要任何参数,返回值是 pid_t 型。pid_t 型实际上就是 int 型。这是专门用来保存进程 PID(进程的编号)的类型。

    pid_t fork(void);
    

    如果子进程创建成功,fork() 函数将返回子进程的 PID,否则返回 -1

    前面说过,创建子进程相当于把自己复制一份。也就是说,创建出来的子进程和父进程几乎是一模一样的,并且都将接着执行 fork() 函数后面的代码。

    不同的是,对于 fork() 函数的返回值,在子进程中将得到 0。因此,如果在 fork() 函数之后用一个 if 语句对 fork() 函数的返回值进行判断,子进程和父进程将进入不同的分支。

    pid_t cpid;
    
    cpid = fork();
    if (cpid == -1) {
        printf("Create process failed!\n");
        exit(1);
    }
    
    if (cpid == 0) {
        printf("Hello from Child!\n");
    } else {
        printf("Hello from Parent!\n");
    }
    

    sleep() 主动睡眠

    进程调用 sleep() 函数,将进入睡眠状态,传递给 sleep() 函数的参数就是睡眠的持续时间,单位秒。下面代码将使进程进入睡眠状态,持续 3 秒钟。

    sleep(3);
    

    wait() 等待进程结束、exit() 进程结束

    父进程调用 wait(),将进入睡眠状态,以等待子进程进入僵死状态。子进程调用 exit() 将使自己进入僵死状态。

    pid_t wait(int * status);
    void exit(int status);
    

    这两个函数都只有一个参数。exit() 函数的参数是一个整型变量,用来保存一个范围在 0-255 之间的整数。wait() 函数的参数要求一个整型变量的地址,这个整型变量将保存这个整数。

    多线程

    进程进一步细分,就是线程。每一个进程都至少有一个线程,这个线程称为 主线程。主线程就是运行主函数 main() 的线程。创建线程相当于调用一个函数,只不过原来的线程会立即执行后续的代码而不等待这个函数返回。这使得被调函数中的代码和后续的代码是并行执行的。因此,可以简单地认为多线程就是同时运行多个函数。

    历史上曾出现过多种线程标准。这些标准互不兼容,这使得程序员难以开发可移植的应用程序。为此,IEEE 制订了后来被广泛采用的线程标准 POSIX threads,简称 Pthreads。POSIX 线程库 实现了这个标准。POSIX 线程库也是最常用的线程库。使用 POSIX 线程库需要包含头文件 pthread.h

    #include <pthread.h>
    

    由于 POSIX 线程库并不属于默认库,因此在使用 gcc 命令进行编译时,要加上 -lpthread 选项。

    pthread_create() 创建线程

    线程通过调用 pthread_create() 函数创建。

    int pthread_create(
        pthread_t * id, 
        pthread_attr_t * attr, 
        void * (* start_routine)(void *), 
        void * arg
    );
    
    • 第一个参数要求一个 pthread_t 变量的地址。这个变量用来保存线程的标识符

    • 第二个参数要求一个 pthread_attr_t 结构的地址。这个结构用于设定线程的一些属性,一般设为 0

    • 第三个参数要求一个函数。创建的线程将调用这个函数。这个函数称为 线程函数。线程函数必须以一个 void 指针为参数,返回值也必须是一个 void 指针。

    • 第四个参数是一个 void 指针,它将会作为线程函数的参数。如果不需要传参,设为 0

    如果线程创建成功,pthread_create() 函数将返回 0,否则返回要给错误代码。这些错误代码是线程库定义的一些常量,但没有一个是 -1

    pthread_exit() 线程结束

    线程调用 pthread_exit() 函数可结束自己,这个函数相当于结束进程的 exit()

    void pthread_exit(void * retval);
    

    唯一的参数是一个 void 指针,用来指向返回值。

    pthread_join() 等待线程结束

    可以调用 pthread_join() 函数来等待另一个线程结束。

    int pthread_join(pthread_t id, void ** retval);
    
    • 第一个参数要求一个线程的标识符

    • 第二个参数要求一个 void 指针的地址。这个指针将被指向线程的返回值。如果不需要得到线程的返回值,可设为 0

    如果顺利,pthread_join() 函数将返回 0,否则返回一个错误代码。

    下面是一个完整的例子。演示了从创建线程到结束线程的过程。

    #include <stdio.h>
    #include <stdlib.h> // exit()
    #include <unistd.h> // fork(), sleep(), _exit(), pid_t
    #include <pthread.h>
    
    void * hello(void * arg)
    {
        printf("Thread start running!\n");
        printf("%s\n", (char *)arg);
        sleep(3);
        pthread_exit("Hello from thread!");
    }
    
    int main(void)
    {
        pthread_t id;
        void * thread_retval;
    
        if (pthread_create(&id, 0, hello, "Hello from main!") != 0) {
            printf("Create thread failed!\n");
            exit(1);
        }
    
        pthread_join(id, &thread_retval);
        printf("%s\n", (char *)thread_retval);
        return 0;
    }
    

    pthread_detach() 脱离同步

    pthread_detach() 函数用来使一个线程与其他线程脱离同步。脱离同步是指其他线程不能用 pthread_join() 函数来等待这个线程结束。这个线程将在退出时自行释放所占的资源。

    int pthread_detach(pthread_t id);
    

    pthread_detach() 函数唯一的参数就是需要脱离同步的线程的标识符。如果顺利,将返回 0,否则返回一个错误代码。

    相关文章

      网友评论

        本文标题:Linux 多进程、多线程 C 语言实现

        本文链接:https://www.haomeiwen.com/subject/pwnpeqtx.html