zoukankan      html  css  js  c++  java
  • Linux进程&线程

    1.进程

    进程的引入是因为为了满足多用户多任务的操作的需要,对于每个任务需要分别分配一个进程进行控制。对于每个进程很显然要标识它所拥有的资源。所以这里就引入了PCB(process control block)这个概念,对于每个PCB会有保存的各个资源变量。当你有了PCB的概念后,紧接着就是面临三个问题就是。

      1.如何创建一个进程

      2. 在新建的一个进程里如何执行自己的程序

      3. 创建的函数如何回收资源

    这样就引出了三个函数,fork(),execve()以及wait()函数。fork()函数是创建新进程的函数,通过一次调用两次返回,来实现对父进程以及子进程的判断。execve是在新创建的进程中如何load用户想执行程序的函数。execve函数分为以下几个函数。

    1 #include <unistd.h>
    2 
    3 int execl(const char *path, const char *arg, ...);
    4 int execlp(const char *file, const char *arg, ...);
    5 int execle(const char *path, const char *arg, ..., char *const envp[]);
    6 int execv(const char *path, char *const argv[]);
    7 int execvp(const char *file, char *const argv[]);
    8 int execve(const char *path, char *const argv[], char *const envp[]);

    其中函数中如果缺少字母p(path) l(list) e(enviroment)则在参数中必须给出。其中execve是系统调用,其它的函数实际上还是调用它来完成功能。 

    wait()函数是清理子进程的函数。如果一个进程结束了,父进程没有调用wait()对其清理的话,那么这个进程叫做僵尸进程。而如果父进程结束了,而子进程未结束,那么这个进程的父进程改为init进程,init进程会自动清理子进程。处理僵尸进程的一个方法是利用信号来处理,因为子进程结束时会给父进程发送SIGCHILD信号,这时父进程把该信号的默认处理函数改为清理函数即可。

    1 #include <sys/types.h>
    2 #include <sys/wait.h>
    3 
    4 pid_t wait(int *status);
    5 pid_t waitpid(pid_t pid, int *status, int options);

    对于wait以及waitpid主要有两个方面的区别

    1. waitpid可以通过pid指定要清理的进程

    2.其中OPTIONS指定WNOHANG可以使得waitpid不阻塞

    另一个话题就是进程间的通信问题。

    linux进程间的通信方法总结如下

    • 通过fork函数把打开文件的描述符传递给子进程
    • 通过wait得到子进程的终结信息
    • 通过加锁的方式,实现几个进行共享读写某个文件
    • 进行间通过信号通信,SIGUSR1和SIGUSR2实现用户定义功能
    • 利用pipe进行通信
    • FIFO文件进行通信
    • mmap,几个进程映射到同一内存区(其中有个MAP_SHARED参数)
    • SYS IPC 消息队列,信号量(很少用)
    • UNIX Domain Socket,常用

    其中利用pipe函数是在内核中开辟一个缓冲区进行数据的传送,但是如果通信的进程间没有从公共的父进程哪里继承文件描述符,那么要考虑使用FIFO/Socket文件进行通信,因为文件的位置是一定的,FIFO以及Socket其实不是实际存在的文件,只是用来表示内核通道的文件。

    2.线程

    因为进程在创建以及切换的过程中消耗的资源很大,所以就引入了线程的手段。线程其实也被称作轻量集的进程。所以这里我们也可以利用学习进程的三个方面的问题来学习线程。

      1.如何创建线程

      2.如何在线程中加载要运行的函数。

      3.如何回收/结束创建的线程

    这里线程的创建以及加载运行所要的函数都是通过pthread_create函数来实现的。

    1 #include <pthread.h>
    2 
    3 int pthread_create(pthread_t *restrict thread,
    4     const pthread_attr_t *restrict attr,
    5     void *(*start_routine)(void*), void *restrict arg);

    其中thread是线程的pid,start_routine是线程要执行的函数指针, arg是其参数。attr则是指的线程的属性。

    结束某个线程而又不结束整个进程,可以有三个方法。

      1. 从线程函数return, 

      2. pthread_cancel终止同一个进程的另一个线程

      3. pthread_exit终止自己

    与waitpid一样我们有时也需要获取结束线程是否成功,这样就引入了pthread_join()函数

    1 #include <pthread.h>
    2 
    3 int pthread_join(pthread_t thread, void **value_ptr);

    其主要作用是挂起等待线程的结束,并通过vaule_ptr返回其值。

    由于线程中很多资源都是进程中共享的,当频繁的对进程进行读写操作的时候,很容易产生访问共享数据冲突的情形,所以需要引入锁来解决这个问题。

     1 #include <pthread.h>
     2 
     3 int pthread_mutex_destroy(pthread_mutex_t *mutex);
     4 int pthread_mutex_init(pthread_mutex_t *restrict mutex,
     5        const pthread_mutexattr_t *restrict attr);
     6 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
     7 
     8 int pthread_mutex_lock(pthread_mutex_t *mutex);
     9 int pthread_mutex_trylock(pthread_mutex_t *mutex);
    10 int pthread_mutex_unlock(pthread_mutex_t *mutex);

    其中lock以及trylock的区别是一个会阻塞,一个不会阻塞。锁所带来的一个问题就是死锁问题,死锁就是多个线程加锁与解锁的顺序不对导致其全部阻塞。这里为了防止死锁。首先要尽量避免多次同时获取多个锁。实在无法避免则所有的线程按照顺序获取与释放,如获取锁1,锁2,锁3,释放锁1,锁2,锁3.如果这个都无法保证那么就使用trylock来代替lock。

    另外线程间同步机制还有其它两种方式分别是条件变量以及信号量。

    条件变量的同步主要是为了满足这样的一个条件,那就是当线程A阻塞时,需要线程B在某个时间唤醒它。也就是说通过线程B给其发信号。而信号量则主要是可是实现多个线程可以同时进行的锁。互斥锁可以看作其一种特殊的情形。信号量也可以使用在进程间同步中。

    线程间通信方式如下

    • 锁机制
    • 信号量机制
    • 信号机制 
    • 全局变量
    • 参数传递

    3. 线程与进程的比较

    • 地址空间:进程内的一个执行单元;进程至少有一个线程;它们共享进程的地址空间;而进程有自己独立的地址空间;
    • 资源拥有:进程是资源分配和拥有的单位,同一个进程内的线程共享进程的资源
    • 线程是处理器调度的基本单位,但进程不是.
    • 二者均可并发执行.
  • 相关阅读:
    Flash向量1点和向量
    Flash/Flex学习笔记(2):捕获摄像头
    Flash向量0序言
    Flash向量3加法和投影
    Flash/Flex学习笔记(3):动态添加组件
    Flash/Flex学习笔记(5):捕获摄像头(续)在线抓屏并保存到客户端本地
    异常写法
    关于存储过程得传出参数的错误
    python Beautiful Soup模块的安装
    python学习笔记比较全
  • 原文地址:https://www.cnblogs.com/qtalker/p/4582059.html
Copyright © 2011-2022 走看看