操作系统 Linux操作系统编程开发
Linux操作系统编程开发
预备知识:
1、进程操作:Linux系统是多任务的操作系统,采用进程作为任务调度的单位,进程在Linux系统下的概念是程序代码的一次执行,包括运行的代码和运行需要的数据、参数等资源。
2、进程和程序的区别:一方面:在Linux系统下,进程是程序代码的执行,所以程序是一段运行的,有生命力的程序,是一个动态的概念;一个程序是指储存在磁盘或者其他存储介质中的静态代码。另一方面:一个进程是基于一个程序运行的,而一个程序可以被重复载入到内存,形成多个进程!
3、CPU时间片(Linux系统大约1ms):时间片即CPU分配给各个程序的时间,每个线程被分配一个时间段,称作它的时间片,即该进程允许运行的时间。
4、Linux系统中进程在宏观上是并行,在微观上是串行的(一个CPU)。在宏观上是并行:同时可以打开多个进程;每个进程都有一个时间片和优先级。在微观上是串行:在每一个CPU时间片中,每个进程都有机会运行,优先级高的进程被运行的概率更大。如果时间片结束,进程还在运行,CPU将剥夺并分配给另一个进程;如果进程在时间片结束之前结束或者进入阻塞状态,CPU立即进行切换!一个CPU,一次只能执行程序的一部分。
5、mmu和多进程系统:
6、PID(进程号Process ID):在Linux系统中,每个进程都有一个进程号(PID),所有的进程使用树状结构组织。Linux系统启动时第一个进程是根进程,该进程进一步调用系统其它进程。Linux系统中,进程之间有一个明显的继承关系,所有进程都是 PID 为1的 init 进程的后代。
7、父进程和子进程:Linux系统中每个进程必有一个父进程,相应的,每个进程也可以由零个或者多个子进程。拥有同一个父进程的所有进程被称为兄弟。进程之间的关系存放在进程描述符 task_struct 中。每个 task_struct 都包含一个指向其父进程 task_struct 的指针 parent,还有一个被称为 children 的子进程链表。
8、一个线程可以创建和撤销另一个线程,同一个进程中的多个线程并发运行。<code>
9、现存进程、交换进程、init进程、精灵进程
10、僵尸进程、孤儿进程
11、进程环境:
12、进程描述符:
13、组识别码(gid-Group ID):每个登录的用户至少都会取得两个ID,一个是用户ID(UserID,简称UID),一个是用户组ID(Group ID,简称GID);每一个文件都会有所谓的所有者ID与用户组ID。
14、PCB(progress control block-进程控制块),系统通过PCB,描述进程和控制进程。在Linux系统下,PCB是 task_struct结构体。
15、进程内存分配示意图(32位操作系统):内核空间和用户空间,内核空间则为所有进程以及内核所共享。
Linux操作系统进程函数:
1、fork/vfork,创建子进程
2、exec系列函数:execv/execl、execp/execlp、execve/execle;用exec函数可以把当前进程替换为一个新进程,且新进程与原进程有相同的PID,它将进程创建与加载一个新进程映象分离。
int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg, ..., char * const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execve(const char *path, const char *argv[], char * const envp[]);
说明:
path参数表示你要启动程序的名称包括路径名
file参数表示命令的文件名
arg参数表示启动程序所带的参数,一般第一个参数为要执行命令名,不是带路径且arg必须以NULL结束
argv参数是需要传递给命令的参数,该数组以NULL结束
envp参数是环境变量数组
3、getpid、getppid:获取进程码
4、setpgid:设置指定进程的组识别码;getpgid:获取指定进程组识别码;(setpgrp、getpgrp是参数为0的特殊情况)
5、setpriority:设置指定进程、进程组、用户的优先级别;getpriority:获取指定进程(进程号)、进程组(识别码)、或者用户(用户ID)的优先级,优先级是-20~20之间的整数,数值越大优先级越高。
6、wait/waitpid:暂停当前进程,等待子进程的中断或结束。wait、参数说明:pid_t wait(int *status),status保存子进程中断或结束的状态,可设为NULL,返回值为中断或结束的子进程的进程号。waitpid、参数说明:pid_t waitpid(pid_t pid,int *status,int options) pid:-1表示任何子进程,0:组识别码相同的所有子进程;status保存子进程中断或结束的状态,可设为NULL;options;0;WNOHANG(若无子进程结束则立即返回)、WUNTRACED(若子进程暂停则立即返回);WNOHANG | WUNTRACED(两者取或),
7、进程终止函数:
_exit:直接使进程停止运行,清除其使用的内存空间,并清除其在内核的各种数据结构。exit() 函数与 _exit() 函数的最大区别在于exit()函数在调用exit系统调用前要检查文件的打开情况,把文件缓冲区中的内容写回文件,也就是“清理I/O缓冲”。
atexit:ISO C的规定,一个进程可以登记多至32个函数,这些函数将由exit自动调用,这些函数为被称为终止处理函数,并调用atexit函数来登记这些函数。
on_exit:
Linux操作系统信号量及操作函数:
预备知识:
0、临界区指同一时刻只能有一个进程执行其中代码的代码段。死锁:某个进程修改了信号量而进入临界区之后,因为崩溃或被“杀死(kill)",却而没有退出临界区,其他被挂起在信号量上的进程永远得不到运行机会。
1、资源互斥和资源同步。资源互斥:指某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性,例如死锁:进程甲占用资源A,申请资源B,进程乙占用资源B,申请资源A就会造成一种资源互斥情况“死锁”;资源同步:是指在互斥的基础上(大多数情况),通过其它机制实现访问者对资源的有序访问。
2、相交进程之间的关系主要有两种,同步与互斥。
3、处理资源的互斥与同步的几种方式: 自旋锁,信号量,互斥锁与原子变量。
4、信号量是一种能够有效处理进程之间资源互斥和同步的锁机制,信号量是一个计数器。一个信号量表示可用资源的个数。信号量的两种操作,P和V:P,等待信号量,当信号量值为0时,程序等待;当信号量值大于0时,信号量减1,程序继续运行(进程运行需要资源)。V,发送信号量,将信号量值加1,(产生资源的进程)。
5、Linux系统下的通讯机制(IPC):管道、消息队列、信号、信号值、共享内存(最快的IPC方式)、共享映射文件、套接字(不同机器之间的进程进行通讯)等。
6、信号量集合机制:多个进程(队列结构)要分别获得多个临界资源(信号量数组)后方能运行,这就是信号量集合机制。如果进程被挂起,Linux 必须保存信号量的操作状态并将当前进程放入等待队列(sem_queue)。
7、线程占用的资源:共享的内存,匿名管道,malloc出来的空间、socket描述符、epoll描述符和线程锁等。
注:2019年10月26日18:07:53,线程的资源是指那些?资源和虚拟内存的关系是什么?为什么资源不能同时被不同的进程占用,而要释放呢?不能通过不同的进程使用不同的指针调用,而且每个进程的虚拟空间(除去共享的内核空间)都是独立的,将程序加载到内存中,然后放入寄存器中,这个加载到的内存不是独立的码?那倒是因为寄存器是唯一的,所以会有资源互斥?个人理解,数据结构实现上,信号是通过字典,键和值是信号集。
8、信号量的数据结构(sem):
struct sem { int semval; /* 信号量的当前值 */ int sempid; /*在信号量上最后一次操作的进程识别号 * };
9、信号量集的数据结构(sem_ds):
struct semid_ds { struct ipc_perm sem_perm; /* IPC权限 */ long sem_otime; /* 最后一次对信号量操作(semop)的时间 */ long sem_ctime; /* 对这个结构最后一次修改的时间 */ struct sem *sem_base; /* 在信号量数组中指向第一个信号量的指针 */ struct sem_queue *sem_pending; /* 待处理的挂起操作*/ struct sem_queue **sem_pending_last; /* 最后一个挂起操作 */ struct sem_undo *undo; /* 在这个数组上的undo 请求 */ ushort sem_nsems; /* 在信号量数组上的信号量号 */ };
10、系统中每一信号量集合的队列结构(sem_queue)
struct sem_queue { struct sem_queue * next; /* 队列中下一个节点 */ struct sem_queue ** prev; /* 队列中前一个节点, *(q->prev) == q */ struct wait_queue * sleeper; /* 正在睡眠的进程 */ struct sem_undo * undo; /* undo 结构*/ int pid; /* 请求进程的进程识别号 */ int status; /* 操作的完成状态 */ struct semid_ds * sma; /*有操作的信号量集合数组 */ struct sembuf * sops; /* 挂起操作的数组 */ int nsops; /* 操作的个数 */ };
11、三种数据结构的关系
信号量操作的函数(系统调用):
1、semget ( key_t key, int nsems, int semflg ),创建并打开一个信号集
2、semop ( int semid, struct sembuf *sops, unsigned nsops),根据索引值,对指定的信号量进行指定的操作(P、V操作)
一、sops参数指向类型为sembuf的一个数组
struct sembuf { ushort sem_num; /* 在数组中信号量的索引值 */ short sem_op; /* 信号量操作值(正数、负数或0) */ short sem_flg; /* 操作标志,为IPC_NOWAIT或SEM_UNDO*/ };
注:1、sem_op为负数,那么就从信号量的值中减去sem_op的绝对值,这意味着进程要获取资源,如果sem_op是正数,把它的值加到信号量,这意味着把资源归还给应用程序的集合。
命令(cmd) | 解 释 |
IPC_STAT | 从信号量集合上检索semid_ds结构,并存到semun联合体参数的成员buf的地址中 |
IPC_SET | 设置一个信号量集合的semid_ds结构中ipc_perm域的值,并从semun的buf中取出值 |
IPC_RMID | 从内核中删除信号量集合 |
GETALL | 从信号量集合中获得所有信号量的值,并把其整数值存到semun联合体成员的一个指针数组中 |
GETNCNT | 返回当前等待资源的进程个数 |
GETPID | 返回最后一个执行系统调用semop()进程的PID |
GETVAL | 返回信号量集合内单个信号量的值 |
GETZCNT | 返回当前等待100%资源利用的进程个数 |
SETALL | 与GETALL正好相反 |
SETVAL | 用联合体中val成员的值设置信号量集合中单个信号量的值 |