linux下进程编程综述

发布时间:2017-1-23 16:29:17 编辑:www.fx114.net 分享查询网我要评论
本篇文章主要介绍了"linux下进程编程综述",主要涉及到linux下进程编程综述方面的内容,对于linux下进程编程综述感兴趣的同学可以参考一下。

进程的基本概念 进程的概念:进程是一个具有独立功能的程序的一次运行活动(系统会分配其新的一定的堆栈段、代码段、数据段)。 进程的特点:动态性、并发性、独立性、异步性。 进程的三种状态:就绪、执行、阻塞。 就绪:是等待被运行的状态;执行:是正在运行的状态;阻塞:是等待I/O等资源空闲的状态。 进程ID(PID):标识进程的唯一数字;父进程的ID(PPID);启动进程用户的ID(UID)。 进程和父进程的关系:在A进程中创建出新的进程B,此时称A进程为B的父进程,B进程为A的子进程。 进程创建运行相关函数例举 获取PID:pid_t getpid(void); 获取PPID:pid_t getppid(void); 创建子进程:pid_t fork(void);//如果此函数被调用则在当前进程中创建一个子进程,也就是接下来是两个进程都要分别运行一下接下来的代码段,所以fork()函数的返回值在父进程和子进程中各返回一次,如果当前返回值为0则表示当前运行在子进程中,如果返回值大于0则表示当前在父进程中运行(实际上pid_t 大于0时此值即为父进程的PID),如果返回值小于0则代表创建进程失败。 创建子进程:pid_t vfork(void);//功能和fork函数差不多。它们区别在于fork函数创建的子进程是和父进程是共享数据段的,而且创建子进程之后父进程和子进程运行的先后顺序是随机的;vfork函数创建的子进程是和父进程是不共享数据段的(子进程重新拷贝父进程的数据段),而且创建子进程之后必然是子进程先运行。它们的共同点在于都是共享代码段,都是不共享堆栈段。 替换原进程:exec函数族;//此函数被调用时效果为将exec函数运行完之后当前进程就结束,即将原进程的数据段、代码段、堆栈段都替换就只有PID不变,测试得到可以加判断使得exec不运行,如果exec运行则在该进程中不会运行位于exec函数所在行之前的程序(除了sleep()之类)以及不管之后的程序是什么都不会运行。 exec函数族(此处仅取一个示例): int execv (const char * path, char * const argv[ ]); //path:被执行程序名(含完整路径)。argv[]: 被执行程序所需的命令行参数数组。 例:char * argv[]={“ls”,”-al”,”/etc/passwd”,(char*)0};execv(“/bin/ls”,argv); 例:char *argv[]={(char*)0};execv("/home/zbf/guoqianlib/code1/2-2-2/a.out",argv); 以上函数,其中调用函数需要包含头文件<unistd.h>,而需要新建pid_t型(实际上就是整形)的变量则需要包含 <sys/types.h>(这个头文件名字的意思是在sys文件下的type.h)。 系统命令:int system(const char *command);//#include <stdlib.h>,就是运行系统SHELL的命令,如system(“ls -al /etc/passwd”)。 进程等待:pid_t wait (int * status)//wait(NULL);阻塞该进程,直到其某个子进程退出。 进程之间通信的方式 1、管道(pipe)和命名管道(FIFO) 2、共享内存 3、信号通信 4、消息队列 5、信号量 6、SOCKET套接字 1、管道 无名管道创建函数:int pipe(int filedis[2]);//只能用于父进程与子进程之间的通信,其中filedis[0] 用于读管道,filedis[1] 用于写管道。 命名管道创建函数:int mkfifo(const char * pathname, mode_t mode);//其中pathname为文件地址比如"/tmp/myfifo",mode为模式O_CREAT|O_EXCL|O_RDWR 不管是无名还是命名管道在创建之后都是open、read、write等文件操作,只不过无名管道不需要open,只要filedis[0]就可以read,而命名管道则需要首先open文件地址如函数中的pathname。此处需要注意的是O_CREAT|O_EXCL标识符是创建文件如果当前有重命名的则就报错。使用open时标识符加上O_NONBLOCK就是防止阻塞而直接出错返回。 管道的一个特点就是管道内容被读取过一次之后就会被清空。命名管道可以使得两个不相关的进程也可以通信。 2、共享内存 共享内存是多进程的通信的一种效率最高的方式。 共享内存的创建:int shmget ( key_t key, int size, int shmflg);//此处注意第一个参数为key_t型,其本质上应该由key_t ftok(const char *pathname, int proj_id)产出,但是我们实际上只是需要开辟一块内存而已,所以投机输入一个随机的key_t键值(如1234,而系统正好没用过)也是可以产生一块共享内存的。 关于shmget函数man摘录: A new shared memory segment, with size equal to the value of size rounded up to a multiple of PAGE_SIZE, is created if key has the value IPC_PRIVATE or key isn’t IPC_PRIVATE, no shared memory segment corresponding to key exists, and IPC_CREAT is specified in shmflg. 以上这段话应该如此了解,不管key是不是你私有产出的(使用ftok得到),只要系统没用到这个键值而且在shmflg里加上IPC_CREAT(实际上我试过没加也可以)就可以得到一个共享内存的标识符。另外的确有IPC_PRIVATE这个定义,打印出来IPC_PRIVATE=0。man中另外也提到shmflg中可以设置用户权限如0666。返回值:如果成功,返回共享内存标识符;如果失败,返回-1。 共享内存的映射:void *shmat (int shmid, const void *shmaddr, int shmflg);//shmid为创建函数返回的标识符,shmaddr为映射地址(如果为0则自动分配地址),shmflg为映射方式,通常为0。返回值:如果成功,则返回共享内存映射到进程中的地址;如果失败,则返回-1。 映射成功之后就直接调用返回的地址,对地址指向内容进行写入或者读出。 共享内存的脱离:int shmdt(const void *shmaddr);//输入映射的地址,如果失败则返回-1。 共享内存不像管道一样被读取之后就会自动清零,要手动清除,这也是多进程可以有效率地读出信息的原因。 3、信号通讯 信号接收函数定义:typedef void (*sighandler_t)(int); sighandler_t signal(int signum, sighandler_t handler);//需要定义一个sighandler_t型指向的类型的函数用于在接收到为signum信号的时候进行数据处理。handler也可以直接是SIG_IGN:忽略此信号或者SIG_DFL: 按系统默认方式处理。 用于测试以上函数的函数以及系统指令: int pause(void);//pause函数使调用进程挂起直至捕捉到一个信号。 unsigned int alarm(unsigned int seconds);//在指定秒之后产生一个SIGALRM信号 系统指令:kill  用于发送信号的某进程;ps aux  查看当前所有进程及进程号。 4、消息队列 消息队列就是一个消息的链表。可以把消息看作一个记录,具有特定的格式。进程可以向中按照一定的规则添加新消息;另一些进程则可以从消息队列中读走消息。目前主要有两种类型的消息队列:POSIX消息队列以及系统V消息队列,系统V消息队列目前被大量使用。系统V消息队列是随内核持续的,只有在内核重起或者人工删除时,该消息队列才会被删除。 消息队列的创建: 消息队列也和共享内存一样需要一个键值,由此函数产生:key_t ftok(const char *pathname, int proj_id); 消息队列创建函数:int msgget(key_t key, int msgflg);//例如msgget((key_t )1234,IPC_CREAT));返回描述字,具体flag见man。 消息发送函数:int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);//例如msgsnd(msgid,(void *)&some_data,MAX_TEXT,0)//其中const void *msgp是指向任意数据格式的指针,典型格式是第一个单元是long int的变量的结构体,而其第二个单元是一个存储变量的数组,其第一个长整形的变量实际上就是“优先级”,发送函数和接收函数的结构体格式要一致,其他参数体会一下。 消息读取函数:ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);//例如msgrcv(msgid,(void *)&some_data,BUFSIZ,msg_to_receive,0)//其中 long msgtyp是接收数据的优先级,只有当发送数据的优先级(以上有说优先级存哪)大于等于该值的时候才会被接收,另外需要注意的是用于定义接收大小的size_t msgsz只是接收到的数组的大小并不包括结构体中的第一个长整型(这样的设定应该是系统自行将第一个变量处理了)。 测试得到,当优先级不高的数据未被接收时,数据还是会缓存,直到被接收,不过并不影响下一个数据的发送。 消息队列的特点:消息再被读取之后会被自动删除。 5、信号量 信号量(又名:信号灯)与其他进程间通信方式不大相同,主要用途是保护临界资源。进程可以根据它判定是否能够访问某些共享资源。除了用于访问控制外,还可用于进程同步。 信号量创建函数:int semget(key_t key, int nsems, int semflg);//nsems:指定打开或者新创建的信号灯集中将包含信号灯的数目;semflg:标识,同消息队列 信号量操作函数:int semop(int semid, struct sembuf *sops, unsigned nsops);//semid:信号量集的ID;sops:是一个操作数组,表明要进行什么操作;nsops:sops所指向的数组的元素个数。 总结:   暂且撇开信号通讯和信号量不谈,此处就管道、共享内存、消息队列这三种方式综上进行比较。其中管道比较初级,但也适用于不同进程之间的通信(可能在父子进程方面有优势),而且需要依靠LINUX下的文件操作函数;共享内存由于涉及到内存方面则不需要相关的文件操作函数,且可以根据内存中数据对该内存的内容进行初步判断,并可以多进程读入效率较高,适用于1对N的通讯,由于有读出之后不会清除的特点则特别需要注意进程之间的相互对应(- -?);消息队列相较管道来说高级,其有优先级有缓存并且有读出之后会清除的特点,可以有效地进行1对不同的1通讯。

上一篇:一篇关于ios block内存管理的文章
下一篇:1090 - APL Lives!

相关文章

相关评论