c语言初始化字符串 函数 manment,[转载]3.09进程(C语言班最后一天的课程)
1,進(jìn)程:是容器,是內(nèi)存上的概念。線(xiàn)程是CPU的概念。
2,fork的作用是根據(jù)一個(gè)現(xiàn)有的進(jìn)程復(fù)制出一個(gè)新進(jìn)程,原來(lái)的進(jìn)程稱(chēng)為父進(jìn)程(Parents Process),新進(jìn)程稱(chēng)為子進(jìn)程(Child Process)。
系統(tǒng)中同時(shí)運(yùn)行著許多進(jìn)程,這些進(jìn)程都是從最初只有一個(gè)進(jìn)程開(kāi)始一個(gè)一個(gè)復(fù)制出來(lái)的。
分進(jìn)程函數(shù)fork(),用來(lái)開(kāi)辟子進(jìn)程。執(zhí)行fork()之后,會(huì)有兩個(gè)進(jìn)程,一個(gè)是原來(lái)的那個(gè),一個(gè)是新生成的子進(jìn)程。這兩個(gè)進(jìn)程都會(huì)執(zhí)行程序下面的代碼。pid = fork();pid得到的是返回子進(jìn)程的id,不是自己的id。
1.c
#include
#include
#include
#include
int main(void)
{
pid_t pid = fork();
if(pid < 0){
perror("fork");
exit(0);
}
if(pid > 0){
printf("Parents!n");
}
if(0 == pid){
printf("Child!n");
}
printf("Hello world!n");
return 0;
}
3,先讓父進(jìn)程睡1秒,會(huì)先執(zhí)行子進(jìn)程。
2.c
#include
#include
#include
#include
int main(void)
{
pid_t pid = fork();
if(pid < 0){
perror("fork");
exit(0);
}
if(0 == pid){
printf("Child!n");
}else{
sleep(1);
printf("Parents!n");
}
return 0;
}
如果父進(jìn)程結(jié)束后,sleep(1);子進(jìn)程會(huì)打印到終端。
3.c
#include
#include
#include
#include
int main(void)
{
pid_t pid = fork();
if(pid < 0){
perror("fork");
exit(0);
}
if(0 == pid){
sleep(1);
printf("Son!n");
}else{
printf("Father!n");
}
return 0;
}
4,進(jìn)程號(hào):pid 。getppid()是得到當(dāng)前進(jìn)程父進(jìn)程的pid;getpid()是得到當(dāng)前進(jìn)程的pid。
如果子進(jìn)程getppid()時(shí),父進(jìn)程已經(jīng)死掉,子進(jìn)程就會(huì)被托管,那getppid()的值就為1。
所以父進(jìn)程要sleep(1),這樣子進(jìn)程getppid()時(shí),得到的就是此進(jìn)程父進(jìn)程的pid。
父進(jìn)程getppid()時(shí),得到的是終端的pid。
4.c
#include
#include
#include
#include
int main(void)
{
pid_t pid = fork();
if(pid < 0){
perror("fork");
exit(0);
}
if(0 == pid){
printf("Child!ngetppid=%d getpid=%d pid=%dn", getppid(),
getpid(), pid);
}else{
printf("Parents!ngetppid=%d getpid=%d pid=%dn", getppid(),
getpid(), pid);
sleep(1);
}
return 0;
}
5,ps查看進(jìn)程;
ps -jas 查看所有的進(jìn)程。
6,探討怎么讓父進(jìn)程知道子進(jìn)程結(jié)束?
exit(0)不會(huì)結(jié)束整個(gè)程序,只會(huì)結(jié)束一個(gè)進(jìn)程。wait(NULL)等待自己子進(jìn)程的結(jié)束。
父進(jìn)程執(zhí)行到wait()時(shí),會(huì)進(jìn)入休眠,子進(jìn)程結(jié)束時(shí),會(huì)發(fā)一個(gè)信號(hào),wait()會(huì)捕捉到這個(gè)信號(hào),以此來(lái)喚醒父進(jìn)程。
5.c
#include
#include
#include
#include
int main(void)
{
pid_t pid = fork();
if(pid < 0){
perror("fork");
exit(0);
}
if(0 == pid){
sleep(3);
printf("Child!n");
exit(0);
}
wait(NULL);
printf("Parents!n");
return 0;
}
7,探討父進(jìn)程與子進(jìn)程關(guān)于變量使用的問(wèn)題?
在fork()之后,會(huì)把原來(lái)的內(nèi)存完全復(fù)制一份,但不管怎么樣更改,都只會(huì)各改各的,互不干擾。
6.c
#include
#include
#include
#include
int main(void)
{
int a = 5;
int * p = &a;
pid_t pid = fork();
if(pid < 0){
perror("fork");
exit(0);
}
if(pid > 0){
*p = 6;
printf("Parents:%dn", *p);
}else{
printf("Child:%dn", *p);
}
return 0;
}
8,怎么樣殺死進(jìn)程?
下面的程序是個(gè)死循環(huán),只能重新開(kāi)一個(gè)終端,執(zhí)行“kill -9 進(jìn)程號(hào)”。
如果執(zhí)行以下命令,一定要?jiǎng)h除交換文件:
$vi 5.c //進(jìn)入5.c 此進(jìn)程進(jìn)程號(hào)為3456
$Ctrl + z
//暫停當(dāng)前程序運(yùn)行
$kill -9 3456 //殺死vi 5.c,這個(gè)進(jìn)程
$fg
//回到剛暫停的程序中,會(huì)報(bào)錯(cuò),因?yàn)槟莻€(gè)進(jìn)程已經(jīng)被殺死了。要?jiǎng)h除那個(gè)交換文件才能執(zhí)行這個(gè)操作。
$ls -a//查看所有文件,能把交換文件查看出來(lái)。
$rm -fr ..5.c.swap. //刪除交換文件。
$fg
7.c
#include
#include
#include
#include
int main(void)
{
pid_t pid = fork();
if(pid < 0){
perror("fork");
exit(0);
}
if(0 == pid){
while(1){
printf("Child!n");
}
}else{
printf("Parents!n");
}
return 0;
}
9,exec族:用來(lái)代替原來(lái)進(jìn)程,執(zhí)行的時(shí)候完全代替,不會(huì)再回來(lái)。
#include
所需頭文件
(1)execl("/bin/ls", "ls", "-l",
NULL); 后面一定要加NULL,表示參數(shù)傳完了。
int
execl(const char *path, const char *arg0, ... )
8.c
#include
#include
#include
#include
int main(void)
{
int ret = execl("/bin/ls", "ls", "-l", NULL);
if(ret < 0){
perror("execl!n");
exit(0);
}
printf("Hello world!n");
return 0;
}
(2)
execlp已經(jīng)安裝過(guò)的程序,不需要寫(xiě)路徑。
int
execlp(const char *file, const char *arg0, ... );
9.c
#include
#include
#include
#include
int main(void)
{
int ret = execlp("ls", "ls", "-l", NULL);
if(ret < 0){
perror("execl!n");
exit(0);
}
printf("Hello world!n");
return 0;
}
(3)int execv(const char *path, char
*const argv[]);
int
execvp(const char *file, char *const argv[]);
10.c
#include
#include
#include
#include
int main(void)
{
char * buf[5] = {};
buf[0] = "ls";
buf[1] = "-l";
buf[2] = NULL;
int ret = execv("/bin/ls", buf);
// int ret =
execvp("ls", buf);
if(ret < 0){
perror("execv");
exit(0);
}
return 0;
}
10,whereis ls 查看ls的路徑。
11,-rwx-rwx-rwx:普通文件
drwx-rwx-rwx:d代表是一個(gè)文件夾
lrwx-rwx-rwx:l代表是一個(gè)快捷方式
12,cd是shell的內(nèi)置命令。如果輸入等于cd,則用chdir(),它并沒(méi)有fork()子進(jìn)程,是父進(jìn)程在執(zhí)行chdir()。
11.c
#include
#include
#include
int main(void)
{
chdir("dic");
//這個(gè)目錄要事先創(chuàng)建好。
mkdir("./ttt");
return 0;
}
如果用execlp(),會(huì)達(dá)不到想要的結(jié)果。
12.c
#include
#include
#include
#include
int main(void)
{
int ret = execlp("cd", "cd", "/", NULL);
if(ret < 0){
perror("execlp");
exit(0);
}
return 0;
}
13,一定要記住(固定格式):切割字符串函數(shù)strtok()
13.c
#include
#include
#include
#include
#define BUF 512
#define BUFF 10
int main(void)
{
char buf[BUF] = {};
ssize_t ret = read(0, buf, BUF);
int i = 0;
char * buff[BUFF] = {};
char * p = buf;
while(1){
p = strtok(p, " n");
if(!p)
break;
buff[i++] = p;
p = NULL;
}
for(i = 0; i < BUFF; i++){
if(NULL == buff[i])
break;
printf("%sn", buff[i]);
}
execvp(buff[0], buff);
return 0;
}
14,編寫(xiě)一個(gè)自己的終端
14.c
#include
#include
#include
#include
#define BUF 512
#define BUFF 10
int main(void)
{
char buf[BUF];
char * buff[BUFF];
char * p = buf;
int i = 0;
pid_t pid;
int ret = 0;
while(1){
write(1, "myshell$", 8);
memset(buf, 0,
BUF);
read(0, buf, BUF);
while(1){
p = strtok(p, "n ");
if(!p){
break;
}
buff[i++] = p;
p = NULL;
}
buff[i] = NULL;
if(strcmp(buff[0], "cd") == 0){
chdir(buff[1]);
continue;
}
pid = fork();
if(pid < 0){
perror("fork");
exit(0);
}else if(pid == 0){
ret = execvp(buff[0], buff);
if(ret < 0){
perror("execvp");
exit(0);
}
}else{
wait(NULL);
}
}
return 0;
}
15,仔細(xì)探討fork()的用法。
15.c
#include
#include
#include
#include
int main(void)
{
pid_t pid;
char * message;
int n = 0;
pid = fork();
if(pid < 0){
perror("fork");
exit(0);
}
if(0 == pid){
message = "This is the childn";
n = 6;
}else{
message = "This is the parentn";
n = 3;
}
for(;n > 0; n--){
printf("%s", message);
sleep(1);
}
return 0;
}
程序運(yùn)行順序如下:
(1)父進(jìn)程初始化。
(2)父進(jìn)程調(diào)用fork,這是一個(gè)系統(tǒng)調(diào)用,因此進(jìn)入內(nèi)核。
(3)內(nèi)核根據(jù)父進(jìn)程復(fù)制出一個(gè)子進(jìn)程,父進(jìn)程和子進(jìn)程的PCB信息相同,用戶(hù)態(tài)代碼和數(shù)據(jù)也相同。因此,子進(jìn)程現(xiàn)在的狀態(tài)和父進(jìn)程一樣,做完了初始化,剛調(diào)用了fork進(jìn)入內(nèi)核,還沒(méi)有從內(nèi)核返回。
(4)現(xiàn)在有兩個(gè)一模一樣的進(jìn)程看起來(lái)都調(diào)用了fork進(jìn)入內(nèi)核等待從內(nèi)核返回(實(shí)際上fork只調(diào)用了一次),系統(tǒng)中還有很多別的進(jìn)程也等待從內(nèi)核返回。是父進(jìn)程先返回還是子進(jìn)程先返回,還是這兩個(gè)進(jìn)程都等待,先去調(diào)度執(zhí)行別的進(jìn)程,這都不一定,取決于內(nèi)核的調(diào)度算法。
(5)如果某個(gè)時(shí)刻父進(jìn)程被調(diào)度執(zhí)行了,從內(nèi)核返回后就從fork函數(shù)返回,保存在變量pid中的返回值是子進(jìn)程的id,是一個(gè)大于0的整數(shù),因此執(zhí)行下面else分支,然后執(zhí)行for循環(huán),打印"This is parentn"三次之后終止。
(6)如果某個(gè)時(shí)刻子進(jìn)程被調(diào)度執(zhí)行了,從內(nèi)核返回后就從fork函數(shù)返回,保存在變量pid中的返回值是0,因此執(zhí)行下面的if(0 == pid)分支,然后執(zhí)行for循環(huán),打印"This is childn"六次這樣之后終止。fork調(diào)用把父進(jìn)程的數(shù)據(jù)復(fù)制一份給子進(jìn)程,但此后二者互不影響,在這個(gè)例子中,fork調(diào)用之后父進(jìn)程和子進(jìn)程的變量message和n被賦予不同的值,互不影響。
(7)父進(jìn)程每打印一條信息就睡眠1秒,這時(shí)內(nèi)核調(diào)度別的進(jìn)程執(zhí)行,在這1秒這么長(zhǎng)的間隙里(對(duì)于計(jì)算機(jī)來(lái)說(shuō)1秒很長(zhǎng)了)子進(jìn)程很有可能被調(diào)度到。同樣地,子進(jìn)程每打印一條信息就睡眠1秒,在這1秒期間父進(jìn)程也很有可能被調(diào)度到。所以程序運(yùn)行地結(jié)果基本上是父子進(jìn)程交替打印,但這也不一定的,取決于系統(tǒng)中其他進(jìn)程的運(yùn)行情況和內(nèi)核的調(diào)動(dòng)算法,如果系統(tǒng)中其他進(jìn)程非常繁忙則有可能觀(guān)察到不同的結(jié)果。
(8)這個(gè)程序是在Shell下運(yùn)行的,因此Shell進(jìn)程是父進(jìn)程的父進(jìn)程。父進(jìn)程運(yùn)行時(shí)Shell進(jìn)程處于等待狀態(tài),當(dāng)父進(jìn)程終止時(shí)Shell進(jìn)程認(rèn)為命令執(zhí)行結(jié)束了,于是打印Shell提示符,而事實(shí)上子進(jìn)程這時(shí)還沒(méi)結(jié)束,所以子進(jìn)程的消息打印到了Shell提示符后面。最后光標(biāo)停在This is the child的下一行,這時(shí)用戶(hù)仍然可以敲命令,即使命令不是緊跟在提示符后面,Shell也能正確讀取。
fork函數(shù):
(1)fork函數(shù)的特點(diǎn)"調(diào)用一次,返回兩次",在父進(jìn)程中調(diào)用一次,在父進(jìn)程和子進(jìn)程中各返回一次。一開(kāi)始是一個(gè)控制流程,調(diào)用fork之后發(fā)生了分叉,變成兩個(gè)控制流程,這也就是"fork"(分叉)這個(gè)名字的由來(lái)了。子進(jìn)程中fork的返回值是0,而父進(jìn)程中fork的返回值則是子進(jìn)程的id(從根本上說(shuō)fork是從內(nèi)核返回的,內(nèi)核自有辦法讓父進(jìn)程和子進(jìn)程返回不同的值),這樣當(dāng)fork函數(shù)返回后,程序員可以根據(jù)返回值的不同讓父進(jìn)程和子進(jìn)程執(zhí)行不同的代碼。
(2)fork的返回值這樣規(guī)定也是有道理的。fork在子進(jìn)程中返回0,子進(jìn)程仍可以調(diào)用getpid函數(shù)得到自己的進(jìn)程id,也可以調(diào)用getppid函數(shù)得到父進(jìn)程的id。在父進(jìn)程中用getpid可以得到自己的進(jìn)程id,然而要想得到子進(jìn)程的id,只有將fork的返回值記錄下來(lái),別無(wú)他法。
(3)fork的另一個(gè)特性是所有由父進(jìn)程打開(kāi)的描述符都被復(fù)制到子進(jìn)程中。父、子進(jìn)程中相同編號(hào)的文件描述符在內(nèi)核中指向同一個(gè)file結(jié)構(gòu)體,也就是說(shuō)file結(jié)構(gòu)體的引用計(jì)數(shù)要增加。
16,exec函數(shù)
用fork創(chuàng)建子進(jìn)程后執(zhí)行的是和父進(jìn)程相同的程序(但有可能執(zhí)行不同的代碼分支),子進(jìn)程往往要調(diào)用一種exec函數(shù)以執(zhí)行另一個(gè)程序。當(dāng)進(jìn)程調(diào)用一種exec函數(shù)時(shí),該進(jìn)程的用戶(hù)空間代碼和數(shù)據(jù)完全被新程序替換,從新程序的啟動(dòng)例程開(kāi)始執(zhí)行。調(diào)用exec并不創(chuàng)建新進(jìn)程,所以調(diào)用exec前后該進(jìn)程的id并未改變。
exec族
man
3 exec
頭文件:#include
int execl(const char *path, const char *arg0, ... );
int execlp(const char *file, const char *arg0, ... );
int execle(const char *path, const char *arg0, ...);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execvP(const char *file, const char *search_path, char *const
argv[]);
man 2 execve
int execve(const char *path, char *const argv[], char *const
envp[]);
這些函數(shù)如果調(diào)用成功則加載新的程序從啟動(dòng)代碼開(kāi)始執(zhí)行,不再返回,如果調(diào)用出錯(cuò)則返回-1,所以exec函數(shù)只有出錯(cuò)的返回值而沒(méi)有成功的返回值。
不帶p(表示path)的exec函數(shù)第一個(gè)參數(shù)必須是程序的相對(duì)路徑或絕對(duì)路徑,例如"/bin/ls"或"./a.out",而不能是"ls"或"a.out"。對(duì)于帶字母p的函數(shù):如果參數(shù)中包含/,則將其視為路徑名。否則視為不帶路徑的程序名,在PATH環(huán)境變量的目錄列表中搜索這個(gè)程序。
帶由字母l(表示list)的exec函數(shù)要求將新程序的每個(gè)命令行參數(shù)都作為一個(gè)參數(shù)傳給它,命令行參數(shù)的個(gè)數(shù)是可變的,因此函數(shù)原型中有…,…中的最有一個(gè)可變參數(shù)應(yīng)該是NULL,起sentinel的作用。
對(duì)于帶有字母v(表示vector)的函數(shù),則應(yīng)該先構(gòu)造一個(gè)指向各參數(shù)的指針數(shù)組,然后將該數(shù)組的首地址當(dāng)作參數(shù)傳給它,數(shù)組中的最后一個(gè)指針也應(yīng)該是NULL,就像main函數(shù)的argv參數(shù)或者環(huán)境變量一樣。
對(duì)于以e(envirnoment)結(jié)尾的exec函數(shù),可以把一份新的環(huán)境變量傳給它,其他exec函數(shù)仍使用當(dāng)前的環(huán)境變量表執(zhí)行新程序。
事實(shí)上,只有execve是真正的系統(tǒng)調(diào)用,其他五個(gè)函數(shù)最終都調(diào)用execve,所以execve在man手冊(cè)第2節(jié),其他函數(shù)在man手冊(cè)第3節(jié)。
總結(jié)
以上是生活随笔為你收集整理的c语言初始化字符串 函数 manment,[转载]3.09进程(C语言班最后一天的课程)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 大逆转裁判安卓汉化(大逆转裁判 安卓)
- 下一篇: C语言课程设计报告输出杨辉三角,C语言学