Linux系统编程学习
王凌波
积硅步以至千里!
展开
-
内存共享 - mmap
函数#include<sys/mman.h>/* 申请内存并映射 */void mmap(void *addr, size_t len, int prot,int flags, int fildes, off_t off)/* 同步数据 */int msync(void *addr, size_t len, int flags);/* 解除映射,并释放内存 */in...原创 2019-11-06 20:48:07 · 236 阅读 · 0 评论 -
有名信号量
信号量分两种,一种是无名信号量,一种是有名信号量。无名信号量一般用于线程间同步或互斥,而有名信号量一般用于进程间同步或互斥。它们的区别和管道及命名管道的区别类似,无名信号量则直接保存在内存中,而有名信号量要求创建一个文件。函数#include <fcntl.h>#include <sys/stat.h>#include <semaphore.h>...原创 2019-10-31 22:03:38 · 1794 阅读 · 2 评论 -
无名信号量
信号量广泛用于进程或线程间的同步和互斥(一般用于线程,进程间常用有名管道),信号量本质上是一个非负的整数计数器,它被用来控制对公共资源的访问。编程时可根据操作信号量值的结果判断是否对公共资源具有访问的权限,当信号量值大于 0 时,则可以访问,否则将阻塞。PV 原语是对信号量的操作,一次 P 操作使信号量减1,一次 V 操作使信号量加1。用于进程或线程间互斥:初始化信号量为1; 执行需要互...原创 2019-10-30 21:32:07 · 464 阅读 · 0 评论 -
线程-读写锁
当有一个线程已经持有互斥锁时,互斥锁将所有试图进入临界区的线程都阻塞住。但是考虑一种情形,当前持有互斥锁的线程只是要读访问共享资源,而同时有其它几个线程也想读取这个共享资源,但是由于互斥锁的排它性,所有其它线程都无法获取锁,也就无法读访问共享资源了,但是实际上多个线程同时读访问共享资源并不会导致问题。在对数据的读写操作中,更多的是读操作,写操作较少,例如对数据库数据的读写应用。为了满足当前能够允许...原创 2019-10-28 21:15:19 · 371 阅读 · 0 评论 -
线程互斥锁
当不同线程同时访问同一个数据时,如果不加任何处理的话,就可能出现数据错乱。用一个例子说明:#include<pthread.h>#include<stdio.h>#include<unistd.h>int num = 10;void* call_back1(void* arg){ num += 1; /* 假设这里执行了很多...原创 2019-10-17 20:04:36 · 396 阅读 · 0 评论 -
线程的私有数据
在多线程程序中,经常要用全局变量来实现多个函数间的数据共享。由于数据空间是共享的,因此全局变量也为所有线程共有。但有时应用程序设计中必要提供线程私有的全局变量,这个变量仅在线程中有效,但却可以跨过多个函数访问。比如在程序里可能需要每个线程维护一个链表,而会使用相同的函数来操作这个链表,最简单的方法就是使用同名而不同变量地址的线程相关数据结构。这样的数据结构可以由 Posix 线程库维护,成为线程私...原创 2019-10-16 20:36:53 · 809 阅读 · 0 评论 -
线程基础
进程是维护应用程序所需的各种资源,并不执行什么。而线程才是真正的执行实体。为了让进程完成一定的工作,进程必须至少包含一个线程。进程,直观点说,保存在硬盘上的程序运行以后,会在内存空间里形成一个独立的内存体,这个内存体有自己的地址空间,有自己的堆,上级挂靠单位是操作系统。操作系统会以进程为单位,分配系统资源,所以我们也说,进程是资源分配的最小单位。线程存在与进程当中,是操作系统调度执行的最小单位...原创 2019-10-14 21:32:59 · 137 阅读 · 0 评论 -
共享内存 --- shm
共享内存是进程间通信中最简单的方式之一。共享内存允许两个或更多进程访问同一块内存,就如同 malloc() 函数向不同进程返回了指向同一个物理内存区域的指针。当一个进程改变了这块地址中的内容的时候,其它进程都会察觉到这个更改。其特点是:共享内存是进程间共享数据的一种最快的方法。一个进程向共享的内存区域写入了数据,共享这个内存区域的所有进程就可以立刻看到其中的内容。 使用共享内存要注意的是多个...原创 2019-09-03 22:08:44 · 2583 阅读 · 0 评论 -
消息队列
消息队列可用于两个完全不相关的进程间的通信,它有以下特点:消息队列可以实现消息的随机查询。消息不一定要以先进先出的次序读取,编程时可以按消息的类型读取。 消息队列允许一个或多个进程向它写入或者读取消息。 与无名管道、命名管道一样,从消息队列中读出消息,消息队列中对应的数据都会被删除。 每个消息队列都有消息队列标识符,消息队列的标识符在整个系统中是唯一的。 消息队列是消息的链表,存放在内...原创 2019-09-02 22:31:06 · 504 阅读 · 0 评论 -
有名管道
无名管道只适用于有亲缘关系的进程间的通信,那么无亲缘关系的进程间如何通信呢?有名管道正是为了解决这个问题。有名管道(FIFO)不同于无名管道之处在于它提供了一个路径名与之关联,以 FIFO 的文件形式存在于文件系统中,这样,即使与 FIFO 的创建进程不存在亲缘关系的进程,只要可以访问该路径,就能够彼此通过 FIFO 相互通信,因此,通过 FIFO 不相关的进程也能交换数据。有名管道和无名管道...原创 2019-08-26 22:28:32 · 4078 阅读 · 0 评论 -
无名管道
特点1、半双工,数据在同一时刻只能在一个方向上流动。 2、数据只能从管道的一端写入,从另一端读出。同意进程可以访问管道两端,也可以自发自收。 3、写入管道中的数据遵循先入先出的规则。若连续写入多次再读出,则将多次写入的内容一并读出。 4、管道所传送的数据是无格式的,这要求管道的读出方与写入方必须事先约定好数据的格式,如多少字节算一个消息等。 5、管道不是普通的文件,不属于某个文件系统,...原创 2019-08-25 11:32:14 · 454 阅读 · 0 评论 -
可靠信号的用法
signal() 函数只能提供简单的信号安装操作,使用该函数处理信号比较简单,只要把要处理的信号和处理函数列出即可。signal() 函数主要用于从 Unix 继承过来的不可靠、非实时信号的处理,并且不支持信号传递信息。Linux 提供了功能更强大的 sigaction() 函数,此函数可以用来检查和更改信号处理操作,可以支持可靠、实时信号的处理,并且支持信号传递信息。先看 kill() 的升...原创 2019-08-05 22:48:14 · 227 阅读 · 0 评论 -
信号集与信号阻塞集
信号集有时候一个进程需要对多个信号进行处理,如果一个一个信号去判断,那会很蛋疼。我们可以用信号集来很好地解决这个蛋疼的问题。顾名思义,信号集是一个信号集合。数据类型为 sigset_t . 对信号集的操作主要有一下几个函数:#include <signal.h> int sigemptyset(sigset_t *set); int sigfillset(sigset...原创 2019-08-05 22:23:36 · 331 阅读 · 0 评论 -
信号的发送与处理
在 Linux 中,信号是一种软件中断。当一个进程正在运行时,收到另一个进程的信号,会产生中断,转而去处理突发事件。Linux 中的信号可以用 kill -l 去查看。其中,1- 31号信号是古老 Unix 就支持的信号,属于不可靠信号(非实时信号,不支持排队,可能会造成信号丢失);而34 - 64 为可靠信号(实时信号,支持排队,不会造成信号丢失)。[lingyun@manjaro ~]$...原创 2019-08-04 22:41:23 · 383 阅读 · 0 评论 -
进程替换 --- exec
在 Linux 中,exec 指的是一组函数,一共有 6 个。其中只有execve()是真正意义上的系统调用,其它都是在此基础上经过包装的库函数。当进程调用一种 exec 函数时,该进程完全由新程序替换,而新程序则从其 main 函数开始执行。因为调用 exec 并不创建新进程,所以前后的进程 ID (当然还有父进程号、进程组号、当前工作目录……)并未改变。exec 只是用另一个新程序替换了当...原创 2019-08-03 12:26:28 · 423 阅读 · 0 评论 -
守护进程
啥叫守护进程不想打字,抄一段,嘿嘿嘿嘿嘿。。。。。。。。。。。守护进程(Daemon Process),也就是通常说的 Daemon 进程(精灵进程),是 Linux 中的后台服务进程。它是一个生存期较长的进程,通常独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。守护进程是个特殊的孤儿进程,这种进程脱离终端,为什么要脱离终端呢?之所以脱离于终端是为了避免进程被任何终端所产...原创 2019-07-30 22:08:04 · 168 阅读 · 0 评论 -
孤儿进程
孤儿进程,没爹。就是父进程已经退出了,但子进程还在运行。但是没爹怎么行?认个干爹呗,于是子进程就认 init 进程为干爹, init 进程就成了该子进程的新父进程。 此后,init 进程负责回收子进程的资源。所以制造孤儿进程并没有什么坏处,甚至有个好处,即可以解决僵尸进程的问题。通过下面的代码了解孤儿进程的产生。代码#include <stdio.h>#include &l...原创 2019-07-19 08:08:40 · 281 阅读 · 0 评论 -
僵尸进程
如果一个进程已经退出,但是占用的资源未被回收,则称这样的进程为僵尸进程。很容易想到,如果子进程退出后,父进程未用 wait() 或 waitpid() 去回收子进程的资源,就会形成僵尸进程。如果一个程序循环创建子进程,但忘记回收退出的子进程的资源。那么这些子进程就会一直占用进程号。而系统的进程号是有限的,这个程序长时间运行后就会耗尽系统的进程号,从而导致无法产生新的进程。代码#inclu...原创 2019-07-18 22:45:53 · 211 阅读 · 0 评论 -
等待进程结束--- wait() 和 waitpid()
在进程退出时,内核会释放掉该进程的所有资源,包括打开的文件和占用的内存等。但是仍然为该进程保留一些信息,主要指进程控制块的信息(包括进程号、退出状态、运行时间等)。所以为了回收这些资源,进程退出时会向他的父进程发送一个 SIGCHLD 信号,通知父进程来获取子进程结束时的状态,同时回收资源。wait() 和 waitpid() 的区别其实这两个函数功能差不多,只是 wait() 会阻塞,而...原创 2019-07-17 22:39:53 · 936 阅读 · 0 评论 -
进程退出--- exit() 和 _exit()
exit() 和 _exit() 的区别void exit(int value)是标准库函数,在头文件 stdlib.h 中定义。具有刷新 I/O 缓冲区的作用。他的参数 value 是返回给父进程的数据(低 8 位有效)。void _exit(int value)是系统调用函数,在头文件 unistd.h 中定义。没有刷新 I/O 缓冲区的作用。他的参数 value 也是返回给父...原创 2019-07-17 21:15:12 · 939 阅读 · 0 评论 -
多进程---vfork()
vfork() 和 fork() 的区别:fork() :a. 首先返回的是0,还是子进程ID是由系统调度决定的,是不确定的。b. 父子进程的地址空间是独立的。vfork() :a. 必定首先返回0,保证让子进程先执行,待子进程退出(exit)或进行进程替换(exec)之后,父进程才会被调用。b. 在子进程退出(exit)或进行进程替换(exec)之前,父子进程的地址空间是...原创 2019-07-16 20:42:15 · 220 阅读 · 0 评论 -
多进程---fork()
fork() 函数的使用很简单。fork() 函数被调用一次,但返回两次。两次返回的区别是:子进程的返回值是 0,而父进程的返回值则是新子进程的进程 ID。故我们可以通过判断返回值来区分父子进程。如果不做判断,fork() 后的代码父子进程会各执行一遍。一个进程调用 fork() 函数后,系统先给新的进程分配资源,例如存储数据和代码的空间。然后把原来的进程的所有值都复制到新的新进程中,只有少...原创 2019-07-14 22:42:17 · 843 阅读 · 1 评论 -
Copy文件描述符
复制文件描述符可以用Linux系统提供的两个系统调用 dup(), dup2(),复制后,新的描述符也标识旧描述符所标识的文件。这样的话,原来的文件描述符和新的文件描述符都指向同一个文件,我们操作这两个文件描述符的任何一个,都能操作它所对应的文件。就像配钥匙一样,新钥匙和旧钥匙都能打开原来的那把锁。(这个类比是从网上看到的,感觉相当形象。)dup()#include<stdio.h...原创 2019-07-14 12:46:02 · 197 阅读 · 0 评论 -
文件I/O的系统调用
文件描述符Linux系统中对文件进行操作的系统调用函数有 open(), close(), read(), write() 等等........关于这些函数需要的头文件和用法可以用>man 函数名去查询。当open一个现存文件或新建文件时,系统(内核)会返回一个文件描述符,文件描述符用来指定已打开的文件。这个文件描述符相当于这个已打开文件的标号,文件描述符是非负整数,是文件...原创 2019-07-14 11:54:39 · 250 阅读 · 0 评论