《LINUX系统编程》笔记p10
semop 函数
作用:对信号量进行增、减操作。
增操作(V操作):会唤醒等待信号量的进程。
减操作(P操作):如果减后的结果小于零,进程会阻塞(睡眠),直至信号量增加值可以减少的值。
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>int semop(int semid, struct sembuf *sops, size_t nsops);// 参数:
// semid 要操作的信号量
// sops ,sembuf 数组的起始地址。
// nsops sembuf 数组的元素个数。
struct sembuf 结构体成员
unsigned short sem_num; /* 信号量数组下标 */
short sem_op; /* 信号量操作的值 */
short sem_flg; /* 操作标志 */
// sem_flg 一般为0,IPC_NOWAIT 表示非阻塞、SEM_UNDO表示进程退出时该进程所有的操作同时撤销。
P 操作的示例
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <unistd.h>int main(int argc, char * argv[]) {key_t sem_id;struct sembuf semb[1];// 创建信号量sem_id = semget(1000, 1, IPC_CREAT|0664);if (-1 == sem_id) {perror("semget");return 1;}// 我们每5秒获取一个信号量,如果能够获取则打印获取成功.int product_count = 0;while(1) {semb[0].sem_num = 0; // 下标为零的信号量semb[0].sem_op = -1; // 信号量的值减-1测试。semb[0].sem_flg = 0; // 默认操作printf("等待中...");fflush(NULL);int ret = semop(sem_id, semb, 1);product_count++;printf("获取成功: product_count:%d\n", product_count);sleep(5);}return 0;
}
v 操作的示例
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <unistd.h>int main(int argc, char * argv[]) {key_t sem_id;struct sembuf semb[1];// 创建信号量sem_id = semget(1000, 1, IPC_CREAT|0664);if (-1 == sem_id) {perror("semget");return 1;}// 每隔4秒将信号量+1操作int product_count = 0;while(1) {semb[0].sem_num = 0; // 下标为零的信号量semb[0].sem_op = +1; // 信号量的值减-1测试。semb[0].sem_flg = 0; // 默认操作int ret = semop(sem_id, semb, 1);product_count++;printf("生产产品 product_count:%d\n", product_count);sleep(2);}return 0;
}
共享内存(Shared Memory)
共享内存是指在内核中分配一段内存,让多个进程都可以使用这段内存进行通信的方式。
共享内存是最快的进程将通信的机制。
共享内存通常需要配合信号量一起使用来实现互斥。
共享内存相关的函数:
- shmget(2),创建和获取进程的ID。
- shmat(2) 映射内存到当前进程。
- shmdt(2) 解除内存映射。
- shmctl(2) 设置和获取共享内存的参数或删除共享内存。
共享内存写示例
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <unistd.h>
#include <sys/shm.h>// 定义共享内存的长度
#define SHM_SIZE (1024)int main(int argc, char * argv[]) {key_t shm_id;int ret;char *memory = NULL;shm_id = shmget(1000, SHM_SIZE, IPC_CREAT|0664);if (-1 == shm_id) {perror("shmget");return 1;}// 将共享内存映射到当前程序memory = (char*) shmat(shm_id, NULL, 0);// 循环收入字符从来修改共享内存while(1) {printf("请输入:");fflush(NULL);ret = read(0, memory, SHM_SIZE);memory[ret-1] = 0;if (0 == strcmp(memory, "exit"))break;}// 解除映射shmdt(memory);return 0;
}
共享内存读示例
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <unistd.h>
#include <sys/shm.h>// 定义共享内存的长度
#define SHM_SIZE (1024)int main(int argc, char * argv[]) {key_t shm_id;int ret;char *memory = NULL;shm_id = shmget(1000, SHM_SIZE, IPC_CREAT|0664);if (-1 == shm_id) {perror("shmget");return 1;}// 将共享内存映射到当前程序memory = (char*) shmat(shm_id, NULL, 0);// 循环收入字符从来修改共享内存while(1) {printf("memory:%s\n", memory);sleep(2);}// 解除映射shmdt(memory);return 0;
}
文件内存映射 mmap(2) 和 munmap(2)
相关函数
#include <sys/mman.h>void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
// 参数:
//. addr 建议的其实地址,NULL则内核自动选择返回地址。
// length 内存映射的长度(从offset开始).
// prot, 权限
// PROT_READ 读取权限,
// PROT_WRITE 写入权限,
// flags 创建标志,MAP_SHARED 表示共享内存。
// fd 文件描述符。
// off_set 需要映射的文件的起始地址。
// 返回值: 成功返回内存地址,错误返回(void*)-1.// 解除映射
int munmap(void *addr, size_t length);
// 参数:
// addr :mmap返回的地址。
// length:mmap映射是内存长度
示例:
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>#define FILENAME "./a.txt"
#define FILESIZE (32)int main(int argc, char * argv[]) {int fd;int ret;char * memory = NULL;fd = open(FILENAME, O_CREAT|O_RDWR, 0644);if (-1 == fd) {perror("open");return 1;}// 将文件扩大到 FILESIZE 长度ret = ftruncate(fd, FILESIZE);if (-1 == ret) {perror("ftruncate");close(fd);return 3;}// 进行内存映射memory = mmap(NULL, FILESIZE, PROT_READ|PROT_WRITE, MAP_SHARED,fd, 0);if (MAP_FAILED == memory) {perror("mmap");close(fd);return 2;}printf("file content:%s\n", memory);*(memory+8) = 'a'; // memory[8] = 'a';memory[28] = 'X';printf("file content:%s\n", memory);// 关闭内存映射munmap(memory, FILESIZE);// 关闭文件close(fd);return 0;
}
线程
什么线程
线程是进程内部的一个执行单元,是程序运行的最小单位。它一般负责进程中具体的任务,并共享进程的资源。
各个线程共享进程的资源包括进程中打开的文件,内存等。
线程有自己独立的栈。
进程是资源管理的最小单位。一个进程至少要有一个线程。
POSIX 的标准线程
接口函数都以 pthread_
开头。
进程的标识: PID
线程的标识: pthread_t 的结构体。
包含关系:
一个操作系统进程创建了多个用户级别的进程。一个进程可以包含有一个或多个线程(一个进程至少要有一个线程)。
进程和线程比较
进程 | 线程 | |
---|---|---|
启动 | 慢 | 块 |
占用资源 | 有自己独立的堆、栈、代码段、数据段 | 共享进程资源,只有独立的栈 |
独立性 | 独立(资源隔离) | 共用资源 |
切换成本 | 保存整个进程资源 | 只保存栈和程序计数器(PC) |
创建线程的函数 pthread_create(3)
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
// 参数:
// thread 线程标识(返回值)
// attr 线程属性结构体地址,默认可以为NULL。
// start_routine 线程的启动函数。
// arg 线程启动函数的参数。
// 返回值: 成功返回0,失败返回错误号。
编译选项:
需要在编译时指定 pthread 库。
gcc -lpthread -o xxx xxx.c
示例代码
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>#include <pthread.h>// 定义一个线程处理函数
void * mythread1(void* data) {printf("data:%p\n", data);while(1) {printf("x");fflush(NULL);sleep(1);}return NULL;
}int main(int argc, char * argv[]) {pthread_t thread_id;int ret;// 创建线程ret = pthread_create(&thread_id, NULL, mythread1, NULL);if (0 != ret) {printf("错误号:%d", ret);return 1;}while(1) {printf(".");fflush(NULL);sleep(2);}return 0;
}
示例
写一个程序,主进程创建9个子线程,第一个线程打印1, 第2个线程打印2,第9个线程打印9。每个线程打印一个数后睡眠一秒后继续打印。
主进程不退出。
参考:
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>#include <pthread.h>#define MAX_THREADS (9) // 最大的线程个数int is_running = 1;
// 定义一个线程处理函数
void * mythread(void* data) {// int * pnumber = (int*) data;int number = (int)data;while(is_running) {printf("%d", number);fflush(stdout);sleep(1);}return data;
}int main(int argc, char * argv[]) {pthread_t thread[MAX_THREADS];// int numbers[MAX_THREADS];int ret;printf("请输入回车开始和结束线程\n");getchar();for (int i = 0; i < 9; i++) {// numbers[i] = i+1;void *varg = (void*)(i+1);ret = pthread_create(&thread[i], NULL, mythread, varg);// ret = pthread_create(&thread[i], NULL, mythread, &numbers[i]);if (ret) {printf("pthread_create error: %d\n", ret);return 1;}}// 等待回车getchar();is_running = 0;// 杀死所有的线程// for (int i = 0; i < MAX_THREADS; i++) {// pthread_cancel(thread[i]);// } // 等待回收资源for (int i = 0; i < MAX_THREADS; i++) {void * pret;pthread_join(thread[i], &pret);printf("pret:%p\n", pret);} printf("主进程结束\n");return 0;
}
终止线程
线程终止的方式有:
- pthread_exit(3),终止自己本身线程。
- 线程函数 return
- pthread_cancel(3),终止其他线程;
- 主进程终止
线程资源回收 pthread_join(3)
线程也会占用资源,线程退出后线程的资源不会释放。需要其他线程或主线程释放资源。
pthread_join 函数用于等待线程退出并回收线程的资源。
#include <pthread.h>int pthread_join(pthread_t thread, void **retval);
在线程函数内获取自己的线程ID
#include <pthread.h>pthread_t pthread_self(void);
// 返回值:调用函数的线程的 ID。