Linux系统启动过程:
1.上电后,BIOS(Basic Input Output System)芯片开始工作。做两个事情:POST(Power-on self test)检测外围关键设备是否正常(CPU、内存、显卡、键盘);POST后,按启动顺序列表,执行启动设备扇区内容。比如硬盘的MBR的代码(512字节代码,0磁道0柱面1扇区)。
2.MBR有446字节为bootLoader程序,64字节存储分区信息(最多四个分区)。Bootloader程序进一步加载2扇区的start.S,startS有两种方式前进:加载3扇区识别文件系统能力,加载/boot/grub下stage2文件;BIOS中断方式寻址(8GB空间内)加载/boot下的stage2.
3.Stage2加载后,解析grub.conf,产生grub界面,加载initrd内容,执行initrd的init脚本。Init脚本加载驱动,创建根设备运行/sbin/init,此为系统一号进程,读取/etc/inittab执行相关工作,rc.sysinit, rc, rc.local等等。
4.Linux图形界面由X client和server通过X协议通讯。Gnome和KDE除了x窗口管理器,还各自有自己的编辑器,浏览器等应用程序。VNC server也是X server
vnc viewer<---(vnc protocal)--->Xvnc (vnc server) <----(X protocal) ----> app
进程启动:
进程都是由fork或exec系统调用产生的。Fork是写时复制技术,vfork基本不用,clone选择性复制资源;exec用新程序替代当前进程上下文。exec函数族的函数执行成功后不会返回,失败是返回-1。shell执行程序时,先fork,在exec程序,目前动态库都采用延迟加载,和延迟解析技术。
当ELF格式程序执行时,内核产生task_struct结构来表示此进程,也常叫PCB(process control block,进程控制块)。内核的schedule()函数可以选择新进程来执行,且调用switch_to进行上下文切换。
内核最大进程数受限制,可以通过/proc/sys/kernel/threads-max 的 proc 文件系统从用户空间更改此值。
调整进程优先级,需要调整进程nice值。一般用户为0-19,root用户为-20-19.root用户才能将nice值调低, renice -5 -p 5200,ps -l和top可以看到nice值。
进程状态:
R,Running:task_struct放入CPU可执行队列中(个别系统将此为Ready),都是task_running状态;
S,可中断:放入对应事件等待队列中。等信号量,等socket连接。Task_interuptible.
D,不可中断:时间短,几乎看不到,处于不能打断情况,比如内核和物理设备在交互。Task_uninterruptible.
T,sigstop信号停止进程,sigcont信号继续进程,task_stopped;gdb跟踪时,task_traced.
Z,僵尸进程:进程退出,资源被回收,只剩下task_struct(退出码,统计信息,父进程可能使用),子进程退出时会发送sigcild让父进程收尸,父进程可以通过waitid,来等待某个进程退出。如果父进程先退出,则子进程由1号进程托管。
X,进程退出状态:进程即将销毁。
查看命令:top,S列为状态,还可以查看PR、NI
top -p 12345 -p 6789//每隔5秒显示pid是12345和pid是6789的两个进程的资源占用情况
top -d 2 -c -p 123456 //每隔2秒显示pid是12345的进程的资源使用情况,并显式该进程启动的命令行参数
进程内存空间:
内核1G空间,进程用户空间3G。
映射到实际物理内存,如果申请内存太大,超过物理内存能分配大小,则报错。
进程间通信:
IPC含:管道、信号、共享内存、消息队列、信号量。
IPCS等命令可以查看。
管道速度慢,不怎么实用;消息队列容易受系统限制,也不怎么实用;信号涉及进程配合,有一定使用;信号量,主要用于进程间同步;共享内存,用于进程间大数据共享,注意使用信号量同步。
5、共享内存
共享内存是分配一块能被其他进程访问的内存,实现是通过将内存去映射到共享它的进程的地址空间,使这些进程间的数据传送不再涉及内核,即,进程间通信不需要通过进入内核的系统调用来实现;
共享内存与其他的进程间通信最大的优点是:数据的复制只有两次,一次是从输入文件到共享内存区,一次从共享内存区到输出文件
而其他的则是需要复制4次:服务器将输入文件读入自己的进程空间,再从自己的进程空间写入管道/消息队列等;客户进程从管道/消息队列中读出数据到自己的进程空间,最后输出到客户指定的文件中;
要使用共享内存,应该有如下步骤:
1.开辟一块共享内存 shmget()
2.允许本进程使用共某块共享内存 shmat()
3.写入/读出
4.禁止本进程使用这块共享内存 shmdt()
5.删除这块共享内存 shmctl()或者命令行下ipcrm
6、信号量
信号量是一种用于提供不同进程间或一个进程间的不同线程间线程同步手段的原语,systemV信号量在内核中维护
二值信号量 : 其值只有0、1 两种选择,0表示资源被锁,1表示资源可用;
计数信号量:其值在0 和某个限定值之间,不限定资源数只在0 1 之间;
计数信号量集 ;多个信号量的集合组成信号量集
1、semget函数
它的作用是创建一个新信号量或取得一个已有信号量,原型为:
1. int semget(key_t key, int num_sems, int sem_flags);
第一个参数key是整数值(唯一非零),不相关的进程可以通过它访问一个信号量,它代表程序可能要使用的某个资源,程序对所有信号量的访问都是间接的,程序先通过调用semget函数并提供一个键,再由系统生成一个相应的信号标识符(semget函数的返回值),只有semget函数才直接使用信号量键,所有其他的信号量函数使用由semget函数返回的信号量标识符。如果多个程序使用相同的key值,key将负责协调工作。
第二个参数num_sems指定需要的信号量数目,它的值几乎总是1。
第三个参数sem_flags是一组标志,当想要当信号量不存在时创建一个新的信号量,可以和值IPC_CREAT做按位或操作。设置了IPC_CREAT标志后,即使给出的键是一个已有信号量的键,也不会产生错误。而IPC_CREAT | IPC_EXCL则可以创建一个新的,唯一的信号量,如果信号量已存在,返回一个错误。
semget函数成功返回一个相应信号标识符(非零),失败返回-1.
2、semop函数
它的作用是改变信号量的值,原型为:
[cpp] view plain copy
1. int semop(int sem_id, struct sembuf *sem_opa, size_t num_sem_ops);
sem_id是由semget返回的信号量标识符,sembuf结构的定义如下:
[cpp] view plain copy
1. struct sembuf{
2. short sem_num;//除非使用一组信号量,否则它为0
3. short sem_op;//信号量在一次操作中需要改变的数据,通常是两个数,一个是-1,即P(等待)操作,
4. //一个是+1,即V(发送信号)操作。
5. short sem_flg;//通常为SEM_UNDO,使操作系统跟踪信号,
6. //并在进程没有释放该信号量而终止时,操作系统释放信号量
7. };
3、semctl函数
该函数用来直接控制信号量信息,它的原型为:
[cpp] view plain copy
1. int semctl(int sem_id, int sem_num, int command, ...);
如果有第四个参数,它通常是一个union semum结构,定义如下:
[cpp] view plain copy
1. union semun{
2. int val;
3. struct semid_ds *buf;
4. unsigned short *arry;
5. };
前两个参数与前面一个函数中的一样,command通常是下面两个值中的其中一个
SETVAL:用来把信号量初始化为一个已知的值。p 这个值通过union semun中的val成员设置,其作用是在信号量第一次使用前对它进行设置。
IPC_RMID:用于删除一个已经无需继续使用的信号量标识符。
参考:https://blog.csdn.net/xiebaocheng12138/article/category/7094256
https://blog.csdn.net/qq_34834193/article/details/53236855
https://blog.csdn.net/ljianhui/article/details/10243617