进程及进程之间的通信

程序与进程

程序:是一种静态的概念,表示有序指令的集合.

进程:是一种动态的概念,表示程序运行的过程(运行过程包含:创建、调度、消亡)

进程状态

在这里插入图片描述

D 	不间断睡眠(通常为IO) 软件不可中断,但是硬件可以,硬件交互
I	属于不可中断等待态D的内核线程,但是该内核线程可能实际上没有任何负载,该线程就是空闲状态
R	运行或可运行(运行队列)
S	可中断睡眠(等待事件完成)软件可中断
X	死(不应该被看到)
Z 	(“僵尸”)进程,终止但未被其父进程收割

<	高优先级(对其他用户不友好)
N	低优先级(对其他用户来说很好)
L	将页面锁定在内存中(用于实时和定制IO)
s	是一个会议领导者
l	是多线程的(使用CLONE_THREAD,像NPTL的pthreads那样)
+	在前台进程组中

进程的系统调用

虚拟内存

  • 每一个进程都有自己的虚拟内存空间(32位系统 4g)
  • 虚拟内存与物理内存有映射关系,但是虚拟内存的大小和物理内存大小没有关系. 与操作系统以及CPU有关系
  • 与内核内存 、物理内存

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FBt84v2Y-1658999320376)(.\IO进程小笔记.assets\123-1656860936411.png)]

fork函数

pid_t fork(void): 进程创建函数 
{ 
	宏观角度: 
	该函数正确调用的话有两个返回值(区分父子进程): 一个>0的值 一个 == 0的值. 
	错误返回 -1 
	pid_t p = fork(); 
	if(p == -1): 错误发生,
	常见错误原因: 资源不足开辟不了进程. 
	else if(p == 0): 表示子进程(由于该子进程没有子进程,所以返回值为0)  	
        pid = getpid();//当前进程的pid
    	pid = getppid();//当前进程的父进程pid
	else if(p > 0): 表示父进程(其实这儿的p就是子进程ID号,便于父进程管理子进程) 
        pid = getpid();//当前进程的pid
    	pid = getppid();//当前进程的父进程pid
    	pid = p;//当前fork()函数分裂的子进程pid
}
  • fork()一旦被调用,则子进程会拷贝所有父进程的资源(正文段(fork之后的)、数据段、代码段、 堆、栈…)
pid_t vfork(void)函数一个已经被淘汰的函数
{
    与fork相比,这个创建的进程数据直接会直接交互、干扰;
    当子进程未被杀死时,父进程不会结束,会造成死锁的后果;
}

进程资源回收

进程退出函数:—exit() / _exit()

void exit(int status) 
{
	status: 结束状态. 
	EXIT_SUCCESS 或 0: 代表正常结束 
	EXIT_FAILURE 或 1 -1:代表错误结束 
	要处理缓存区内容(比如:把缓存剩下的内容写入文件). 
} 
void _exit(int status) 
{
	status: 结束状态.
    EXIT_SUCCESS 或 0: 代表正常结束 
    EXIT_FAILURE 或 1 -1:代表错误结束 
    销毁缓存区的所有东西,直接退出进程. 
}

资源回收函数:(子进程资源需要被父进程回收…) --wait

wait()waitpid() 
{ 
	等待子进程退出.并且回收子进程资源. 
pid_t wait(int *status) 
{
     status: 获取进程退出状态. 不需要获取的话 就为 NULL 
     pid_t: 返回子进程的进程号 
     为了避免僵尸进程的出现,通常会用wait来回收子进程的资源. 
}
pid_t waitpid(pid_t pid, int *status, int options) 
{
    pid: pid > 0 只等待进程ID等于pid的子进程,不管其它已经有多少子进程运行结束退出了,只要指定的子进程 					 还没有结束,waitpid就会一直等下去 
       	 pid == -1 等待任何一个子进程退出,没有任何限制,此时waitpid和wait的作用 一模一样 
         pid == 0 等待同一个进程组中的任何子进程,如果子进程已经加入了别的进程组, waitpid不会对它做任					何理睬 
        		ps axj 可以查看组进程. 
         pid < -1 等待一个指定进程组中的任何子进程,这个进程组的ID等于pid的绝对 值 
        status: 与wait一致 
        options:0 与wait函数一样,需要一直等待进程退出(阻塞). 
            	 为WNOHANG 不需要等待(不阻塞). 
            返回值: 正常返回子进程的进程号 使用选项WNOHANG且没有子进程结束时:0 错误: -1; 
}

exec函数族

  • exec函数族提供了一种在进程中启动另一个程序执行的方法。
int execl(const char *path, const char *arg, ...) 
int execv(const char *path, char *const argv[]); 
int execle(const char *path, const char *arg, ..., char *const envp[]) 
int execve(const char *path, char *const argv[], char *const envp[]) 
int execlp(const char *file, const char *arg, ...) 
int execvp(const char *file, char *const argv[]) 
   	l: 代表后面的参数的传入是 list 形式(依次传入). 
    v: 代表后面的参数的传入是以指针数组传入的. 
    例如: char *arv[] = {"ls", "-l", NULL}; 
	p: 第一个参数不需要指明路径,只需要传入可执行文件名就可以了. (在系统默认PATH路径下寻 找) 
    e: 最后一个参数可以传入环境变量. 
        
    除了第一个参数以外后面参数所以形式都需要以NULL结尾!! 
       	例如:char *arg[] = {"ls", "-l", NULL} 
			execvp("ls", arg);
  • 新的可执行程序把子进程原来的数据段、代码段和堆栈段,全部替换,除了进程号外

    守护进程

    pid = fork()
    	if(pid > 0)
    		exit(0);
    ====
    孤儿进程-->守护进程
        
    ====
    
    1. 让进程摆脱原会话的控制
    2. 让进程摆脱原进程组的控制
    3. 让进程摆脱原终端的控制

会话–setsid()函数

pid_t setsid(void)
{
	作用:创建一个新会话并担任该会话的组长
	返回值:-1失败
			成功返回进程组ID
}

改变当前目录–chdir()函数

int chdir(const char *path);
{
	path:改换地址的绝对路径
	返回值: 0成功
			-1失败
}

重设文件掩码–umask()函数

mode_t umask(mode_t mask);
{
	mask:掩码,当mask = 0700时,表示关闭该进程的该用户的读、写、执行的权限||通常设置为0
	
}

关闭文件描述符–close()

close(i)	//i = 0,1,2
原因:占用资源,可能导致所在文件无法卸载

守护进程创建完毕

进程间的通信

  • 传统通信方式:无名管道、有名管道、信号

  • IPC通信:共享内存、消息队列、信号灯

  • BSD通信: 套接字通信(网络编程篇)

传统通信方式

无名管道

1、是"半双工"工作方式 "半双工"同时只能从一端写入,另一端读取. 
2、无名管道不属于文件系统. 数据交互在"内核内存"完成. 
3、无名管道只能用于亲缘进程间的通信. 
    函数接口: pipe 
{
int pipe(int pipefd[2]); 
      用法: int fd[2]; 
        	pipe(fd); 
        fd[0] 表示读端 
        fd[1] 表示写端 
}
	ret = read()在读取管道时(与读取文件返回0有区别).  
        如果写端关闭 且当中没有内容可读则会返回0. ret == 0 
        如果写端没有关闭 且当中没有内容可读,read会阻塞.
        
        读端如果关闭,写端的存在是没有意义的,所以内核会发送一个管道破裂信号(SIGPIPE),
		该信号会 默认结束进程
        当写端关闭,如果管道中还有内容则读端会继续读取,直到读取完成返回0.

有名管道

1、是"半双工"工作方式 "半双工"同时只能从一端写入,另一端读取. 
2、有名管道属于文件系统. 数据交互在"内核内存"完成. 
3、有名管道可以用于非亲缘进程间的通信. 
    指令mkfifo 可以直接创建管道文件. 
接口函数: mkfifo() 
{ 
    创建管道文件. 
 int mkfifo(const char *pathname, mode_t mode);
    pathname : 管道名
    mode:权限
        
  创建好后直接用open打开,就可以向管道里读写数据了
        值得一提的是:open打开管道是,文件路径为绝对路径,打开方式要么只读要么只写,如果读写都打开的话,则该进程无法与其他连接该管道的进程通信

信号

常见信号
在这里插入图片描述

在这里插入图片描述

发送信号–kill/raise

int kill(pid_t pid, int sig)
{
    pid ; <0	发送信号给进程号为pid的进程
          = 0	发送信号给单曲进程组中的所有进程
          =-1	发送信号给所有信号表中的进程
          <-1	发送信号给-pid进程组中的所以进程
    sig:信号类型
        返回值:0成功
        		-1错误
}

int raise(int sig)
{
    这个是向进程自身发送信号
    sig:信号类型
        返回值:0成功
        		-1错误
}

信号设置–signal

typedef void (*sighander_t)(int)
int signal(int signum, sighander_t hander_t)
{
	signum : 指定信号
	hander:	SIG_IGN 忽略该信号
			SIG_DFL 默认处理
			自定义处理函数
	返回值:失败返回SIG_ERR

}

IPC通信

共享内存

创建–shmget

int shmget(key_t key ,int size, int shmflg)
{
    key:健值,同上可用ftok或者IPC_PRIVATE
    size:共享内存大小
    shmflg:权限位,同上可加IPC_CREAT:创建新的共享内存
                IPC_EXCL:和IPC_CREAT同用,表创建唯一共享内存
    返回值:-1失败
           成功返回标识符
}

映射–shmat

char *shmat(int shmid, const void * shmaddr, int shmflg)
{
    shmid:由shmget返回的标识符
    shmaddr:映射的指定地址,一般设为NULL,自动分配
    shmflg:0 内存可读可写
        	SHM_RDONLY:只读
     返回值:失败-1;
        成功:被映射地址    
}

解除映射–shmdt

int shmdt(const void * shmaddr)
{
	shmaddr:shmat的返回值
	返回值:0成功
			-1失败

}

控制共享内存—shmctl

int shmctl(int shmid, int cmd, struct shmid_ds *buf);
{
    我们这儿用来删除
    shmid:shmget返回值
    cmd:IPC_RMID
    buf:NULL
        返回值:-1失败
        		0成功
}

信号量

创建函数–semget()

int semget(key_t key,int nsems, int semflg);
{
    key:可以是IPC_PRIVATE,也可以用ftok()
    nsems:创建信号灯个数
    semflag:同open中一样听见权限位,
        		IPC_CREAT:创建新的信号量
                IPC_EXCL:和IPC_CREAT同用,表创建唯一信号量
	返回值:成功返回标识符
                    失败 -1
}

信号灯操作函数–semop()

int semop(int semid, struct sembuf * sem, size_t n)
{
    semid:信号灯集的标识符
        sem:结构体指针
        struct sembuf{
            short sem_num;	//信号灯的编号
            short sem_op;	//信号灯操作方式
            					0:等待
            					-1:P操作
                                1:V操作
                                (V+ ,,, P-short sem_flg;	//0:阻塞等待
     n:操作个数 1
      返回值:
         成功信号量标识符
         失败-1
        }
}

信号灯控制函数–semctl()

int semctl(int semid, int semnum, int cmd, ....)
{
    semid:信号灯集id
    semnum:要修改的信号灯的编号
     cmd:
        SETVAL:设置信号灯值
        GETVAL:获取信号灯值
        IPC_RMID :删除信号灯
    返回值:失败返回-1;
            值得注意的是该函数的第四个参数
            cmd = SETVAL||SETGAT时
            union semun{
               int              val;  //SETVAL的值  
               struct semid_ds *buf;  //IPC_SET,IPC_STAT的缓存区 
               unsigned short  *array;//SETALL,GETALL数组  
               struct seminfo  *__buf;//IPC_INOF缓存区  

        }!!!需要自己定义
        初始化
            semctl(semid, 0 ,SETVAL, 0);//给编号0赋初值0
    	删除
            semctl(semid, 0, IPC_RMID);//semnum随便取,直接删除的是id的信号灯集
    
}

消息队列

创建–msgget

int msgget(key_t key, int msgflg)
{
    key:同上
    msgflg:同open中一样听见权限位,
        		IPC_CREAT:创建新的信号量
                IPC_EXCL:和IPC_CREAT同用,表创建唯一信号量
        返回值:成功返回id、
        		失败返回-1
    
}

发送–msgsnd()

int msgsnd(int msqid, const void * msgp, size_t msgsz, int msgflg)
{
    msqid:msgget返回的id
    msgp:指向消息的结构体指针,需要减去 long mtype
        ex:struct msgbuf {
            long mtype;	//消息类型
            char buf[N];	//正文
            int data;		//正文
        }
    msgsz:正文大小 上述的sizeof(msgbuf)-sizeof(long)
    msgflg : 通常设置为0,阻塞状态
        	 设置为IPC_NOWAIT。非阻塞
        
        返回值:成功0
        		错误-1
}

接受–msgrcv

int msgrcv(int msqid, void * msgp, size_t msgsz, long int msgtyp,int msgflg)
{
    msqid:msgget返回的id
    msgp:同上
    msgtyp:0 :接收队列中的第一个消息
        	>0:接收队列中第一个msgtyp的消息
            <0:接收队列中第一个类型值不小于|msgtyp|且这个类型值是消息队列中相对最小的
    msgflg: 同上
        返回值:成功0
        		失败-1
}

控制–msgrcv

int msgctl(int msqid, int cmd, struct msqid_ds *buf);
{
    msqid:同上
    cmd:IPC_STAT 读取消息队列中的的数据结构msqid_ds存入buf
        IPC_SET设置消息队列中的数据msqid_ds,取用buf
      	IPC_RMID 删除消息队列
    buf:         struct msqid_ds {
               struct ipc_perm msg_perm;     /* Ownership and permissions */
               time_t          msg_stime;    /* Time of last msgsnd(2) */
               time_t          msg_rtime;    /* Time of last msgrcv(2) */
               time_t          msg_ctime;    /* Time of last change */
               unsigned long   __msg_cbytes; /* Current number of bytes in
                                                queue (nonstandard) */
               msgqnum_t       msg_qnum;     /* Current number of messages
                                                in queue */
               msglen_t        msg_qbytes;   /* Maximum number of bytes
                                                allowed in queue */
               pid_t           msg_lspid;    /* PID of last msgsnd(2) */
               pid_t           msg_lrpid;    /* PID of last msgrcv(2) */
           };
		cmd为IPC_RMID时 buf设置为NULL
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值