• linux概念之IPC


    一切皆文件,文件类型为普通,目录,管道,socket,链接,块设备,字符设备
    cd /proc/2305/fd/

    该进程打开了6个文件描述符,三个为字符设备即012,三个为socket即345,socket的两类协议簇,即inet与unix。其中3是ipv4的,4是unix,5是ipv6的。
    查看man netstat会看得比较清楚

    man netstat所看到的
    Active Internet connections (TCP, UDP, raw)
    The protocol (tcp, udp, raw) used by the socket.
    Active UNIX domain Sockets
    The protocol (usually unix) used by the socket.

    w/o server是什么意思
    Active Internet connections (w/o servers)
    Active UNIX domain sockets (w/o servers)
    -p, --program
    Show the PID and name of the program to which each socket belongs.
    -l, --listening
    Show only listening sockets. (These are omitted by default.)
    -a, --all
    Show both listening and non-listening sockets. With the --interfaces option, show interfaces that are not marked

    Unix域协议并不是一个实际的协议族,而是在单个主机上执行客户/服务器通信的一种方法,它其实是IPC(InterProcess Communication)进程间通信中的一种,顺便提一下,
    进程间通信可用如下方式:管道(半双工),FIFOS(命名管理),流管道(全双工),命令流管道,消息队列,信号量,共享内存,套接口,流
    IPC是一种标准的Unix通信机制。分类LPC(本地)与RPC(网络)
    前几种通常限于同一台主机的各个进程间通信,后两种可以是不同主机上的各进程间通信。与网络通信不同,网络中双方确认需要IP和端口号,而在同一台机器上的2个进程则不需要这么麻烦,
    如果写过管道通信的例子,则这里类似于管道,需要定义是一个用于通信的文件(不能是系统中已有的文件)

    Unix域提供两类套口:字节流套接口和数据报套接口。使用Unix域套接口的理由有三个:
    1,在源自Berkeley的实现中,Unix域套接口往往比通信两端位于同一个主机的TCP套接口快出一倍。
    2,Unix域套接口可用于同一个主机上的不同进程之间传递描述字。
    3,Unix域套接口较新的实现把客户的凭证(用户ID和组ID)提供给服务器,从而能够提供额外的安全检查措施。

    这两天之前在看UNIX socket中的实现,想着试试他的性能跟TCP/IP有什么区别,如果它的性能还不及TCP/IP的话,那么他也没有什么存在的意义。
    写了一个简单的echo server,简单的测试了几组数据,然后发现其性能确实比TCP/IP性能要高,数值比较接近UNIX网络编程里提到的2倍。

    socket的就是走网络,普通文件的就是走磁盘IO。
    为了将不同的类型的I/O与对应的文件描述符绑定,则是需要不同的初始化函数的。文件i/o与网络i/o
    普通文件就通过open函数,指定对应的文件路径,操作系统通过路径能够找到对应的文件系统类型,如ext4啊,fat啊等等。
    网络就通过socket函数来初始化,socket函数就通过(domain, type, protocol)来找到对应的网络协议栈,比如TCP/IP,UNIX等等。
    所以网络相关的调用,如listen,connect, bind等等,第一步基本上就是通过文件描述符找到对应的内核socket结构,然后在进行对应的操作。
    在socket层内核完成的就是一个interface功能,或许也可以叫做桥接模式(bridge pattern)。

    内核支持某功能

    由于LVS像iptables一样是工作在内核层,所以只需要安装模块ip_vs就可以了,并没有后台进程在跑
    内核空间与用户空间工具
    netfilter/iptables
    ipvs/ipvsadm
    kvm/qemu
    drbd/

    ll /proc/sys/fs/inotify   #列出文件目录,出现下面的内容,说明服务器内核支持inotify
    -rw-r--r-- 1 root root 0 Mar  7 02:17 max_queued_events
    -rw-r--r-- 1 root root 0 Mar  7 02:17 max_user_instances
    -rw-r--r-- 1 root root 0 Mar  7 02:17 max_user_watches
    备注:Linux下支持inotify的内核最小为2.6.13,可以输入命令:uname -a查看内核
    CentOS 5.X 内核为2.6.18,默认已经支持inotify

    开发和维护内核是一件很繁杂的工作,因此,只有那些最重要或者与系统性能息息相关的代码才将其安排在内核中。其它程序,比如GUI,管理以及控制部分的代码,一般都会作为用户态程序。在linux系统中,把系统的某个特性分割成在内核中和在用户空间中分别实现一部分的做法是很常见的(比如linux系统的防火墙就分成了内核态的Netfilter和用户态的iptables)。然而,内核程序与用户态的程序又是怎样行通讯的呢?
    答案就是通过各种各样的用户态和内核态的IPC(interprocess   communication  )机制来实现。比如系统调用,ioctl接口,proc文件系统以及netlink socket,本文就是要讨论netlink socekt并向读者展示这种用网络通讯接口方式实现的IPC机制的优点。

    用户态与内核态通信
    多数的 Linux 内核态程序都需要和用户空间的进程交换数据,但 Linux 内核态无法对传统的 Linux 进程间同步和通信的方法提供足够的支持!本文就总结下常见的ipc,
    1.getsockopt/setsockopt     
    2.mmap      mmap加载文件后注意还要mknod
    3.netlink/socket      
    4.proc/seq     

    (1)切记不能访问已经卸载的一个proc文件,否则会导致kernel panic;
    (2)注意不要删除正在调用的问proc文件。因为文件的入口项不存在关联的作者,文件的调用也并没有作用到模块的引用计数上;
    (3)勿注册两个同名的proc文件,这样将导致入口项无法区分。
    以上这些都是proc文件的缺点,也是使用时必须注意的地方。所以,现在推荐使用seq_file 和sys_fs。

    Seq_file File System
    针对proc文件的不足而诞生了Seq_file。
    Seq_file的实现基于proc文件。使用Seq_file,用户必须抽象出一个链接对象,然后可以依次遍历这个链接对象。这个链接对象可以是链表,数组,哈希表等等。

    一般地,内核通过在procfs文件系统下建立文件来向用户空间提供输出信息,用户空间可以通过任何文本阅读应用查看该文件信息,但是procfs 有一个缺陷,如果输出内容大于1个内存页,需要多次读,因此处理起来很难,另外,如果输出太大,速度比较慢,有时会出现一些意想不到的情况, Alexander Viro实现了一套新的功能,使得内核输出大文件信息更容易,该功能出现在2.4.15(包括2.4.15)以后的所有2.4内核以及2.6内核中,尤其 是在2.6内核中,已经大量地使用了该功能。


    5.copy_from_user/copy_to_user  这个与mmap的类似都依赖于一个字符设备文件
    6.文件,严格来说不算


    用户与内核之间的通信主要有内核启动参数、模块参数、sysfs、procfs、sysctl、netlink、seq_file、系统调用、debug、relayfs等等。
    内核启动参数:简单的说linux使用bootloader向开发者提供接口向内核启动传输一个参数从而控制内核启动行为。
    模块参数:写过内核参数的对这个很了解,使用module_param可以在加载内核模块时输入用户参数。
    sysfs和procfs都是获取内核行为的方式。主要区别是sysfs除了用于访问内核状态、计算机属性、进程状态信息之外还会对设备进行管理。
    sysctl:用户可以通过sysctl配置内核参数。
    seq_file: seq_file是在procfs的基础上发展起来的,主要弥补了profs中内核向用户输出大量数据时的多次读写以及速度慢的问题。
    系统调用:是实现用户与内核通信的一种软中断方式。
    bebug:是一个虚拟文件系统,主要用于内核开发者调试信息时向用户空间输出调试信息。
    relayfs:relayfs是一个快速的转发(relay)数据的文件系统,它为那些需要从内核空间转发大量数据到用户空间的工具和应用提供了快速有效的转发机制。

    用户态进程间通信
    常见的进程间通信方式主要有:pipe、有名管道、信号量、消息队列、共享内存、信号量以及套接字。但是上面的通信方式都不能实现用户与内核之间的交互。

    1 常见进程间通信方式

    通信方式

    无法实现内核与用户之间通信原因

    管道(不包括命名管道)

    局限于父子进程间的通信。

    消息队列

    在硬、软中断中无法无阻塞地接收数据。

    信号量

    无法介于内核态和用户态使用。

    共享内存

    需要信号量辅助,而信号量又无法使用。

    套接字

    在硬、软中断中无法无阻塞地接收数据。

    2 用户与内核通信方式对比

    通信方式

    通信方式类别

    procfsdebugsysfsrelayfs

    基于文件系统的通信方式

    系统调用、sysctl

    用户空间发起

    内核启动参数、模块参数

    用户设置内核参数

    seq_file

    内核向用户输出信息

    从表2可以看出,用户态与内核态之间的通信要么基于文件系统,要么是用户与内核单向通信,有没有用户与内核实现双向通信的方式?netlink基于sock,实现了用户与内核之间小量数据之间的交互。

    系统调用与标准库函数实现之间的差异。

     

     

    信号量与互斥锁

    1965年,荷兰学者Edsger Dijkstra提出的信号量(Semaphores)机制是一种卓有成效的进程同步工具,在长期广泛的应用中,信号量机制得到了极大的发展,它从整型信号量经记录型信号量,进而发展成为“信号量集机制”,现在信号量机制已经被广泛的应用到单处理机和多处理机系统以及计算机网络中。

    以一个停车场的运作为例。简单起见,假设停车场只有三个车位,一开始三个车位都是空的。这时如果同时来了五辆车,看门人允许其中三辆直接进入,然后放下车拦,剩下的车则必须在入口等待,此后来的车也都不得不在入口处等待。这时,有一辆车离开停车场,看门人得知后,打开车拦,放入外面的一辆进去,如果又离开两辆,则又可以放入两辆,如此往复。
    在这个停车场系统中,车位是公共资源,每辆车好比一个线程,看门人起的就是信号量的作用。

    信号量(Semaphore),有时被称为信号灯,是在多线程环境下使用的一种设施,是可以用来保证两个或多个关键代码段不被并发调用。在进入一个关键代码段之前,线程必须获取一个信号量;一旦该关键代码段完成了,那么该线程必须释放信号量。其它想进入该关键代码段的线程必须等待直到第一个线程释放信号量。为了完成这个过程,需要创建一个信号量VI,然后将Acquire Semaphore VI以及Release Semaphore VI分别放置在每个关键代码段的首末端。确认这些信号量VI引用的是初始创建的信号量。

    用ipcs查看ipc相关信息。

    14:20:33 8 ~:#ipcs -l

    ------ Shared Memory Limits --------
    max number of segments = 4096
    max seg size (kbytes) = 67108864
    max total shared memory (kbytes) = 17179869184
    min seg size (bytes) = 1

    ------ Semaphore Limits --------
    max number of arrays = 128
    max semaphores per array = 250
    max semaphores system wide = 32000
    max ops per semop call = 32
    semaphore max value = 32767

    ------ Messages: Limits --------
    max queues system wide = 973
    max size of message (bytes) = 65536
    default max size of queue (bytes) = 65536

    ipcrm 命令
    移除一个消息对象。或者共享内存段,或者一个信号集,同时会将与ipc对象相关链的数据也一起移除。当然,只有超级管理员,或者ipc对象的创建者才有这项权利啦

    ipcrm用法
    ipcrm -M shmkey  移除用shmkey创建的共享内存段
    ipcrm -m shmid    移除用shmid标识的共享内存段
    ipcrm -Q msgkey  移除用msqkey创建的消息队列
    ipcrm -q msqid  移除用msqid标识的消息队列
    ipcrm -S semkey  移除用semkey创建的信号
    ipcrm -s semid  移除用semid标识的信号

     

     

    用户态与内核态通信之系统调用

    http://www.linuxidc.com/Linux/2014-12/110238.htm

    系统调用(System Call)是操作系统为在用户态运行的进程与硬件设备(如CPU、磁盘、打印机等)进行交互提供的一组接口。当用户进程需要发生系统调用时,CPU 通过软中断切换到内核态开始执行内核系统调用函数。下面介绍Linux 下三种发生系统调用的方法:

    1.glibc提供的库函数

    glibc 是 Linux 下使用的开源的标准 C 库,它是 GNU 发布的 libc 库,即运行时库。glibc 为程序员提供丰富的 API(Application Programming Interface),除了例如字符串处理、数学运算等用户态服务之外,最重要的是封装了操作系统提供的系统服务,即系统调用的封装。那么glibc提供 的系统调用API与内核特定的系统调用之间的关系是什么呢?

    • 通常情况,每个特定的系统调用对应了至少一个 glibc 封装的库函数,如系统提供的打开文件系统调用 sys_open 对应的是 glibc 中的 open 函数;
    • 其次,glibc 一个单独的 API 可能调用多个系统调用,如 glibc 提供的 printf 函数就会调用如 sys_opensys_mmapsys_writesys_close 等等系统调用;
    • 另外,多个 API 也可能只对应同一个系统调用,如glibc 下实现的 malloccallocfree 等函数用来分配和释放内存,都利用了内核的 sys_brk 的系统调用。

    举例来说,我们通过 glibc 提供的chmod 函数来改变文件 etc/passwd 的属性为 444:

    2.glibc提供的syscall函数

     

    3.通过int中断

    如果我们知道系统调用的整个过程的话,应该就能知道用户态程序通过软中断指令int 0x80 来陷入内核态(在Intel Pentium II 又引入了sysenter指令),参数的传递是通过寄存器,eax 传递的是系统调用号,ebx、ecx、edx、esi和edi 来依次传递最多五个参数,当系统调用返回时,返回值存放在 eax 中。

    仍然以上面的修改文件属性为例,将调用系统调用那段写成内联汇编代码:

     

    用户态与内核态通信之内核启动参数

    https://www.kernel.org/pub/linux/kernel/v2.6/longterm/v2.6.32/linux-2.6.32.71.tar.gz

    [提示]内核源码树下的 Documentation/kernel-parameters.txtDocumentation/x86/x86_64/boot-options.txt 文件列出了所有可用的引导参数,并作了简要说明。

    kernel-parameters

    boot loader parameter  Documentation/x86/boot.txt

    http://www.jinbuguo.com/kernel/boot_parameters.html

    cat /boot/grub/grub.conf

    kernel /vmlinuz-2.6.32-431.el6.x86_64 ro root=/dev/mapper/vg_vm1-lv_root rd_NO_LUKS LANG=en_US.UTF-8 rd_NO_MD rd_LVM_LV=vg_vm1/lv_root SYSFONT=latarcyrheb-sun16 crashkernel=auto rd_LVM_LV=vg_vm1/lv_swap  KEYBOARDTYPE=pc KEYTABLE=us rd_NO_DM rhgb quiet

    cat /proc/cmdline
    ro root=/dev/mapper/vg_vm1-lv_root rd_NO_LUKS LANG=en_US.UTF-8 rd_NO_MD rd_LVM_LV=vg_vm1/lv_root SYSFONT=latarcyrheb-sun16  rd_LVM_LV=vg_vm1/lv_swap  KEYBOARDTYPE=pc KEYTABLE=us rd_NO_DM rhgb quiet

     

    在Redhat的系统中,还有个经常看到的kernel启动参数——rhgb,rhgb表示redhat graphics boot,就是会看到图片来代替启动过程中显示的文本信息,这些信息在启动后用dmesg也可以看到
    rhgb = redhat graphical boot – This is a GUI mode booting screen with most of the information hidden while the user sees a rotating activity icon spining and brief information as to what the computer is doing.
    quiet = hides the majority of boot messages before rhgb starts. These are supposed to make the common user more comfortable. They get alarmed about seeing the kernel and initializing messages, so they hide them for their comfort.

    在Linux中,给kernel传递参数以控制其行为总共有三种方法:
    1.build kernel之时的各个configuration选项。
    2.当kernel启动之时,可以参数在kernel被GRUB或LILO等启动程序调用之时传递给kernel。
    3.在kernel运行时,修改/proc或/sys目录下的文件。

    这里我简单讲的就是第二种方式了,kernel在grub中配置的启动参数。
    首先,kernel有哪些参数呢? 在linux的源代码中,有这样的一个文档Documentation/kernel-parameters.txt,它介绍了kernel的各个参数及其意义。

    顺便贴一个内核版本为2.6.32.1的内核的kernel-parameters文档


    其次,kernel启动参数以空格分隔,而且是严格区分大小写的(如:mem和MEM是不一样的)。
    再次,对于module特有的kernel参数写法是这样的,[module name].[parameter=XX],例如,igb.max_vfs=7这个kernel启动参数的效果就是相当于这样来动态加载module: modprobe igb max_vfs=7
    另外,kernel是怎样处理这些启动参数的呢? 启动参数通常是这样的形式: name[=value_1][,value_2]…[,value_10]
    “name”是关键字,内核用它来识别应该把”关键字”后面的值传递给谁,也就是如何处理这个值,是传递给处理进程还是作为环境变量或者抛给”init”。值的个数限制为10,你可以通过再次使用该关键字使用超过10个的参数。 首先,kernel检查关键字是不是 ‘root=’, ‘nfsroot=’, ‘nfsaddrs=’, ‘ro’, ‘rw’, ‘debug’或’init’,然后内核在bootsetups数组里搜索于该关键字相关联的已注册的处理函数,如果找到相关的已注册的处理函数,则调用这些函数并把关键字后面的值作为参数传递给这些函数。比如,你在启动时设置参数name=a,b,c,d,内核搜索bootsetups数组,如果发现”name”已注册,则调用”name”的设置函数如name_setup(),并把a,b,c,d传递给name_setup()执行。 所有型如”name=value”参数,如果没有被上面所述的设置函数接收,将被解释为系统启动后的环境变量,比如”TERM=vt100″启动参数就会被作为一个启动后的环境变量。所有没有被内核设置函数接收也没又被设置成环境变量的参数都将留给init进程处理,比如”single”。
    下面简单总结一下我在工作中常用到的一些kernel启动参数吧。

     

    ======================================

    rpc程序,rpc编程  http://blog.chinaunix.net/uid-1724205-id-2813082.html  sun rpc编程简介

    一、 概述
      在传统的编程概念中,过程是由程序员在本地编译完成,并只能局限在本地运行的一段代码,也即其主程序和过程之间的运行关系是本地调用关系。因此这种结构在网络日益发展的今天已无法适应实际需求。总所周知,传统过程调用模式无法充分利用网络上其他主机的资源(如CPU、Memory等),也无法提高代码在实体间的共享程度,使得主机资源大量浪费。
      而本文要介绍的RPC编程,正是很好地解决了传统过程所存在的一系列弊端。通过RPC我们可以充分利用非共享内存的多处理器环境(例如通过局域汪连接得多台工作站),这样可以简便地将你的应用分布在多台工作站上,应用程序就像运行在一个多处理器的计算机上一样。你可以方便的实现过程代码共享,提高系统资源的利用率,也可以将以大量数值处理的操作放在处理能力较强的系统上运行,从而减轻前端机的负担。

    以下我将通过实例向读者介绍通过简单层RPC的实现方法。通常在此过程中我们将使用RPC协议编译工具-Rpcgen。Rpcgen 工具用来生成远程程序接口模块,它将以RPC语言书写的源代码进行编译,Rpc 语言在结构和语法上同C语言相似。由Rpcgen 编译生成的C源程序可以直接用C编译器进行编译,因此整个编译工作将分为两个部分。Rpcgen的源程序以.x结尾,通过其编译将生成如下文件:
      a) 一个头文件(.h)包括服务器和客户端程序变量、常量、类型等说明。
      b) 一系列的XDR例程,它可以对头文件中定义的数据类型进行处理。
      c) 一个Server 端的标准程序框架。
      d) 一个Client 端的标准程序框架。

     

  • 相关阅读:
    网络编程(1)
    反射,魔法方法,单例模式
    远程的文件传输
    DNS
    windows服务
    outlook邮箱配置
    win7服务器搭建
    windows常用命令
    C盘满了怎么办
    0x80070035找不到网络路径
  • 原文地址:https://www.cnblogs.com/createyuan/p/3715802.html
Copyright © 2020-2023  润新知