Linux系统编程

一、一些生活中遇到的知识

1、a进程存在内存泄露,会对b进程造成什么影响?

 二、进程间通信  进程相关知识

1、管道

    1.1、无名管道pipe

    1.2、有名管道fifo

 

n、一些代码题

1、识别正确的字符串

2、sort使用方法

3、归并排序  冒泡排序 快速排序   

4、找出一个数组中小于等于给定数值的最长子串

 

 

一、一些生活中遇到的知识

1、a进程存在内存泄露,会对b进程造成什么影响?

背景知识:
(1)swap分区的含义
swap分区是一块特殊的磁盘空间,用于扩展系统的物理内存。当物理内存不足时,会将系统部分暂时不使用的数据移动到swap分区中,从而释放物理内存空间(RAM),以供其他进程使用。
(2)swap分区的作用
a) 扩展虚拟内存:当物理内存(RAM)不够时,swap做为后备存储,放置系统因为内存不足而出现的崩溃问题;大小一般为2倍的物理内存大小。。
b) 支持休眠:系统休眠时,可以将物理内存中的数据拷贝到swap分区中,此时需要保证swap空间至少等于物理内存大小;
c) 内存管理优化:内核可以将长时间未使用的内存页移到swap中,以提高内存的利用率。
(3)swap分区的类型
a) swap分区:独立的磁盘分区,专门用于swap。可使用shell命令,如fdisk或parted创建一个swap分区,使用mkswap /dev/sda3 格式化分区,使用swapon /dev/sda3 启用swap。注:会有安全问题,要及时的清空swap分区中的数据;可以禁用swap分区,但会触发OOM。
b) swap文件:普通文件,如/swapfile,更加的灵活,无需调整分区,直接删除文件来扩大swap文件的大小。
解答:由于a进程存在内存泄露,虽然进程间是独立的虚拟内存空间,a进程内存不会影响到b进程的内存空间,但是会通过物理内存耗尽来间接的影响到b进程。当物理内存不够的时候,就会使用swap空间,由于swap空间位于磁盘,频繁的读写磁盘会出现性能问题,使b进程运行卡顿,如果是对实时性要求比较高的系统,如支付系统,会造成比较大的影响。
解决方法:使用top、htop、vmstat实时监控内存使用情况、使用cgroups或容器技术(如Docker)限制a进程最大内存使用量、使用ssd作为swap存储(而不是机械硬盘)
推荐书籍:鸟哥的linux私房菜:基础学习篇、linux内核的设计与实现、深入理解linux内核、Unix/Linux系统编程手册、性能之巅、Linux系统编程。

 Q1:如何将swap分区添加到/etc/fstab?

 1 #1、确定swap分区的UUID或设备名字(如/dav/sda2)
 2 sudo blkid
 3 #显示结果如下:
 4 /dev/sda2: UUID="1234-5678" TYPE="swap"
 5 
 6 #2 编辑/etc/fstab文件
 7 vi /etc/fstab  #或使用其他文件编辑器:vim、nano
 8 #在该文件的末尾追加下面的一行:
 9 UUID=1234-5678 none swap sw 0 0
10 或者使用设备名字:
11 /dev/sda2 none swap sw 0 0
12 解析如下:
13 UID=1234-5678 或 /dev/sda2: 分区的标识符。
14 
15 none: 挂载点,对于 swap 分区通常留空。
16 
17 swap: 文件系统类型。
18 
19 sw: 挂载选项,通常为 sw 表示启用 swap。
20 
21 0: dump 备份操作的控制标志(通常为 0)。
22 
23 0: fsck 检查的顺序(在启动时检查文件系统时的顺序,对于 swap 通常为 0)。
24 
25 #3 启用swap分区
26 swapon -a
27 #4 验证swap分区是否启用
28 swapon -s
29 #或者查看文件
30 cat /proc/swaps
View Code

11

 

 二、进程间通信

1、管道

为什么要引入管道?
不同进程间通过加锁和解锁操作同一个文件,也可以实现进程间同步,但是这种方式需要访问磁盘文件系统,代价很高。管道使用系统调用,可以不与文件系统打交道就可以支持进程通信。
适用场景
生产者消费者场景。

1.1、无名管道pipe

单向通信,只适用于具有亲缘关系的进程间通信,如父子进程。

存储在内核内存中,不占用磁盘空间,创建的管道文件随进程销毁而销毁。

创建方法:使用pipe()创建,返回两个文件描述符fd[0]读、fd[1]写。注:半双工通信方式体现在进程0只可以操作fd[0]、另一个文件描述符fd[1]需要close,此时进程1可以操作写操作符fd[1]、不可以操作读操作符fd[0],此时需要将进程0的fd【0】关闭。

 实例:

 1 #include <unistd.h>
 2 #include <stdio.h>
 3 void main()
 4 {
 5     int    fildes[2];
 6     pid_t    pid;
 7     int    i, j;
 8     char    buf[256];
 9     if ( pipe( fildes ) < 0 )
10     {
11         fprintf( stderr, "pipe error!/n" );
12         return;
13     }
14     if ( (pid = fork() ) < 0 )
15     {
16         printf( stderr, "fork error!/n" );
17         return;
18     }
19     if ( pid == 0 )  //子进程的进程id为0
20     {
21         close( fildes[0] );
22         memset( buf, o, sizeof(buf) );
23         j = read( fildes[0], buf, sizeof(buf) );
24         fprintf( stderr, "[child] buf =[%s] len [%d] /n", buf, j );
25         return;
26     }
27 
28     close( fildes[0] );
29     write( fildes[1], "Hello!", strlen( "Hello!" ) );
30     write( fildes[1], "World!", strlen( "World!" ) );
31 }
View Code

 实例2:

 1 #include <stdio.h>
 2 #include <unistd.h>
 3 #include <string.h>
 4 #include <sys/wait.h>
 5 
 6 int main() 
 7 {
 8     int pipefd[2]; // pipefd[0]读端, pipefd[1]写端
 9     pid_t pid;
10     char buf[100];
11 
12     // 创建无名管道---使用pipe创建的管道文件随进程的结束而销毁
13     if (pipe(pipefd) == -1) 
14     {
15         perror("pipe");
16         return 1;
17     }
18 
19     pid = fork(); // 创建子进程
20     if (pid < 0) {
21         perror("fork");
22         return -1;
23     }
24 
25     if (pid == 0) 
26     { 
27         // 子进程(读数据)
28         close(pipefd[1]); // 关闭写端
29         read(pipefd[0], buf, sizeof(buf));
30         printf("Child received: %s\n", buf);
31         close(pipefd[0]);
32     } 
33     else 
34     { 
35         // 父进程(写数据)
36         close(pipefd[0]); // 关闭读端
37         const char *msg = "Hello from parent via unnamed pipe!";
38         write(pipefd[1], msg, strlen(msg) + 1);
39         close(pipefd[1]);
40         wait(NULL); // 等待子进程结束
41     }
42     return 0;
43 }
unnamepipe.c

gcc unnamed_pipe.c -o unnamed_pipe && ./unnamed_pipe

 

1.2、有名管道fifo

单向或双向(需要创建两个fifo)通信,不具有亲缘关系的进程也可以进程通信。以特殊文件的形式存储在文件系统中(如/tmp/myfifo),有磁盘索引节点,故任何进程都可以访问fifo文件,fifo文件可以手动删除。

创建方法:mkfifo()或mkfifo命令,可以使用unlink()删除。

实例1: 

写进程代码:

 1 #include <stdio.h>
 2 #include <sys/types.h>
 3 #include <sys/stat.h>
 4 #include <sys/errno.h>
 5 extern int errno;
 6 void main()
 7 {
 8     FILE    *fp;
 9     char    buf[255];
10 /*创建管道,如果已存在则跳过*/
11     if ( mkfifo( "myfifo", S_IFIFO | 0666 ) < 0 && errno != EEXIST )
12         return;
13     while ( 1 )
14     {
15         if ( (fp = fopen( "myfifo", "w" ) ) == NULL )
16             return;            /*打开管道*/
17         printf( "please input:" );
18         gets( buf );
19         fputs( buf, fp );
20         fputs( "/n", fp );
21         if ( strncmp( buf, "quit", 4 ) == 0 )
22             || strncmp( buf, "exit", 4 ) == 0)
23             break;
24         fclose( fp );
25     }
26 }
View Code

 

读进程代码:

 1 #include <stdio.h>
 2 #include <fcntl.h>
 3 #include <stdio.h>
 4 void main()
 5 {
 6     FILE    *fp;
 7     char    buf[255];
 8     while ( 1 )
 9     {
10         if ( (fp = fopen( "myfifo", "r" ) ) == NULL )
11             return;
12         fgets( buf, sizeof(buf), fp );
13         printf( "gets:[%s]", buf );
14         if ( strncmp( buf, "quit", 4 ) == 0 || strncmp( buf, "exit", 4 ) == 0 )
15             break;
16         fclose( fp );
17     }
18 }
View Code

注:使用read()和write()操作myfifo文件也可以。

 

实例2:

写进程(write.c):

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <unistd.h>
 4 #include <sys/stat.h>
 5 #include <fcntl.h>
 6 #include <string.h>
 7 
 8 #define FIFO_PATH "/tmp/my_fifo"
 9 
10 int main() 
11 {
12     int fd;
13     char *msg = "Hello from writer via named pipe!";
14 
15     // 创建FIFO(若已存在则忽略)
16     mkfifo(FIFO_PATH, 0666);
17 
18     // 打开FIFO(写入模式)
19     fd = open(FIFO_PATH, O_WRONLY);
20     if (fd == -1) 
21     {
22         perror("open");
23         return -1;
24     }
25 
26     // 写入数据
27     write(fd, msg, strlen(msg) + 1);
28     close(fd);
29     return 0;
30 }
write.c

读进程(read.c):

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <unistd.h>
 4 #include <fcntl.h>
 5 #include <sys/stat.h>
 6 
 7 #define FIFO_PATH "/tmp/my_fifo"
 8 
 9 int main() 
10 {
11     int fd;
12     char buf[100];
13 
14     // 打开FIFO(读取模式)
15     fd = open(FIFO_PATH, O_RDONLY);
16     if (fd == -1) 
17     {
18         perror("open");
19         return -1;
20     }
21 
22     // 读取数据
23     read(fd, buf, sizeof(buf));
24     printf("Reader received: %s\n", buf);
25     close(fd);
26 
27     // 可选:删除FIFO文件
28     unlink(FIFO_PATH);
29     return 0;
30 }
read.c

gcc reader.c -o reader && ./reader
gcc writer.c -o writer && ./writer

 

 

n、一些代码题

1、识别正确的字符串

正确的字符串规则:1)只包含如下字符(、)、[、]、{、};2)必须是成对出现的且以左边的括号为开始且是闭合的。

如str="()"、str=“”([])”、str="()[]{}"都是正确的字符串,如str="(]"时不正确的字符串

代码:

 1 #include <iostream>
 2 #include <string>
 3 #include <stack>
 4 #include <unordered_map>
 5 using namespace std;
 6 
 7 bool is_valid(string str)
 8 {
 9     stack<char> par;
10     unordered_map<char,char> name = { {')','('}, {']', '['}, {'}','{'}};
11     for(char c:str)
12     {
13         if(c == '(' || c == '[' || c == '{')
14         {
15             par.push(c);
16         }
17         else
18         {
19             if(par.empty() || par.top() != name[c])
20             {
21                 return false;
22             }
23             par.pop();  
24         }
25     }
26     return par.empty();
27 
28 }
29 
30 int main()
31 {
32     string str = "()";
33     string str_2 = "([])";
34     string str_3 = "(]";
35     string str_4 = "()[]{}";
36 
37     bool value =  is_valid(str_4);
38     cout << str_4 << ":" << value << endl;                                                                                                                               
39     return 0;
40 }
View Code

 

2、soret使用方法

 

 

3、归并排序

归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide andConquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并

其实就是分为归和并两个过程:

归: 不断将原数组拆分为子数组(一分为二),直到每个子数组只剩下一个元素 = 》 归过程结束

并:不断合并相邻的两个子数组为一个大的子数组,合并的过程就是将两个已经有序的子数组合并为一

举个栗子:

如下图所示的待排序序列,经过不断的归过程变成了一个个的小数组:

当每个子数组都只剩下一个元素时,每个子数组都是一个有序数组,然后开始两两合并。

如下是并过程:

 代码实现如下:

  1 #include <iostream>
  2 #include <thread>
  3 #include <vector>
  4 
  5 using namespace std;
  6 
  7 //l、m、r均代表的时vec数组的下标
  8 void merge(vector<int> & vec, int l, int m, int r)
  9 {
 10     //比如最后时,l=0,m=1,r=2则让L中存两个vec中的元素,让R中存一个vec中的元素
 11     int n1 = m - l + 1;
 12     int n3 = r - m;
 13 
 14     cout << "n1:" << n1 << ",n3:" << n3 << endl;
 15     
 16     //额外的申请内存,保存vec[m]元素左右的子数组,因为接下来会修改vec数组
 17     int *L = new int[n1];
 18     int *R = new int[n3];
 19 
 20     //将vec[l...m]拷贝到L[],其中包括vec[l]和vec[m]
 21     for(int i = 0; i < n1; i++)
 22     {
 23         L[i] = vec[l + i];
 24     }
 25 
 26     //将vec[m+1...r]拷贝到R[]
 27     for(int j = 0; j < n3; j++)
 28     {
 29         R[j] = vec[m + 1 + j];
 30     }
 31     //注意k时用来指示vec的新索引,需初始化为l
 32     int i = 0, j = 0, k = l;
 33     while(i < n1 && j < n3)
 34     {
 35         //开始比较大小,将小的放在vec的左边
 36         if(L[i] <= R[j])
 37         {
 38             vec[k] = L[i];
 39             i++;
 40         }
 41         else
 42         {
 43             vec[k] = R[j];
 44             j++;
 45         }
 46         k++;
 47     }
 48 
 49     //拷贝L[]剩余元素,如果有
 50     while(i < n1)
 51     {
 52         vec[k] = L[i];
 53         k++;
 54         i++;
 55     }
 56 
 57     //拷贝R[]剩余元素,如果有
 58     while(j < n3)
 59     {
 60         vec[k] = R[j];
 61         k++;
 62         j++;
 63     }
 64 
 65     delete [] L;
 66     delete [] R;
 67 
 68 }
 69 
 70 void mergeSort(vector<int> & vec, int l, int r)
 71 {
 72     if(l < r)
 73     {
 74         int m = l + (r - l) / 2;
 75         mergeSort(vec, l, m);
 76         mergeSort(vec, m + 1, r);
 77 
 78         merge(vec, l, m, r);
 79     }
 80 }
 81 
 82 int main()
 83 {
 84     vector<int> vec = {10,1,30,66,33,0,1};
 85     
 86     for(auto v : vec)
 87     {
 88         cout << v << ",";
 89     }
 90     cout << endl;
 91 
 92     mergeSort(vec, 0, vec.size() - 1);
 93 
 94     for(auto v : vec)
 95     {
 96         cout << v << ",";
 97     }
 98     cout << endl;
 99 
100 
101     return 0;
102 }
归并排序

运行结果:

内存只有1G,待排序的数据有100G,该如何对这100G的数据进行排序?

a. 先把这个100G的数据分成200份

b. 分别将0.5G的数据读入内存,进行内部排序(归并,快排,堆排)

c. 进行200个文件的merge操作即可

4、找出一个数组中小于等于给定数值的最长子串

 

服务器之间交换的接口成功率作为服务器调用关键特质,某个时间段内的接口失败率使用一个数组表示,数组中的每个元素都是单位时间内失败率数值,数组中的数值为0-100的整数,给定一个数值(minAverageLost)表示某个时间段内平均失败容忍值,即平均失败率小雨等于minAverageLost,找出数组中最长时间段,如果未找到则返回null。
输入描述:输入有两行内容,第一行为(minAverageLost),第二行为数组,数组元素通过空格分割,minAverageLost
和数组中的元素取值范围为0-100,数组元素个数不会超过100。
输出描述:找出数组中平均值小于等于minAverageLost的最长时间段,输出数组下标,格式为begin-end,下标从0开始,如果存在多个最长时间段,则输出多个下标对,且下标对之间使用空格连接,多个下标按照从小到大排序。
示例1:
输入:
2
0 0 100 2 2 99 0 2
输出:
0-1 3-4 6-7
请使用c++实现

 1 #include <iostream>
 2 #include <sstream>
 3 #include <vector>
 4 #include <string>
 5 #include <sstream>
 6 #include <algorithm>
 7 using namespace std;
 8 
 9 int main() {
10     int minAverageLost = 0;
11     string str;
12 
13     cin >> minAverageLost;
14     cin.ignore(); //忽略换行符
15 
16     //第二行
17     getline(cin, str);
18     stringstream ss(str);
19     vector<int> nums;
20     int num = 0;
21     while(ss >> num)
22     {
23         nums.push_back(num);
24     }
25 
26     vector<pair<int, int>> valid;
27 
28     int len = nums.size();
29     //计算出数组中前i个元素的和并保存到pre中
30     //其中pre[i]表示num[0...i]的和
31     vector<int> pre(len+1, 0);
32     for(int i = 0; i < len; i++)
33     {
34         pre[i+1] = pre[i] + nums[i];
35     }
36 
37     for(int i=0; i < len; i++)
38     {
39         for(int j = i; j < len; j++)
40         {
41             //此时sum[i...j]的值就等于pre[j+1]-pre[i]
42             int sum = pre[j + 1] - pre[i];
43             int length = j - i + 1;
44             if(sum <= minAverageLost * length)
45             {
46                 valid.emplace_back(i, j);
47             }
48         }
49     }
50 
51     if(valid.empty())
52     {
53         cout << "NULL" << endl;
54         return 0;
55     }
56     
57     int max_len = 0;
58     for(auto & p : valid)
59     {
60         int length = p.second - p.first + 1;
61         if(max_len < length)
62         {
63             max_len = length;
64         }
65     }
66 
67     vector<pair<int, int>> result;
68     for(auto & p : valid)
69     {
70         if((p.second - p.first + 1) == max_len)
71         {
72             result.push_back(p);
73         }
74     }
75 
76     sort(result.begin(), result.end());
77 
78     vector<string> outputs;
79     for(auto & p : result)
80     {
81         outputs.push_back(to_string(p.first) + "-" + to_string(p.second));
82     }
83 
84     for(int i =0; i < outputs.size(); i++)
85     {
86         if(i > 0)
87         {
88             cout << " ";
89         }
90         cout << outputs[i];
91     }
92     cout << endl;
93 
94     return 0;
95 
96 }
97 // 64 位输出请用 printf("%lld")
View Code

 

posted @ 2025-03-04 15:49  兵临城下的匹夫  阅读(44)  评论(0)    收藏  举报
TOP