linux 讨论题合集(个人复习)

常规文件的权限是什么?如何分配或修改这些权限?文件夹(目录)的权限是什么?显示常规文件和文件夹的区别

讨论:①常规的文件权限有四种,r可读、w可写、x可执行、-没有权限;②可以使用chmod命令来分配或者修改文件的权限,其语法是 chomd 权限 filename ;③linux是一个多用户多任务的操作系统,可以通过用户好用户组来更好的控制目录文件的权限,具体说来就是:每一个文件都有一个拥有者,该拥有则属于某一个用户组,每一个文件的权限可以细分为三组(拥有者权限、用户组其他用户的权限、其他用户权限),可以使用 chmod 拥有者:所属组 目录 来修改文件的权限,可以使用ls -la来查看文件的权限;④显示常规文件其第一列的第一个字母为-,而目录文件的第一列的第一个字母为d,常规来讲,一个文件的默认访问权限是 -rw-rw-rw-,一个目录的默认访问权限是drwxrwxrwx。⑤对于目录来讲,r表示对其下文件可读,w表示对文件夹可添加可删除,x表示可以进入到该目录。

当无法执行命令“chown、chgrp、chmod”等,请给出可能的原因,如何保证成功执行。

讨论:linux对于文件的管理最常用的命令有“chown、chgrp和chmod”,chown用来改变文件的所有权,这个命令只有root用户可以使用;chgrp也是只有root用户可以使用;chmod对于文件的所有者和root用户都可以使用;如果无法执行以上的命令,可以切换到root用户再来执行。

怎么新建一个目录?如何复制或者删除一个非空的目录。

讨论:①可以使用mkdir新建一个空的目录;②可以使用cp -r old new 对非空目录进行复制,rm -r old 对非空目录进行删除。

当用户使用“mv a b”,请表示出b如果是一个文件、目录或者不存在时的可能性。

讨论:mv命令可以将文件或者目录移动到另一个目录,或者对文件进行改名。如果b是一个文件,系统会把a改名为b,原来的b会被覆盖;如果b是一个目录,系统会把文件a移动到b的目录下,作为b的子文件(如果b本身也有名为a的文件,原来b下的a会被覆盖);如果b不存在,系统会把a改名成b。

什么是文件系统?请举个例子。(也许你可以使用U盘)

讨论:操作系统中用于负责管理和存储文件信息的软件机构称为文件管理系统,简称文件系统。在linux中,文件系统必须先被挂载才能使用可以使用mount和umount去挂载或者卸载一个文件,被挂载的文件会被暂时覆盖,一般只有使用超级用户才可以实现该权限。
sda1表示第一块硬盘的第一个分区;
sdb表示第二块硬盘。
比如mount /dev/sdb /home/aa:这个命令表示把sdb的文件挂在到aa目录下。
可以使用df来显示文件的的挂载情况。

什么是管道,怎么去使用管道去创建一个组合命令,请给出一个例子。

讨论:①管道是linux的一种通信机制,管道可以实现把一个程序的输出直接连接到另一个程序的输入,从本质上讲,管道是一种特殊的文件;②可以使用管道符 | 来建立管道,格式命令为 a | b,表示a的输出作为b的输入,比如可以使用组合命令 find . -name . p*.c -print|xargs grep include ,这条命令的意思是把所有名字以p开头,以.c结尾的文件找到,并且把这些文件的 include 字符都找到。
使用 ls -l | more,这也是常用的管道命令。
管道符可以一直连续的使用,换句话说一条组合命令可以使用多个管道符。

假设一个人正在读取一个大型的程序的源代码,他想要检查一个函数的定义,这个函数不在当前的读取文件中,你对他有什么建议。

讨论:可以使用查找定位的方式,在linux中可以使用find进行查找文件,使用grep对文件内容进行匹配。一般来说一个函数的定义存在于头文件中,可以使用 find -e filename.h,查找文件,使用grep functionName filename.h 来查找文件内容。
组合起来可以使用以下组合命令: find -e *.h | xargs grep functionName。

如果一个人想要在网上发送一个大型的程序的所有文件给他的舍友,你可以给他一些建议吗。

讨论:可以使用打包压缩的方式对文件进行处理,方便网络上的传输。在linux上,可以使用tar对文件进行打包成一个文件,使用gzip和bzip2对文件进行压缩,可以大大节省内存。例如可以使用 tar cvf asd a把打包成asd文件,使用 gzip把asd压缩成压缩文件,再传输。
可以使用tar vtf filename 查看filename文件的打包的情况,但是并不解开打包

假设网下登录TTY1,但是由于某一种原因停止了,你能给出一个解决的方案吗?

讨论:可以使用ps -t tty1 查看其中的命令,然后可以使用kill -9 PID 来杀死进程。杀死进程后,可以使用密码重新登录,继续其他的内容。

假设四个人(李、张、王、赵)在一个小组中开发一个系统。如何为其文件分配权限通常情况下,640是合理的。但是,如果只能使用“chmod”来修改权限,那就太天真了。您需要修改每个用户的默认设置,以确保在创建文件时拥有正确的权限。(也许你需要在互联网上搜索学习命令umask、useradd、groupadd等。)

讨论:若用户创建一个文件,则文件的默认访问权限为 -rw-rw-rw- ,创建目录的默认权限drwxrwxrwx ,而umask值则表明了需要从默认权限中去掉哪些权限来成为最终的默认权限值,umask 026 表示后续添加的文件的权限均为640(两个值相加为666),但是添加命令断电后命令就会失效;useradd用来添加用户;groupadd用来添加用户组。
详情可以查看:https://blog.csdn.net/yspg_217/article/details/121900800


描述使用vi的两种模式,如何打开两种模式

讨论:①vi有两种模式,一种是编辑模式,一种是命令模式。②使用vi filename 可以进入vi ,这个时候vi处于命令模式,在命令模式下,可以使用i 进入到编辑模式,表示从当前光标位置开始插入;可以使用a进入编辑模式,表示从当前光标位置的下一个位置开始插入文字;使用小写o表示从新的一行的行首开始插入文件;使用大写O表示从光标所在行的上面的新的一行插入文字。③在编辑模式下,使用esc可以回到命令模式下。

如何插入一行

讨论:在命令模式下,使用小写o表示从新的一行的行首开始插入文件;使用大写O表示从光标所在行的上面的新的一行插入文字。

怎么复制粘贴一行文本

讨论:①在命令模式下,vi使用yy复制光标所在的行;使用数字+yy表示复制光标所在的后n行;使用yw复制光标所在的第一个单词,使用数字+yw复制光标所在的n个单词;②vi使用小写字母p来粘贴缓冲区的字符到光标所在位置。

怎么在文件中匹配,怎么替换所匹配到的内容

讨论:①在命令模式下,vi使用/+搜索词对文件进行匹配,匹配成功后,可以使用小写n对匹配项从上而下移动,或者使用大写N对匹配项从下而上移动;②在命令模式下,可以使用s命令对光标所在位置的字符进行编辑,编辑后退出编辑模式,使用n在匹配项中移动,再使用 . 来重复上一条编辑指令;③可以使用全局替换指令对内容进行匹配和替换,指令举例如下:1,$s/old/new/g。

怎么重复或者撤销上一条命令

讨论:可以使用 . 重复上一条指令;使用 u 撤销上一条指令。

怎么在保存或者不保存的情况下退出vi

讨论:如果不需要保存,可以使用 :q! 强制退出vi,如果需要保存,可以使用 :qw 退出,其中w表示存盘。


问题一:如何定义和使用shell变量,给出一个例子

讨论:使用shell变量有三种方法,一种是用等号直接赋值,比如a=123;第二种是从键盘中读入,比如read b;第三种是使用for循环变量,比如 for i in a b c,可以对i循环赋值为a b c。

问题二:if/while语句可以利用多少种类型的判断?

讨论:if/while语句可以有三种类型的判断,一种是字符串的判断,比如判断写入的数据是否是yes/no;第二种是文件的判断,比如使用 -f 判断文件是否存在,使用 -d 判断文件是否是一个目录;第三种是判断一个值是否相等,shell变量虽然都是字符串,但是可以使用$()对字符串取值。

数值比较:-eq(等于)、-ne(不等于)、-gt(大于)、-ge(大于等于)、-lt(小于)、-le(小于等于)
字符串比较:=(等于)、!=(不等于)、-z(字符串长度为零)、-n(字符串长度不为零)
文件测试:-e(文件存在)、-d(目录存在)、-f(文件是否为文本)、-r/-w/-x(文件是否可读/可写/可执行)
逻辑运算:&&(与)、||(或)
其他特殊操作符:()(子shell)、[](数组)

问题三:什么是正则表达式,怎么样使用正则表达式去匹配得更加清楚

讨论:正则表达式可以用来匹配符合某一种规则的字符,以case语句中,需要匹配no的各种大小写的可能性,可以使用正则匹配 [Nn][Oo] | [Nn] ,这样可以使得条件表达式更加简洁。

正则表达式是一种用于匹配和操作文本的强大工具。它可以用来定义模式,从而实现更灵活和精确的文本搜索和替换。
以下是一些正则表达式的示例:

  • ^hello:匹配以"hello"开头的字符串。
  • world$:匹配以"world"结尾的字符串。
  • [0-9]+:匹配一个或多个数字。
  • .*:匹配任意字符(除了换行符)的任意次数。
    通过使用正则表达式,我们可以创建更清晰的模式,以便更准确地匹配我们所需的文本。

问题四:程序(可执行文件)的参数是什么,函数的参数是什么,举例说明如何使用这些参数。

讨论:程序的位置参数和函数的位置参数是不同的,①程序的参数指的是整一个程序(命令的)参数,比如在命令行中输入ls -la ,该命令中的 -la 就是该命令的第一个参数,用 $1 表示;②函数的参数表示函数调用时传递的参数,跟在函数调用的后面。

问题五:什么是sed,什么是awk,举例说明怎么使用他们

讨论:sed是一个“流编辑”工具,它不面向屏幕,而且是非交互式的,流编辑器非常适合于执行重复的编辑,这种重复的编辑如果由人工完成将花费大量的时间,例如 echo “frankreich” | sed -e ‘s/frank/deutsch/’ 可以把前面的frank换成deutsch。
awk也是一个优秀的样式扫描和处理工具,awk的功能强于sed和grep,awk几乎可以完成grep和sed所能完成的全部工作。例如 who | awk ‘pts/1{print $1}’ 可以导出pts/1终端上登录的用户


请用代码展示使用标准c语言和使用系统调用来操控文件的区别

讨论:使用标准的c函数操控文件的函数包括fopen、fgets、fputc、fclose等,并且使用标准c函数操控的是文件的指针类型;而使用系统调用操控文件的函数包括open、read、write等,使用系统调用操控的是文件的描述符。一般来说,系统调用通常比标准c调用的效率高,这是因为标准c函数需要进行额外的库函数调用和缓冲操作。此外,标准c函数是跨平台的。而系统调用因为是直接调用操作系统的功能,所以可能在不同的操作系统中有不同的实现。

什么是mmap,怎么使用mmap来在多进程中共享内存

讨论:mmap是内存映射文件的一种方法,即将一个文件或者其他的映射对象映射到进程的地址空间,实现文件磁盘地址和进程虚拟地址的映射关系。使用mmap进行映射,会得到磁盘和某一个文件的地址相同的地址,实现内存的共享

什么是文件夹?你能在文件夹里写点什么吗?为什么?描述文件夹的结构,并使用代码演示如何扫描文件夹。

讨论:文件目录对应的是文件的属性信息,文件的属性信息与文件的实体不是存在在一个位置上的,把文件的属性信息存放在一个位置,这一类的文件叫做目录文件,也就是windows上的文件夹。

在linux中,一切皆文件,目录也是一个文件,也有属于自己的innode,与普通文件不同,普通文件的数据块中存放的是文件数据,而目录文件的数据块中存储的是别的文件的信息,一条这样的信息就是一个FCB,也就是一个文件目录项,FCB的有序集合称为文件目录。
FCB包含文件的基本信息(文件名、物理地址、逻辑结构、物理结构等)、存储控制信息(可读可写可执行)、使用信息(文件建立时间、文件修改时间)
目录信息不可修改,因为目录信息保存的是文件的文件名,文件存放的物理地址等,这是在文件创建的时候就已经确定的。

在这里插入图片描述

在linux中,系统的目录呈现树状结构,最顶端为根目录/,根目录之下包含各个系统必须的目录。linux根目录下都是重要的目录,每一个目录都有其作用,不可随意删除

写一个自己版本的grep命令


gcc编译器对程序的编译过程

预编译(预处理):在这个阶段主要做了三件事情:展开头文件、宏替换、去掉注释行,这一阶段由gcc的预处理器完成,最终得到的还是源文件,文本格式。
编译:这个阶段需要调用gcc编译器对文件进行编译,最终得到一个汇编文件
汇编:这个阶段需要调用gcc的汇编器对文件进行汇编,最终得到一个二进制文件
链接:这个阶段需要调用gcc的链接器对程序需要调用的库进行链接,得到一个可以执行的文件

make构建工具
make是揭示makefile指令的命令工具,make构造项目的时候需要加载一个makefile的文件。
makefile中每一条规则都有三个部分组成:目标、依赖和命令(每一个命令都必须使用Tab缩进且独占一行)


fotk语句执行成功后,会返回一个子进程的pid给父进程,并且返回0给子进程,由于父子进程并发执行,所以这个时候x的值为1或者-1都有可能,看父子进程谁先被调用。

尝试使用wait和waitid来同步父子进程的执行

child_pid=wait(&stat_val);//父进程等待任意一个子进程结束完成
child_pid=wait(child_id,&stat_val,0);//父进程等待pid=child_id子进程的结束,如果pid=-1与wait一直,stat_val表示进程退出状态

什么是execl函数簇

execl函数簇不是指一个函数,而是execl函数的统称,execl函数可以在不创建新进程的情况下,以一个全新的程序替换当前的程序的正文、数据和堆栈。
例如.c程序中使用ls命令替换当前.c程序进行execlp(“ls”,“ls”,0);第一个参数ls为命令名,第二个ls为第一个参数,.c程序执行到这行代码时,会直接执行ls命令,后面的代码不执行。

在一个程序中有多少种信号,怎么使用一个用户定义信号

在linux系统中一共含有32种信号,可以使用kill -10 9969给pid=9969的进程发送信号10

#include<signal.h>
#include<stdio.h>
#include<unistd.h>
#include<signal.h>void signal_handle(int sig_num){if(sig_num==SIGUSR1)printf("SIGUSR1-----\n");//在命令行中发送SIGUSR1信号可以执行signal_handle函数printf("signal_handle\n");
}int main(){signal(SIGUSR1,signal_handle);while(1){printf("main------\n");sleep(1);}return 0;
}

在程序运行过程中,使用kill -s SIGUSR1 pid可以在命令行中发送对应的执行给程序,从而执行信号所对应的信号处理函数。

signal和sigaction的区别是什么

下面所指的signal都是指以前的older signal函数,现在大多系统都用sigaction重新实现了signal函数,不会出现空窗期。

  • signal在调用函数hander之前会有一段空窗期,在空窗期期间会先把hander指针恢复,这样会导致signal丢失信号,不能处理重复的信号;sigaction调用之后不会恢复hander指针,只有当再次调用sigaction修改handle指针才会恢复。

阅读以下代码,确定x的值,并在此基础上,说明进程和线程之间的区别

在以上代码中,x的值最后为-1。x的值在主线程中初始化为0,然后在主线程中设为1,在创建的线程fun中设置为-1。
线程和进程的区别在于:线程是调度的基本单位,进程是资源分配的基本单位;进程是程序的执行实例,是独立拥有存储空间的运行单位,每一个进程都有自己的地址空间、数据栈以及其他用于跟踪进程执行的辅助数据;线程是进程的一条的一条独立路径,线程几乎不独立拥有资源,线程共享进程的资源,包括文件描述符等,线程共享相同的地址空间,因此以上的x是会被主线程和其他线程共享。

如何进行线程的同步

①可以使用信号量控制线程的执行顺序,比如sem_init初始化一个信号量;sem_post相当于原子操作的V操作;sem_wait相当于原子操作的P操作;

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#include<pthread.h>
#include<semaphore.h>void *thread_function(void *arg);
sem_t bin_sem;
#define WORK_SIZE 1024
char work_area[WORK_SIZE];int main(){int res;pthread_t a_thread;void *thread_result;res=sem_init(&bin_sem,0,0);//信号量初始化为0if(res!=0){printf("Semaphore initialization failed");exit(EXIT_FAILURE);}res=pthread_create(&a_thread,NULL,thread_function,NULL);if(res!=0){perror("Thread creation failed");exit(EXIT_FAILURE);}printf("Input some text. Enter 'end' to finish\n");while(strncmp("end",work_area,3)!=0){fgets(work_area,WORK_SIZE,stdin);sem_post(&bin_sem);}printf("\nWaiting for thread to finish\n");res=pthread_join(a_thread,&thread_result);if(res!=0){perror("Thread join failed");exit(EXIT_FAILURE);}printf("Thread join\n");sem_destroy(&bin_sem);//清除信号资源exit(EXIT_SUCCESS);}
void * thread_function(void *arg){sem_wait(&bin_sem);while(strncmp("end",work_area,3)!=0){printf("You input %d characters\n",strlen(work_area)-1);sem_wait(&bin_sem);}pthread_exit(NULL);
}

②也可以使用互斥锁进行同步:pthread_mutex_init用来初始化一个互斥锁,pthread_mutex_lock、pthread_mutex_unlock进行加锁和解锁操作,pthread_mutex_destroy用来释放锁操作。
pthread_mutex_t mutex;
pthread_mutex_init(&mutex,NULL); ///< 初始化互斥锁
pthread_mutex_lock(&mutex); ///< 加锁
///< 操作公共资源
pthread_mutex_unlock(&mutex); ///< 解锁
pthread_mutex_destroy(&mutex); ///< 销毁互斥锁

请设计使用进程和线程实现相同目标的程序,请说明你对进程/线程应用范围的看法

使用进程(在C中使用fork):

#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>int main() {int x = 0;if (fork() == 0) {// 子进程x++;printf("子进程:x = %d\n", x);} else {// 父进程wait(NULL);printf("父进程:x = %d\n", x);}return 0;
}
[cch@aubin os]$ gcc demo.c
[cch@aubin os]$ ./a.out
子进程:x = 1
父进程:x = 0
[cch@aubin os]$ 

使用线程(在C中使用pthread):

#include <pthread.h>
#include <stdio.h>int x = 0;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;void *increment(void *arg) {pthread_mutex_lock(&mutex);x++;printf("线程:x = %d\n", x);pthread_mutex_unlock(&mutex);return NULL;
}int main() {pthread_t thread1, thread2;pthread_create(&thread1, NULL, increment, NULL);pthread_create(&thread2, NULL, increment, NULL);pthread_join(thread1, NULL);pthread_join(thread2, NULL);printf("主线程:x = %d\n", x);return 0;
}
[cch@aubin os]$ ./a.out
线程:x = 1
线程:x = 2
主线程:x = 2
[cch@aubin os]$ 

进程的应用范围:
并行计算: 进程适用于需要充分利用多核处理器进行并行计算的场景。每个进程有独立的内存空间,可以并行执行,提高计算效率。
稳定性: 进程之间的独立性可以提高程序的稳定性。一个进程的崩溃不会影响其他进程的执行。
资源隔离: 进程之间有独立的内存空间,可以更好地实现资源隔离,确保一个进程的错误不会波及到其他进程。
线程的应用范围:
轻量级任务: 线程适用于需要执行相对轻量级任务的场景,例如I/O密集型任务。线程的创建和切换开销较小。
共享内存: 线程之间共享同一进程的内存空间,适合在同一应用程序内共享数据。
响应性: 线程可用于实现更快的响应时间,例如在GUI应用程序中响应用户输入。


有名管道和无名管道的区别

无名管道
匿名性: 未命名管道是一种临时通信机制,没有明确的文件系统路径或标识符。它通常是由一个进程创建,并通过进程间的文件描述符传递给另一个进程使用。
单向通信: 未命名管道是单向的,无名管道的读端和写端是固定的,只能支持单向的数据流。通常,一个进程作为写入端,而另一个进程作为读取端。
生命周期: 未命名管道的生命周期与创建它的进程相关。当创建它的进程终止时,管道也会被销毁。
通信限制: 未命名管道通常在具有亲缘关系的进程之间使用,因为它们共享同一个父进程。

命名管道:
命名: 命名管道在文件系统中有一个明确的路径名,可以通过文件系统访问。它有一个标识符,允许不相关的进程通过路径名进行通信。
双向通信: 命名管道支持双向通信,进程可以通过相同的路径名进行读取和写入。
生命周期: 命名管道的生命周期不依赖于创建它的进程。它可以持续存在,直到显式地被删除或系统关闭。
通信范围: 命名管道允许不相关的进程进行通信,因为它们可以通过路径名引用。

无名管道和有名管道都不支持seek操作

代码展示如何实现管道的重定向IO

使用无名管道时,还可以搭配使用close()关闭文件描述符和dup()复制管道文件描述符来实现输入输出标准的重定向。

#defiine STD_INPUT 0
#define STD_OUTPUT 1
int main()
{int fd[2];int pid;char str[256];if (pipe(fd)<0) {perror(“pipe create error!);exit(1);}						if ((pid = fork())==-1) {perror(“process fork error!);	exit(1)}if (pid == 0) {//子进程close(fd[0]);					close(STD_OUTPUT);		dup(fd[1]);					close(fd[1]);					printf(“abc\n”);//把数据输出到管道,管道写数据}else {//父进程close(fd[1]);					close(STD_INPUT);			dup(fd[0]);					close(fd[0]);					fgets(str,sizeof(str),stdin);//从键盘输入数据到管道,管道读数据printf(%s\n”,str);}
}

写代码表示两个程序的管道通信

//pipe4.c
#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
#include<string.h>int main(int argc,char *argv[]){int data_processed;char buffer[BUFSIZ+1];int file_descriptor;memset(buffer,'\0',sizeof(buffer));sscanf(argv[1],"%d",&file_descriptor);//读取格式化的argv[1]给file_descriptordata_processed=read(file_descriptor,buffer,BUFSIZ);printf("%d-read %d bytes:%s\n",getpid(),data_processed,buffer);exit(EXIT_FAILURE);}
# 0表示键盘,从键盘中读取输入并且输出
[cch@aubin os]$gcc pipe4.c -o pipe
[cch@aubin os]$ ./pipe 0
123466
4947-read 7 bytes:123466[cch@aubin os]$ 
#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
#include<string.h>int main(){int data_processed;int file_pipes[2];const char some_data[]="123";char buffer[BUFSIZ+1];pid_t fork_result;memset(buffer,'\0',sizeof(buffer));if(pipe(file_pipes)==0){fork_result=fork();if(fork_result==-1){fprintf(stderr,"Fork failure");exit(EXIT_FAILURE);}//子进程if(fork_result==0){sprintf(buffer,"%d",file_pipes[0]);//将file_pipes[0]转换成字符串,以适应execl调用中参数类型的要求,其中fotmat参数与print中的类型一致/*execlp("ls", "ls", "-l", "-F", NULL);         使用程序名在PATH中搜索。execl("/bin/ls", "ls", "-l", "-F", NULL);    使用参数1给出的绝对路径搜索。*/if(execl("pipe","pipe",buffer,(char *)0)==-1)printf("execl error\n");exit(EXIT_FAILURE);}else{data_processed=write(file_pipes[1],some_data,strlen(some_data));printf("father:Wrote %d bytes\n",data_processed);}exit(EXIT_SUCCESS);}exit(EXIT_FAILURE);
}
[cch@aubin os]$ gcc demo.c
[cch@aubin os]$ ./a.out
father:Wrote 3 bytes
argv[1]=3
6098-read 3 bytes:123
[cch@aubin os]$ 

使用代码展示信号量、共享内存和消息队列的典型应用场景。

信号量:

#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/sem.h>int main(){//创建一个信号量集,并且设置信号量集中信号量的值为1key_t key=12345;int sem_id=semget(key,1,0666|IPC_CREAT);//不存在就创建if(sem_id==-1){perror("semget error");exit(1);}//对信号了p操作struct sembuf sops;sops.sem_num=0;sops.sem_op=-1;//psop.sem_flg=0;if(semop(sem_id,&sops,1)<0){perror("semop error");return -1;}//互斥区printf("互斥区操作\n");sops.sem_op=1;//v操作if(semop(sem_id,&sops,1)<0){perror("semop error");return -1;}//删除信号量if(semctl(sem_id,0,IPC_RMID)<0){perror("semctl error");return -1;}/*union semun{int val;struct semid_ds *buf;ushort *array;}argument;argument.val=1;if(semctl(sem_id,0,SETVAL,argument)==-1){//设置信号量的值perror("semctl error");exit(1);}
*/return 0;
}

共享内存:

//share2.c#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <sys/ipc.h>
#include <sys/shm.h>#define MAXBUFSIZE 4096
#define SHAREMEMESIZE 4096int read_file(char *buf, int size, const char *filename);int main() {int n, shm_id;char buf[MAXBUFSIZE] = {0};char *shm_p = NULL;/* 获取共享内存,如果不存在则创建*/if ( (shm_id = shmget(0x666, SHAREMEMESIZE, 0644 | IPC_CREAT)) < 0) {perror("shmget error");return 1;}/*把共享内存链接到当前进程的地址空间*/shm_p = (char *)shmat(shm_id, NULL, 0);if (shm_p == (void *) -1) {perror("shmat error:");return 1;}// 从文件中读取数据n = read_file(buf, MAXBUFSIZE, "./read_text");if (n == -1) {return 1;}printf("read from file: %s\n", buf);// 将数据拷贝到共享内存memcpy(shm_p, buf, strlen(buf));// 把共享内存从当前进程分离shmdt(shm_p);return 0;
}// 读文件内容到buf
int read_file(char *buf, int size, const char *filename) {int fd, n;// 打开read_testif ( (fd = open(filename, O_RDONLY)) < 0) {perror("open file_read_test error:");return -1;}// 读取文件内容n = read(fd, buf, size);if (n < 0) {perror("read file error:");return -1;}// 关闭文件close(fd);return n;
}//share#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>#define MAXBUFSIZE 4096
#define SHAREMEMESIZE 4096int write_file(const char *buf, int size, const char *filename);int main() {int shm_id;char buf[MAXBUFSIZE] = {0};char *shm_p = NULL;/* 获取共享内存,如果不存在则创建*/if ( (shm_id = shmget(0x666, SHAREMEMESIZE, 0644)) < 0) {perror("shmget error");return 1;}/*把共享内存链接到当前进程的地址空间*/shm_p = (char *)shmat(shm_id, NULL, 0);if (shm_p == (void *) -1) {perror("shmat error:");return 1;}// 拷贝共享内存中的数据memcpy(buf, shm_p, MAXBUFSIZE);// 将共享内存中的内容写入write_text文件中write_file(buf, strlen(buf), "write_text");printf("write to file: %s\n", buf);// 把共享内存从当前进程分离shmdt(shm_p);//删除共享内存if (shmctl(shm_id, IPC_RMID, 0) == -1) { printf("shmctl failed\n"); return -1; }return 0;
}// 将buf中的数据写入文件
int write_file(const char *buf, int size, const char *filename) {int fd, n;// 打开文件,不存在则创建if ((fd = open(filename, O_WRONLY | O_CREAT, 0644)) < 0) {perror("open file error:");return -1;} // 写入内容n = write(fd, buf, size);if (n < 0) {perror("write file error");return -1;}close(fd);return n;
}
[cch@aubin ipc]$ gcc share.c -o share
[cch@aubin ipc]$ gcc share2.c -o share2
[cch@aubin ipc]$ ./share2
read from file: this is read text[cch@aubin ipc]$ ./share
write to file: this is read text[cch@aubin ipc]$ 

消息队列:

//running.c
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include<errno.h>
#include<unistd.h>
#include<sys/msg.h>#define MAX_TEXT 512
struct my_msg_st {long int my_msg_type;char some_text[MAX_TEXT];
};int main(){int running=1;struct my_msg_st some_data;int msgid;char buffer[BUFSIZ];msgid=msgget((key_t)1234,0666|IPC_CREAT);if(msgid==-1){fprintf(stderr,"msgget failed with error:%d\n",errno);exit(EXIT_FAILURE);}while(running){printf("Enter some text:");fgets(buffer,BUFSIZ,stdin);some_data.my_msg_type=1;strcpy(some_data.some_text,buffer);if(msgsnd(msgid,(void*)&some_data,MAX_TEXT,0)==-1){//send 发送消息到msgid中fprintf(stderr,"msgsnd failed\n");exit(EXIT_FAILURE);}if(strncmp(some_data.some_text,"end",3)==0) running=0;}exit(EXIT_SUCCESS);}//running2.c
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include<errno.h>
#include<unistd.h>
#include<sys/msg.h>#define MAX_TEXT 512
struct my_msg_st {long int my_msg_type;char some_text[MAX_TEXT];
};int main(){int running=1;int msgid;struct my_msg_st some_data;long int msg_to_receive=0;//接受消息类型设置,按照先进先出的顺序接收数据//获取消息队列msgid=msgget((key_t)1234,0666|IPC_CREAT);if(msgid==-1){fprintf(stderr,"msgget failed with error:%d\n",errno);exit(EXIT_FAILURE);}while(running){if(msgrcv(msgid,(void*)&some_data,BUFSIZ,msg_to_receive,0)==-1){fprintf(stderr,"msgrcv failed with error:%d\n",errno);exit(EXIT_FAILURE);}printf("You wrote:%s",some_data.some_text);if(strncmp(some_data.some_text,"end",3)==0) running=0;}if(msgctl(msgid,IPC_RMID,0)==-1){//把消息队列删除fprintf(stderr,"msgctl(IPC_RMID)failed\n");exit(EXIT_FAILURE);}exit(EXIT_SUCCESS);}

使用代码来显示POSIX信号量和IPC信号量之间的差异。

Posix信号量

#include <stdio.h>
#include <semaphore.h>
#include <fcntl.h>int main() {// 创建和初始化POSIX信号量sem_t *posix_sem = sem_open("/my_posix_sem", O_CREAT, 0666, 1);// 在临界区使用信号量sem_wait(posix_sem);// 临界区操作printf("临界区操作\n");sem_post(posix_sem);// 删除POSIX信号量sem_close(posix_sem);sem_unlink("/my_posix_sem");return 0;
}

IPC信号量:

#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/sem.h>int main(){//创建一个信号量集,并且设置信号量集中信号量的值为1key_t key=12345;int sem_id=semget(key,1,0666|IPC_CREAT);//不存在就创建if(sem_id==-1){perror("semget error");exit(1);}//对信号p操作struct sembuf sops;sops.sem_num=0;sops.sem_op=-1;//psop.sem_flg=0;if(semop(sem_id,&sops,1)<0){perror("semop error");return -1;}//互斥区printf("互斥区操作\n");sops.sem_op=1;//v操作if(semop(sem_id,&sops,1)<0){perror("semop error");return -1;}//删除信号量if(semctl(sem_id,0,IPC_RMID)<0){perror("semctl error");return -1;}return 0;
}

posix信号量:

  1. posix信号量通常使用sem_open函数创建,信号由斜杠字符/开头的名称标识
  2. posix信号量可以在进程之间共享,进程可以使用上面的/开头的名称标识打开相同的信号量
  3. posix信号量支持使用sem_wait(P)、sem_post(V)、sem_close(关闭)等操作
  4. posix可以使用sem_ulink显示销毁,也可以在最后一个进程使用sem_close关闭信号量的时候自动销毁

System-V IPC信号量:

  1. 这种信号量通常使用semget系统调用创建,由一个整数标识符标识
  2. ipc信号量也可以在进程之间共享,但是这种共享是基于整数标识符而不是名称标识符
  3. ipc信号量的操作是使用semop系统调用来完成的,该调用允许在单个原子操作中执行一组信号量操作。
  4. ipc信号量使用带有IPC_RMID命令的semctl来删除信号量

在文件一章中,我们讨论了mmap(),它可以用于在进程之间共享内存。您能否将此技术与IPC的共享内存进行比较,并说明哪种技术更适合在进程之间共享大数据。

mmap: mmap是一种将文件内存映射到进程的虚拟地址空间的技术,在这种机制下,文件可以被视为内存的一部分,可以实现程序直接对这部分的内存进行读写操作,实现像访问内存一样对文件进行读写,这种方法提高了处理的效率,简化了文件的操作。

共享内存:共享内存使用的是shmget和shmat;共享内存提供semop,可以用于共享内存的进程之间的同步

二者区别:
数据源和持久化:
mmap: 通过 mmap 映射的数据通常来自文件系统中的文件。这意味着数据是持久化的——即使程序终止,文件中的数据依然存在。当你通过映射的内存区域修改数据时,这些更改最终会反映到磁盘上的文件中。
共享内存:共享内存是一块匿名的(或者有时与特定文件关联的)内存区域,它可以被多个进程访问。与 mmap 映射的文件不同,共享内存通常是非持久的,即数据仅在计算机运行时存在,一旦系统关闭或重启,存储在共享内存中的数据就会丢失。

使用场景:
mmap:mmap 特别适合于需要频繁读写大文件的场景,因为它可以减少磁盘 I/O 操作的次数。它也允许文件的一部分被映射到内存中,这对于处理大型文件尤为有用。
共享内存:共享内存通常用于进程间通信(IPC),允许多个进程访问相同的内存区域,这样可以非常高效地在进程之间交换数据。

性能和效率:
mmap:映射文件到内存可以提高文件访问的效率,尤其是对于随机访问或频繁读写的场景。系统可以利用虚拟内存管理和页面缓存机制来优化访问。
共享内存:共享内存提供了一种非常快速的数据交换方式,因为所有的通信都在内存中进行,没有文件 I/O 操作。

同步和一致性:
mmap:使用 mmap 时,必须考虑到文件内容的同步问题。例如,使用 msync 调用来确保内存中的更改被同步到磁盘文件中。
共享内存:在共享内存的环境中,进程需要使用某种形式的同步机制(如信号量、互斥锁)来避免竞争条件和数据不一致。

简而言之,mmap 主要用于将文件映射到内存以提高文件操作的效率,而共享内存主要用于进程间的高效数据交换。二者虽有相似之处,但各自适用于不同的应用场景,如果文件非常大,以至于无法或不方便完全加载到内存中时,可以使用mmap映射整个文件,并像访问内存数组一样访问文件的任何部分,而无需加载整个文件;如果是进程之间需要快速共享大量数据,可以使用共享内存。


如何在典型的应用场景中创建TCP和UDP套接字。

TCP:sockfd = socket(AF_INET, SOCK_STREAM,0);
UDP:sockfd =socket(AF_INET, SOCK_DGRAM,0);

描述了TCP/UDP通信中客户端和服务器的框架。使用代码展示如何创建并发服务器。

TCP服务器端:
通过上图我们可以看到服务器端的流程是:
调用socket()创建一个新的socket。
调用bind()将创建的socket绑定在一个地址上,即分配ip和端口。
调用listen()监听客户端访问。
调用accept()接收客户端的访问
调用read()/write()或者send()/recv()传输数据。

TCP客户端的流程是:
调用socket()创建一个新的socket
调用connect()将客户端通过地址请求连接到服务器的socket上。
调用read()/write()或者send()/recv()传输数据。

UDP服务器端的流程是:
调用socket()创建一个socket,类似于创建一个邮箱。
调用bind()绑定服务器的地址(一个众所周知的地址,比如一个公司的邮箱地址,以便允许客户访问)。
调用recvfrom()/sendto()接收、发送数据报。
调用close()关闭socket

UDP客户端的流程是:
调用socket()创建一个socket,类似于创建一个邮箱。
调用recvfrom()/sendto()接收、发送数据报。
调用close()关闭socket

使用代码显示select()、poll()函数的优点。

使用select和poll同时处理多个客户端连接请求

select代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>#define MAX_CLIENTS 5int main() {int server_socket, client_sockets[MAX_CLIENTS];fd_set readfds;int max_sd, activity, new_socket, i;struct sockaddr_in address;int addrlen = sizeof(address);// Create server socketserver_socket = socket(AF_INET, SOCK_STREAM, 0);// Initialize sockaddr_in structure// ...// Bind server socket// ...// Listen for incoming connections// ...while (1) {FD_ZERO(&readfds);FD_SET(server_socket, &readfds);max_sd = server_socket;for (i = 0; i < MAX_CLIENTS; i++) {int sd = client_sockets[i];if (sd > 0)FD_SET(sd, &readfds);if (sd > max_sd)max_sd = sd;}// Wait for an activity on any of the socketsactivity = select(max_sd + 1, &readfds, NULL, NULL, NULL);// Handle activity on the server socketif (FD_ISSET(server_socket, &readfds)) {// Accept a new connectionnew_socket = accept(server_socket, (struct sockaddr*)&address, (socklen_t*)&addrlen);// Add the new socket to the array of client sockets// ...}// Handle activity on client socketsfor (i = 0; i < MAX_CLIENTS; i++) {int sd = client_sockets[i];if (FD_ISSET(sd, &readfds)) {// Handle data from client// ...}}}return 0;
}

poll代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>#define MAX_CLIENTS 5int main() {int server_socket, client_sockets[MAX_CLIENTS];struct pollfd fds[MAX_CLIENTS + 1];int nfds = 1; // including server socketint i, ret;struct sockaddr_in address;int addrlen = sizeof(address);// Create server socketserver_socket = socket(AF_INET, SOCK_STREAM, 0);// Initialize sockaddr_in structure// ...// Bind server socket// ...// Listen for incoming connections// ...// Initialize pollfd structure for server socketfds[0].fd = server_socket;fds[0].events = POLLIN;for (i = 0; i < MAX_CLIENTS; i++) {fds[i + 1].fd = -1; // initialize client sockets to -1}while (1) {// Wait for an activity on any of the socketsret = poll(fds, nfds, -1);// Handle activity on the server socketif (fds[0].revents & POLLIN) {// Accept a new connectionclient_sockets[nfds - 1] = accept(server_socket, (struct sockaddr*)&address, (socklen_t*)&addrlen);fds[nfds].fd = client_sockets[nfds - 1];fds[nfds].events = POLLIN;nfds++;}// Handle activity on client socketsfor (i = 1; i < nfds; i++) {if (fds[i].revents & POLLIN) {// Handle data from client// ...}}}return 0;
}

(1)select ()优点:
1)select 的可移植性更好,在某些 Unix 系统上不支持 poll。
2)select 对于超时值提供了更好的精度:微秒,而 poll 是毫秒。
(2)poll() 函数的优点:
1)poll 不要求开发者计算最大文件描述符加一的大小。
2)poll 在应付大数目的文件描述符的时候相比于 select 速度更快。
3)它没有最大连接数的限制,原因是它是基于链表来存储的。

// 使用select()
fd_set read_fds;
FD_ZERO(&read_fds);
FD_SET(socket1, &read_fds);
FD_SET(socket2, &read_fds);
select(max_socket + 1, &read_fds, NULL, NULL, NULL);
// 使用poll()
struct pollfd fds[2];
fds[0].fd = socket1;
fds[0].events = POLLIN;
fds[1].fd = socket2;
fds[1].events = POLLIN;poll(fds, 2, timeout_in_milliseconds);

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/205855.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何使用录屏软件在电脑录制PDF文件

我有一个PDF文件&#xff0c;想用录屏软件将它录制下来并添加上详细的注释&#xff0c;然后发给客户看&#xff0c;请问应该如何录制呢&#xff1f;有没有推荐的录屏软件呢&#xff1f; 不用担心&#xff0c;本文将会详细的为您讲解如何使用录屏软件在电脑端录制PDF文件&#…

软件测试jmeter基本使用

1安装与配置 1.jdk下载 下载地址&#xff1a;https://www.oracle.com/java/technologies/downloads/#jdk18-windows&#xff08;压缩包中会给&#xff09; 2.jmeter下载 Apache JMeter - Download Apache JMeter&#xff08;压缩包中有&#xff09; 3.操作教学 打开软件后新…

【MySQL数据库】SQL查询语句总结

目录 一、查询数据 1.1 基本查询语句 1.2 表单查询 1.3 WHERE子句 1.3.1 IN关键字查询 1.3.2 Between查询范围 1.3.3 Like匹配查询 1.3.4 AND多条件查询&#xff08;等同于&&&#xff09; 1.3.5 OR多条件查询&#xff08;等同于||&#xff09; 1.3.6 LIMIT子句 1.3.7 对…

【Node.js】笔记整理4 - 版本管理工具nvm

写在最前&#xff1a;跟着视频学习只是为了在新手期快速入门。想要学习全面、进阶的知识&#xff0c;需要格外注重实战和官方技术文档&#xff0c;文档建议作为手册使用 系列文章 【Node.js】笔记整理 1 - 基础知识【Node.js】笔记整理 2 - 常用模块【Node.js】笔记整理 3 - n…

【docker】docker安装与优化

目录 一、安装Docker 1、关闭防火墙 2、安装依赖包 3、设置阿里云镜像源 4、安装Docker-CE社区版并设置为开机自启动 5、查看Docker信息 二、设置镜像加速 1、申请加速地址 2、实现加速操作 三、网络优化 1、如何网络优化 2、具体操作 四、docker-server端配置文件…

前端:实现二级菜单(点击实现二级菜单展开)

效果 代码 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" content"widthdevice-width, i…

【Excel】WPS快速按行筛选过滤

用的筛选都是进行列数据过滤&#xff0c;那么遇到一个情况需要行数据过滤查看数据 行过滤 选中行&#xff0c;然后右键菜单&#xff0c;行筛选。 列过滤

7Docker搭建es和kibana

一、安装es 1.拉取镜像 sudo docker pull elasticsearch:7.12.0 elasticsearch:7.12.0:我安装的版本是7.12.0&#xff0c;可以根据实际的情况安装 创建docker容器挂在的目录&#xff1a; sudo mkdir -p /opt/elasticsearch/config sudo mkdir -p /opt/elasticsearch/data s…

使用Python爬取快手视频与评论(App与Web端分析)

文章目录 1. APP端抓包配置2. APP端抓包分析3. Web端抓包分析4. 爬虫代码实现 【作者主页】&#xff1a;吴秋霖 【作者介绍】&#xff1a;Python领域优质创作者、阿里云博客专家、华为云享专家。长期致力于Python与爬虫领域研究与开发工作&#xff01; 【作者推荐】&#xff1a…

13:kotlin类和对象 -- 属性(Properties)

定义属性 类属性可使用var和val定义 class Address {var name: String "Holmes, Sherlock"var street: String "Baker"var city: String "London"var state: String? nullvar zip: String "123456" }属性使用 fun copyAddres…

【虚拟机】Docker基础 【一】

1.1.部署MySQL 首先&#xff0c;我们利用Docker来安装一个MySQL软件&#xff0c;大家可以对比一下之前传统的安装方式&#xff0c;看看哪个效率更高一些。 如果是利用传统方式部署MySQL&#xff0c;大概的步骤有&#xff1a; 搜索并下载MySQL安装包上传至Linux环境编译和配置…

低调使用。推荐一个 GPT4 Turbo、Vision、GPTs、DELL·E3 等所有最新功能同步可用国内网站

在 11 月 6 日&#xff0c;万众期待的 OpenAI DevDay&#xff0c;ChatGPT 发布了一系列新的产品&#xff0c;其中推出了 GPT4 Turbo&#xff0c;并且将GPT4 Vision&#xff0c;DELLE3 等等能力全部集合到一起&#xff0c;不需要再分开使用&#xff0c;原来的局限的文本聊天也进…

php订单发起退款(余额和微信支付)

index.html <a class"btn btn-danger btn-change btn-tuikuan btn-disabled" href"javascript:;"><i class"fa fa-tuikuan"></i> 订单退款</a>-->order.js // 为表格绑定事件Table.api.bindevent(table);//退款按钮…

【经验分享】openGauss 客户端(Data Studio / DBeaver)连接方式

前言 本篇介绍了openGauss常用的客户端连接工具Data Studio和DBeaver 01 客户端工具 openGauss部署之后&#xff0c;在服务器上提供了在命令行下运行的数据库连接工具gsql。此工具除了具备操作数据库的基本功能&#xff0c;还提供了若干高级特性&#xff0c;便于用户使用。…

建设“参与城市”大学--SMU在2023年绿色金融全球论坛上分享观点

2023年11月21日&#xff0c;由新加坡管理大学&#xff08;SMU&#xff0c;简称新大&#xff09;和中国人民大学&#xff08;RUC&#xff0c;简称人大&#xff09;联合主办的“绿色金融与治理&#xff1a;从承诺到行动”全球论坛在北京召开。论坛汇集了来自新加坡、中国及世界各…

【C++高阶(六)】哈希的应用--位图布隆过滤器

&#x1f493;博主CSDN主页:杭电码农-NEO&#x1f493;   ⏩专栏分类:C从入门到精通⏪   &#x1f69a;代码仓库:NEO的学习日记&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学习C   &#x1f51d;&#x1f51d; 哈希的应用 1. 前言2. 位图的概念以及定义3. 位…

记一篇Centos7安装innodb_ruby

安装innodb_ruby过程非常坎坷&#xff0c;这里记录下安装过程&#xff0c;有些坑当时没有记录下来&#xff0c;主要把完成安装过程就记录下来 yum安装ruby默认的会安装ruby2.0.0版本&#xff0c;但是在安装innodb_ruby时&#xff0c;会报错&#xff0c;提示至少需要2.4版本以上…

探索 Rollup:简化你的前端构建流程

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

Rust UI开发(四):iced中如何添加菜单栏(串口调试助手)

注&#xff1a;此文适合于对rust有一些了解的朋友 iced是一个跨平台的GUI库&#xff0c;用于为rust语言程序构建UI界面。 这是一个系列博文&#xff0c;本文是第四篇&#xff0c;前三篇链接&#xff1a; 1、Rust UI开发&#xff08;一&#xff09;&#xff1a;使用iced构建UI时…

Amazon CodeWhisperer 使用体验

文章作者&#xff1a;STRIVE Amazon CodeWhisperer 是最新的代码生成工具&#xff0c;支持多种编程语言&#xff0c;如 java,js,Python 等&#xff0c;能减少开发人员手敲代码时间&#xff0c;提升工作效率。PS:本人是一名 CodeWhisperer 业余爱好者 亚马逊云科技开发者社区为开…