日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

c语言初始化字符串 函数 manment,[转载]3.09进程(C语言班最后一天的课程)

發(fā)布時間:2023/12/2 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 c语言初始化字符串 函数 manment,[转载]3.09进程(C语言班最后一天的课程) 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

1,進程:是容器,是內(nèi)存上的概念。線程是CPU的概念。

2,fork的作用是根據(jù)一個現(xiàn)有的進程復(fù)制出一個新進程,原來的進程稱為父進程(Parents Process),新進程稱為子進程(Child Process)。

系統(tǒng)中同時運行著許多進程,這些進程都是從最初只有一個進程開始一個一個復(fù)制出來的。

分進程函數(shù)fork(),用來開辟子進程。執(zhí)行fork()之后,會有兩個進程,一個是原來的那個,一個是新生成的子進程。這兩個進程都會執(zhí)行程序下面的代碼。pid = fork();pid得到的是返回子進程的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,先讓父進程睡1秒,會先執(zhí)行子進程。

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;

}

如果父進程結(jié)束后,sleep(1);子進程會打印到終端。

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,進程號:pid 。getppid()是得到當前進程父進程的pid;getpid()是得到當前進程的pid。

如果子進程getppid()時,父進程已經(jīng)死掉,子進程就會被托管,那getppid()的值就為1。

所以父進程要sleep(1),這樣子進程getppid()時,得到的就是此進程父進程的pid。

父進程getppid()時,得到的是終端的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查看進程;

ps -jas 查看所有的進程。

6,探討怎么讓父進程知道子進程結(jié)束?

exit(0)不會結(jié)束整個程序,只會結(jié)束一個進程。wait(NULL)等待自己子進程的結(jié)束。

父進程執(zhí)行到wait()時,會進入休眠,子進程結(jié)束時,會發(fā)一個信號,wait()會捕捉到這個信號,以此來喚醒父進程。

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,探討父進程與子進程關(guān)于變量使用的問題?

在fork()之后,會把原來的內(nèi)存完全復(fù)制一份,但不管怎么樣更改,都只會各改各的,互不干擾。

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,怎么樣殺死進程?

下面的程序是個死循環(huán),只能重新開一個終端,執(zhí)行“kill -9 進程號”。

如果執(zhí)行以下命令,一定要刪除交換文件:

$vi 5.c //進入5.c 此進程進程號為3456

$Ctrl + z

//暫停當前程序運行

$kill -9 3456 //殺死vi 5.c,這個進程

$fg

//回到剛暫停的程序中,會報錯,因為那個進程已經(jīng)被殺死了。要刪除那個交換文件才能執(zhí)行這個操作。

$ls -a//查看所有文件,能把交換文件查看出來。

$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族:用來代替原來進程,執(zhí)行的時候完全代替,不會再回來。

#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)安裝過的程序,不需要寫路徑。

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代表是一個文件夾

lrwx-rwx-rwx:l代表是一個快捷方式

12,cd是shell的內(nèi)置命令。如果輸入等于cd,則用chdir(),它并沒有fork()子進程,是父進程在執(zhí)行chdir()。

11.c

#include

#include

#include

int main(void)

{

chdir("dic");

//這個目錄要事先創(chuàng)建好。

mkdir("./ttt");

return 0;

}

如果用execlp(),會達不到想要的結(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,編寫一個自己的終端

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,仔細探討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;

}

程序運行順序如下:

(1)父進程初始化。

(2)父進程調(diào)用fork,這是一個系統(tǒng)調(diào)用,因此進入內(nèi)核。

(3)內(nèi)核根據(jù)父進程復(fù)制出一個子進程,父進程和子進程的PCB信息相同,用戶態(tài)代碼和數(shù)據(jù)也相同。因此,子進程現(xiàn)在的狀態(tài)和父進程一樣,做完了初始化,剛調(diào)用了fork進入內(nèi)核,還沒有從內(nèi)核返回。

(4)現(xiàn)在有兩個一模一樣的進程看起來都調(diào)用了fork進入內(nèi)核等待從內(nèi)核返回(實際上fork只調(diào)用了一次),系統(tǒng)中還有很多別的進程也等待從內(nèi)核返回。是父進程先返回還是子進程先返回,還是這兩個進程都等待,先去調(diào)度執(zhí)行別的進程,這都不一定,取決于內(nèi)核的調(diào)度算法。

(5)如果某個時刻父進程被調(diào)度執(zhí)行了,從內(nèi)核返回后就從fork函數(shù)返回,保存在變量pid中的返回值是子進程的id,是一個大于0的整數(shù),因此執(zhí)行下面else分支,然后執(zhí)行for循環(huán),打印"This is parentn"三次之后終止。

(6)如果某個時刻子進程被調(diào)度執(zhí)行了,從內(nèi)核返回后就從fork函數(shù)返回,保存在變量pid中的返回值是0,因此執(zhí)行下面的if(0 == pid)分支,然后執(zhí)行for循環(huán),打印"This is childn"六次這樣之后終止。fork調(diào)用把父進程的數(shù)據(jù)復(fù)制一份給子進程,但此后二者互不影響,在這個例子中,fork調(diào)用之后父進程和子進程的變量message和n被賦予不同的值,互不影響。

(7)父進程每打印一條信息就睡眠1秒,這時內(nèi)核調(diào)度別的進程執(zhí)行,在這1秒這么長的間隙里(對于計算機來說1秒很長了)子進程很有可能被調(diào)度到。同樣地,子進程每打印一條信息就睡眠1秒,在這1秒期間父進程也很有可能被調(diào)度到。所以程序運行地結(jié)果基本上是父子進程交替打印,但這也不一定的,取決于系統(tǒng)中其他進程的運行情況和內(nèi)核的調(diào)動算法,如果系統(tǒng)中其他進程非常繁忙則有可能觀察到不同的結(jié)果。

(8)這個程序是在Shell下運行的,因此Shell進程是父進程的父進程。父進程運行時Shell進程處于等待狀態(tài),當父進程終止時Shell進程認為命令執(zhí)行結(jié)束了,于是打印Shell提示符,而事實上子進程這時還沒結(jié)束,所以子進程的消息打印到了Shell提示符后面。最后光標停在This is the child的下一行,這時用戶仍然可以敲命令,即使命令不是緊跟在提示符后面,Shell也能正確讀取。

fork函數(shù):

(1)fork函數(shù)的特點"調(diào)用一次,返回兩次",在父進程中調(diào)用一次,在父進程和子進程中各返回一次。一開始是一個控制流程,調(diào)用fork之后發(fā)生了分叉,變成兩個控制流程,這也就是"fork"(分叉)這個名字的由來了。子進程中fork的返回值是0,而父進程中fork的返回值則是子進程的id(從根本上說fork是從內(nèi)核返回的,內(nèi)核自有辦法讓父進程和子進程返回不同的值),這樣當fork函數(shù)返回后,程序員可以根據(jù)返回值的不同讓父進程和子進程執(zhí)行不同的代碼。

(2)fork的返回值這樣規(guī)定也是有道理的。fork在子進程中返回0,子進程仍可以調(diào)用getpid函數(shù)得到自己的進程id,也可以調(diào)用getppid函數(shù)得到父進程的id。在父進程中用getpid可以得到自己的進程id,然而要想得到子進程的id,只有將fork的返回值記錄下來,別無他法。

(3)fork的另一個特性是所有由父進程打開的描述符都被復(fù)制到子進程中。父、子進程中相同編號的文件描述符在內(nèi)核中指向同一個file結(jié)構(gòu)體,也就是說file結(jié)構(gòu)體的引用計數(shù)要增加。

16,exec函數(shù)

用fork創(chuàng)建子進程后執(zhí)行的是和父進程相同的程序(但有可能執(zhí)行不同的代碼分支),子進程往往要調(diào)用一種exec函數(shù)以執(zhí)行另一個程序。當進程調(diào)用一種exec函數(shù)時,該進程的用戶空間代碼和數(shù)據(jù)完全被新程序替換,從新程序的啟動例程開始執(zhí)行。調(diào)用exec并不創(chuàng)建新進程,所以調(diào)用exec前后該進程的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)用成功則加載新的程序從啟動代碼開始執(zhí)行,不再返回,如果調(diào)用出錯則返回-1,所以exec函數(shù)只有出錯的返回值而沒有成功的返回值。

不帶p(表示path)的exec函數(shù)第一個參數(shù)必須是程序的相對路徑或絕對路徑,例如"/bin/ls"或"./a.out",而不能是"ls"或"a.out"。對于帶字母p的函數(shù):如果參數(shù)中包含/,則將其視為路徑名。否則視為不帶路徑的程序名,在PATH環(huán)境變量的目錄列表中搜索這個程序。

帶由字母l(表示list)的exec函數(shù)要求將新程序的每個命令行參數(shù)都作為一個參數(shù)傳給它,命令行參數(shù)的個數(shù)是可變的,因此函數(shù)原型中有…,…中的最有一個可變參數(shù)應(yīng)該是NULL,起sentinel的作用。

對于帶有字母v(表示vector)的函數(shù),則應(yīng)該先構(gòu)造一個指向各參數(shù)的指針數(shù)組,然后將該數(shù)組的首地址當作參數(shù)傳給它,數(shù)組中的最后一個指針也應(yīng)該是NULL,就像main函數(shù)的argv參數(shù)或者環(huán)境變量一樣。

對于以e(envirnoment)結(jié)尾的exec函數(shù),可以把一份新的環(huán)境變量傳給它,其他exec函數(shù)仍使用當前的環(huán)境變量表執(zhí)行新程序。

事實上,只有execve是真正的系統(tǒng)調(diào)用,其他五個函數(shù)最終都調(diào)用execve,所以execve在man手冊第2節(jié),其他函數(shù)在man手冊第3節(jié)。

總結(jié)

以上是生活随笔為你收集整理的c语言初始化字符串 函数 manment,[转载]3.09进程(C语言班最后一天的课程)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。