Linux线程同步
一、线程同步的概念
线程同步?怎么同步?一起运行?一起停止?我当年听说线程同步这个词的时候,也是一头雾水。
在人们的日常生活中的锁大概有两种:一种是不允许访问;另一种是资源忙,同一时间只允许一个使用者占用,其它使用者必须要等待。
1)不允许访问的锁容易理解,就像每家每户的门锁,不允许外人进入。
2)第二种锁,例如火车上的厕所,它是公共的,空闲的时候任何人可以进入,人进去以后就会把它锁起来,其它的人如果要上厕所,必须等待解锁,即里面的人出来。还有红绿灯,红灯是加锁,绿灯是解锁。
对多线程来说,资源是共享的,基本上不存在不允许访问的情况,但是,共享的资源在某一时间点只能有一个线程占用,所以需要给资源加锁。
不知道是什么人采用了线程同步这个词,如果让我的命名,我会定义为线程锁,锁线程吗?不是,是锁共享资源,线程给共享资源加的锁。
线程的锁的种类有互斥锁、读写锁、条件变量、自旋锁、信号灯。
在本章节中,只介绍互斥锁,其它的锁应用场景复杂,开发难度很大,不合适初学者。
二、互斥锁
互斥锁机制是同一时刻只允许一个线程占有共享的资源。
1、初始化锁
1int pthread_mutex_ini ...
Linux多线程
一、线程的概念
和多进程相比,多线程是一种比较节省资源的多任务操作方式。启动一个新的进程必须分配给它独立的地址空间,每个进程都有自己的堆栈段和数据段,系统开销比较高,进行数据的传递只能通过进行间通信的方式进行。在同一个进程中,可以运行多个线程,运行于同一个进程中的多个线程,它们彼此之间使用相同的地址空间,共享全局变量和对象,启动一个线程所消耗的资源比启动一个进程所消耗的资源要少。
二、线程的使用
1、创建线程
在Linux下,采用pthread_create函数来创建一个新的线程,函数声明:
函数声明:
1int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);
参数thread为为指向线程标识符的地址。
参数attr用于设置线程属性,一般为空,表示使用默认属性。
参数start_routine是线程运行函数的地址,填函数名就可以了。
参数arg是线程运行函数的参数。新创建的线程从start_routine函数的地址开始 ...
Linux信号量
一、信号量的概念
信号量(信号灯)本质上是一个计数器,用于协调多个进程(包括但不限于父子进程)对共享数据对象的读/写。它不以传送数据为目的,主要是用来保护共享资源(信号量、消息队列、socket连接等),保证共享资源在一个时刻只有一个进程独享。
信号量是一个特殊的变量,只允许进程对它进行等待信号和发送信号操作。最简单的信号量是取值0和1的二元信号量,这是信号量最常见的形式。
通用信号量(可以取多个正整数值)和信号量集方面的知识比较复杂,应用场景也比较少。
本文只介绍二元信号量。
二、相关函数
Linux中提供了一组函数用于操作信号量,程序中需要包含以下头文件:
123#include <sys/types.h>#include <sys/ipc.h>#include <sys/sem.h>
1、semget函数
semget函数用来获取或创建信号量,它的原型如下:
1int semget(key_t key, int nsems, int semflg);
1)参数key是信号量的键值,typedef unsigned int key_t,是信 ...
Linux共享内存
一、共享内存的概念
共享内存(Shared Memory)就是允许多个进程访问同一个内存空间,是在多个进程之间共享和传递数据最高效的方式。操作系统将不同进程之间共享内存安排为同一段物理内存,进程可以将共享内存连接到它们自己的地址空间中,如果某个进程修改了共享内存中的数据,其它的进程读到的数据也将会改变。
共享内存并未提供锁机制,也就是说,在某一个进程对共享内存的进行读写的时候,不会阻止其它的进程对它的读写。如果要对共享内存的读/写加锁,可以使用信号灯。
二、相关函数
Linux中提供了一组函数用于操作共享内存,程序中需要包含以下头文件:
12#include <sys/ipc.h>#include <sys/shm.h>
1、shmget函数
shmget函数用来获取或创建共享内存,它的声明为:
1int shmget(key_t key, size_t size, int shmflg);
参数key是共享内存的键值,是一个整数,typedef unsigned int key_t,是共享内存在系统中的编号,不同共享内存的编号不能相同,这一点由程序员保证 ...
Linux信号
一、如何让程序在后台运行
在之前的章节中,如果要运行程序,在命令提示行下输入程序名后回车,程序被执行,然后等待程序运行完成,在程序运行的过程中,也可以用Ctrl+c中止它。
在实际开发中,我们需要让程序在后台运行,没有界面,没有用户输入数据,例如socket服务端程序book250。
如果想让程序在后台运行,有两种方法。
1、加“&”符号
如果想让程序在后台运行,执行程序的时候,命令的最后面加“&”符号。
如:
1./book250 &
程序就在后台运行了。
在后台运行的程序,用Ctrl+c无法中断,并且就算终端退出了,程序仍在后台运行。
如果终端退出了,后台运行的程序将由系统托管。
在第一张图中,book250的父进程是12178,第二张图中,book250的父进程是1。
为了不影响接下来的学习,用killall book250指令让book250程序退出。
2、采用fork
另一种方法是采用fork,主程序执行fork,生成一个子进程,然后父进程退出,留下子进程继续运行,子进程将由系统托管。
在book250的main函数后增加以下代码:
1if ...
Linux进程间通信
进程的数据空间是独立的,私有的,不能相互访问,但是在某些情况下进程之间需要通信来实现某功能或交换数据,包括:
1)数据传输:一个进程需要将它的数据发送给另一个进程。
2)共享数据:多个进程想要操作共享数据,一个进程对共享数据的修改,别的进程应该立刻看到。
3)通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如通知进程退出)。
4)进程控制:一个进程希望控制另一个进程的运行。
一、进程通信
进程通信的方式大概分为六种。
1)管道:包括无名管道(pipe)及命名管道(named pipe),无名管道可用于具有父进程和子进程之间的通信。命名管道克服了管道没有名字的限制,因此,除具有管道所具有的功能外,它还允许无亲缘关系进程间的通信。
2)消息队列(message):进程可以向队列中添加消息,其它的进程则可以读取队列中的消息。
3)信号(signal):信号用于通知其它进程有某种事件发生。
4)共享内存(shared memory):多个进程可以访问同一块内存空间。
5)信号量(semaphore):也叫信号灯,用于进程之间对共享资源进行加锁。
6)套接字(s ...
Linux多进程的应用
前面的章节介绍socket通信的时候,socket的服务端在同一时间只能和一个客户端通信,并不是服务端有多忙,而是因为单进程的程序在同一时间只能做一件事情,不可能一边等待客户端的新连接一边与其它的客户端进行通信。
一、并发的服务端
如果把socket服务端改为多进程,在每次accept到一个客户端的连接后,生成一个子进程,让子进程负责和这个客户端通信,父进程继续accept客户端的连接,socket的服务端在监听新客户端的同时,还可以与多个客户端进行通信。这就是并发,如下图:
1、服务端
把book248.cpp修改一下,改为多进程。
示例(book250.cpp)
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106 ...
Linux多进程
一、进程的概念
什么是进程?进程这个概念是针对系统而不是针对程序员的,对程序员来说,我们面对的概念是程序,当输入指令执行一个程序的时候,对系统而言,它将启动一个进程。
进程就是正在内存中运行中的程序,Linux下一个进程在内存里有三部分的数据,就是“代码段”、”堆栈段”和”数据段”。”代码段”,顾名思义,就是存放了程序代码。“堆栈段”存放的就是程序的返回地址、程序的参数以及程序的局部变量。而“数据段”则存放程序的全局变量,常数以及动态数据分配的数据空间(比如用new函数分配的空间)。
系统如果同时运行多个相同的程序,它们的“代码段”是相同的,“堆栈段”和“数据段”是不同的(相同的程序,处理的数据不同)。
二、进程的编号
1、查看进程
ps 查看当前终端的进程。
ps -ef 查看系统全部的进程。
ps -ef |more 查看系统全部的进程,结果分页显示。
UID :启动进程的操作系统用户。
PID :进程编号。
PPID :进程的父进程的编号。
C :CPU使用的资源百分比。
STIME :进程启动时间。
TTY :进程所属的终端。
TIME ...
封装socket通信类
book241.cpp和book242.cpp程序已经有点长了,有些啰嗦了,如果还想扩展功能,或用于多进程、多线程,程序结构将非常复杂。
不管是socket通信程序的客户端还是服务端,准备工作的代码又长又难看占地方,影响了主程序的结构,必须分离出来。
如何分离? 封装。
一、C的封装方法
C语言只能把程序代码封装成函数。
1、客户端
示例(book245.cpp)
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162/* * 程序名:book245.cpp,此程序用于演示用C语言的方法封装socket客户端 * 作者:C语言技术网(www.freecplus.net) 日期:20190525*/#include <stdio.h>#include <string.h>#include <unistd.h>#include <netdb.h>#include & ...
网络通信基础socket
各位兄弟,在学习Linux编程基础之前,一定要先学习Linux基础知识和计算机网络基础知识,如果对这两方面的基础知识和基本概念不熟,谈不上Linux编程和网络通信编程。
一、socket通信的概念
socket也称作“套接字”,描述了计算机的IP地址和端口,运行在计算机中的程序之间采用socket进行数据通信。通信的两端都有socket,它是一个通道,数据在两个socket之间进行传输。
socket把复杂的TCP/IP协议族隐藏在socket接口后面,对程序员来说,只要用好socket相关的函数,就可以完成数据通信。
二、套接字(socket)
TCP提供了流(stream)和数据报(datagram)两种通信机制,所以套接字也分为流套接字和数据报套接字。
流套接字的类型是SOCK_STREAM,它提供的是一个有序、可靠、双向字节流的连接,因此发送的数据可以确保不会丢失、重复或乱序到达,而且它还有出错后重新发送的机制(就像两个人在打电话,聊天您一句我一句,有来有往,没听清楚就再说一次)。
数据报套接的类型是SOCK_DGRAM,它不需要建立和维持一个连接,采用UDP/IP协议实现 ...