Linx系统编程(进程)

进程

进程:启动的程序

程序:二进制文件,占用的内存资源

并发:指的是多个事情在同一时间段同时发生了(多个任务之间互相抢占资源)

并行:指的是多个事情在同一时间点同时发生了(多个任务之间不互相抢占资源)

进程id:系统中每个进程都有唯一的id,在C语言中用pid_t类型表示,其实就是一个非负整数

进程状态:就绪、运行、挂起、停止

1.fork函数

函数原型:pid_t fork(void)

返回值:  =0  父进程

                   >0  子进程

                   -1   error

父子进程的执行顺序不一定(抢占资源)

父子进程的创建

 父进程的结束然后才是子进程的开始

2.ps和kill指令

ps:查看进程信息

a:显示现行终端下的所有程序

u:以用户为主的格式显示程序状况

x:显示所有程序 

kill:向指定进程发送信息

ps aux|grep a.out          4482

kill -9 4482

grep+可执行程序:查找相关程序

各个进程的地址空间中的数据是完全独立的

对于同一个变量读时共享,写时分别在物理地址上拷贝一份变量单独读写

父子进程间不可以通过全局变量进行通信(原因:两个进程内存不能共享) 

i = 400

i = 200

3.exec函数

(1)execl函数:加载一个进程, 通过 路径+程序名 来加载。

 int execl(const char *path, const char *arg, …);

 执行指定路径(不常用)

(2)execlp函数: 加载一个进程,借助PATH环境变量.

int execlp(const char *file, const char *arg1, …);

        file:要加载的程序的名字
   arg1:命令的第一个单词

#include <stdio.h>
#include <unistd.h>
int main(void)
{
    pid_t pid = fork();//创建子进程
    if (pid > 0) 
    {
        //执行ls -l 命令
        execlp("ls", "ls", "-al",NULL);
    } 
    else if (pid == 0) 
    {
        printf("My parent process id = %d\n", getpid());
    }
    return 0;
}

4.孤儿进程和僵尸进程

僵尸进程:一个进程使用 fork 创建子进程,如果子进程退出,而父进程并没有调用 wait 或 waitpid 获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中,这种进程称之为僵死进程。(Ctrl+c杀不死)

孤儿进程:一个父进程退出,而它的一个或多个子进程还在运行,那么这些子进程将成为孤儿进程。孤儿进程将被 init 进程(进程号为1)所收养,并由 init 进程对它们完成状态收集工作。

 孤儿进程被init进程所收养

只有杀死父进程才会重新回收子进程,直接杀僵尸进程是杀不死的 

5.进程回收

(1)wait函数:

                          · 阻塞并等待子进程退出;

                          · 回收子进程残留资源

                          · 获取子进程结束状态

 函数原型:pid_t wait (int* status)

1.WIFEXITED(status):为非零,进程正常结束;

WEXISTATUS(status)如上宏为真,使用此宏获取进程退出状态的参数

2.WIFSIGNALED(status):为非零,进程异常退出

WTERMSIG(status)如上宏为真,使用此宏获取进程退出状态的参数

(2)waitpid函数:同wait

pid_t waitpid(pid_t pid,int *status,int options);

 

 6.进程退出

三个终止函数:

void exit(int status);

void _Exit(int status);

void _exit(int status);

exit()函数_exit()函数都是用来终止进程的,exit()是一个标准 C 库函数,而_exit()和_Exit()是系统调用。_exit()和_Exit()两者等价

 是用break来结束子进程会导致子进程的结束不会告诉父进程,导致一些值混乱,最好是用exit

7.进程间通信

进程1把数据从用户空间拷到内核缓冲区,进程2再从内核缓冲区把数据读走

IPC

管道:调用pipe系统函数即可创建。

① 数据自己读不能自己写。

② 数据一旦被读走,便不在管道中存在,不可反复读取。

③ 由于管道采用半双工通信方式。因此,数据只能在一个方向上流动。

④ 只能在有公共祖先的进程间使用管道。

查看管道缓冲区大小:

·  ulimit -a命令

·  fpathconf函数(管道fd[0],int name)

·  pathconf()查看man fpathconf

int pipe(int fd[2])

fd-传出参数:
            fd[0]:读
            fd[1]:写
返回值:
        0:成功
        1:失败

管道的建立 

实现父子间进程通信: 父进程关闭管道读端,子进程关闭管道写端。父进程可以向管道中写入数据,子进程将管道中的数据读出。由于管道是利用环形队列实现的,数据从写端流入管道,从读端流出,这样就实现了进程间通信。

有名管道的进程间通信

int mkfifo(const char *pathname,  mode_t mode);

 

 消息队列

是消息的链表,存放内核,一个消息队列有一个标识符来标识

不移动按顺序来读,也可按消息的类型读取。

ipcs -q :查看当前的消息队列

消息队列的发送

 

 接收

 内容被读走了所以used-byte = 0;

消息队列多个进程间通信 

写:

读:

消息队列进程进行全双工通信

 服务端

客户端

8.共享内存

ipcs -m : 查看共享内存

ipcrm -m : 删除

(1)创建共享内存:int shmget (key_t key,size_t size,int shmflg)

· shmget会根据key值创建一个共享内存,当创建多个共享内存时,每一个key值唯一

· 该参数用于确定共享内存大小。

· shmflg  :  IPC_CREAT | 权限          IPC_CREAT | IPC_EXCL | 权限

返回值;shmid

共享内存的创建

(2)连接 void *shmat(int shmid,const void *shmidaddr,int shmflg)

shmaddr用于确定将共享内存挂在进程虚拟地址哪个位置,一般填NULL代表让内核自己确定位置。

shmflg用于确定挂接方式,一般填0

(3)分离  int shmdt(const void *shmidaddr)

(4)   销毁  shmctl (int shmid,int cmd,struct shmid_ds *buf)

 struct shmid_ds *buf一般填NULL

(5)  key_t ftok(“路径”,int project_id)

ftok根据路径名,提取文件信息,再根据这些文件信息及project ID合成key

pathname 是目录还是必须存在的文件的具体路径。

proj-id取值在0~255(unix)


shmat把共享内存映射到当前进程的地址空间


管道读取内存只能读取一次,消息队列、共享内存读取多次后依然存在

//将进程里的地址映射删除

ipcrm -m 32792:删除共享内存

 

共享内存进程间通信

write

read

read进程shmid不需用  IPC_CREAT|0777 来创建

 


 9.信号

信号通信:只能是内核向用户空间进程发送信息。

kill -l :可以查看已存在的信号

(1)信号的处理

unsigned int alarm(unsigned int seconds);

·   函数说明:alarm()用来设置信号SIGALRM 在经过参数seconds 指定的秒数后传送给目前的进程. 如果参数seconds 为0, 则之前设置的闹钟会被取消, 并将剩下的时间返回.

·   返回值:返回之前闹钟的剩余秒数, 如果之前未设闹钟则返回0.

void (*signal(int sig, void (*func)(int)))  (int)
第一个参数sig:指定信号;

第二个参数是函数指针,是我们自己写的处理函数,这个函数的返回值是函数指针。

返回值:成功,返回之前的信号处理方式

                失败,-1.

自定义的信号处理函数指针

 

 (2)信号父子间进程通信

 

 

 (3)信号灯(信号量的集合)

得到一个信号量集标识符或创建一个信号量集对象:semget函数

int semget(key_t key, int nsems, int semflg)

key : 0(IPC_PRIVATE):会建立新信号量集对象

nsems :创建信号量集中信号量的个数,该参数只在创建信号量集时有效

int semctl(int semid, int semnum, int cmd, union semun arg)

semnum:信号灯编号

cmd: 如下

GETVAL

返回信号量集合内单个信号量的值

SETVAL

设置信号量集合中单个信号量的值

IPC_RMID

从内核中删除信号量集合

 

 (4)信号灯PV操作

semop(完成对信号量的P操作或V操作)

int semop(int semid, struct sembuf *sops, unsigned nsops)

semid:信号量集标识符

sops是一个指向结构体数组的指针

struct senbuf
{
    unsigned short sem_num;//信号灯编号
    short sem_op ;//对该信号的操作,P操作:1,V操作:-1
    short sem_flg;//0 阻塞;1 非阻塞
}

nsops:进行操作信号量的个数,即sops结构变量的个数,需大于或等于1。最常见设置此值等于1,只完成对一个信号量的操作

PV操作:

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值