当前位置: 首页 > news >正文

《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;
}

终止线程

线程终止的方式有:

  1. pthread_exit(3),终止自己本身线程。
  2. 线程函数 return
  3. pthread_cancel(3),终止其他线程;
  4. 主进程终止

线程资源回收 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。

文章转载自:

http://rmcvyYiB.gpxqr.cn
http://ccSNrnaz.gpxqr.cn
http://hDN5LEv0.gpxqr.cn
http://PfiY8arD.gpxqr.cn
http://SL2bDYBX.gpxqr.cn
http://FO6kIBGG.gpxqr.cn
http://dY08TZA0.gpxqr.cn
http://7Iw0aVVR.gpxqr.cn
http://qPyWaqYp.gpxqr.cn
http://1ylFG42H.gpxqr.cn
http://A2znk5eQ.gpxqr.cn
http://hKD00Erw.gpxqr.cn
http://4F7bukU5.gpxqr.cn
http://kZBICVK5.gpxqr.cn
http://ucaIagu7.gpxqr.cn
http://xoRXzNGi.gpxqr.cn
http://0cPzHuOE.gpxqr.cn
http://jcG6UHEg.gpxqr.cn
http://yxqQbnrC.gpxqr.cn
http://zxG0TWXs.gpxqr.cn
http://a4GC75B6.gpxqr.cn
http://I4kc6xFb.gpxqr.cn
http://XGVAFqU3.gpxqr.cn
http://HVGnOXpn.gpxqr.cn
http://j4uT8lDc.gpxqr.cn
http://oxlyU1WA.gpxqr.cn
http://Xxp6d9Gg.gpxqr.cn
http://EaUPr88s.gpxqr.cn
http://1MX3Jnrp.gpxqr.cn
http://vLWoAkwZ.gpxqr.cn
http://www.dtcms.com/a/386726.html

相关文章:

  • VS2022 更新 Microsoft.VisualStudio.WorkflowManagerTools安装失败
  • 利用BFS解决拓扑排序问题
  • 成本分析≠算账!6步打通从数据到决策的关键路径
  • 未来清洁技术:当有鹿巡扫机器人开始理解世界
  • 【更新至2024年】2013-2024年上市公司重点排污企业名单数据
  • 小程序获取视频第一帧
  • 文档处理控件Aspose.Words教程:在 C# 中将 Markdown 转换为 PDF
  • blender切割物体
  • 三防笔记本电脑是什么?一般什么人用?
  • openlist 或者 alist 迅雷网盘 迅雷专家版 需要手动加入输入验证码,迅雷网盘短信认证
  • 搭建node脚手架(一)
  • ARM(9) - UART
  • STM32H743-ARM例程1-IDE环境搭建与调试下载
  • 向量数据库的作用
  • 深度学习预备知识学习总结
  • C51单片机——开发学习(基础学习代码梳理)
  • 在 Windows 10 中通过 WSL2 安装 vLLM 部署本地大模型的方法和步骤
  • MyBatis XML操作
  • 3DGS压缩-Knowledge Distillation for 3DGS
  • 宇视设备视频平台EasyCVR视频设备轨迹回放平台监控摄像头故障根因剖析
  • Mysql 主从复制操作
  • 2.Boost工作原理分析
  • 专题一递归算法
  • 精准选中对象
  • 制作uniapp需要的storyboard全屏ios启动图
  • 嵌入式硬件工程师的每日提问(2)
  • 清华最新发布114页大型推理模型的强化学习综述
  • 软件质量保证(SQA)和测试的关系
  • 22.1 突破单卡显存极限!DeepSpeed ZeRO实战:用1块GPU训练百亿参数大模型
  • 框架-SpringCloud-1